aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/ABI/stable/sysfs-class-udc93
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-hid11
-rw-r--r--Documentation/ABI/testing/configfs-usb-gadget-midi12
-rw-r--r--Documentation/devicetree/bindings/usb/dwc3.txt23
-rw-r--r--Documentation/devicetree/bindings/usb/exynos-usb.txt6
-rw-r--r--Documentation/devicetree/bindings/usb/pxa-usb.txt22
-rw-r--r--Documentation/usb/gadget_hid.txt7
-rw-r--r--arch/arm/boot/dts/am4372.dtsi4
-rw-r--r--drivers/pci/quirks.c20
-rw-r--r--drivers/phy/phy-omap-usb2.c14
-rw-r--r--drivers/phy/phy-twl4030-usb.c6
-rw-r--r--drivers/usb/chipidea/ci.h8
-rw-r--r--drivers/usb/chipidea/ci_hdrc_imx.c2
-rw-r--r--drivers/usb/chipidea/ci_hdrc_msm.c8
-rw-r--r--drivers/usb/chipidea/core.c89
-rw-r--r--drivers/usb/chipidea/debug.c2
-rw-r--r--drivers/usb/chipidea/host.c10
-rw-r--r--drivers/usb/chipidea/otg_fsm.c27
-rw-r--r--drivers/usb/chipidea/udc.c18
-rw-r--r--drivers/usb/common/usb-otg-fsm.c8
-rw-r--r--drivers/usb/core/hcd.c7
-rw-r--r--drivers/usb/dwc2/Kconfig68
-rw-r--r--drivers/usb/dwc2/Makefile32
-rw-r--r--drivers/usb/dwc2/core.c10
-rw-r--r--drivers/usb/dwc2/core.h197
-rw-r--r--drivers/usb/dwc2/core_intr.c17
-rw-r--r--drivers/usb/dwc2/gadget.c387
-rw-r--r--drivers/usb/dwc2/hcd.c94
-rw-r--r--drivers/usb/dwc2/hcd.h10
-rw-r--r--drivers/usb/dwc2/pci.c7
-rw-r--r--drivers/usb/dwc2/platform.c44
-rw-r--r--drivers/usb/dwc3/Kconfig2
-rw-r--r--drivers/usb/dwc3/core.c251
-rw-r--r--drivers/usb/dwc3/core.h89
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c44
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c1
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c20
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c35
-rw-r--r--drivers/usb/dwc3/dwc3-st.c2
-rw-r--r--drivers/usb/dwc3/ep0.c26
-rw-r--r--drivers/usb/dwc3/gadget.c68
-rw-r--r--drivers/usb/dwc3/platform_data.h20
-rw-r--r--drivers/usb/dwc3/trace.h20
-rw-r--r--drivers/usb/gadget/Kconfig61
-rw-r--r--drivers/usb/gadget/composite.c64
-rw-r--r--drivers/usb/gadget/configfs.c5
-rw-r--r--drivers/usb/gadget/function/Makefile4
-rw-r--r--drivers/usb/gadget/function/f_hid.c374
-rw-r--r--drivers/usb/gadget/function/f_midi.c364
-rw-r--r--drivers/usb/gadget/function/f_ncm.c3
-rw-r--r--drivers/usb/gadget/function/f_rndis.c3
-rw-r--r--drivers/usb/gadget/function/u_hid.h42
-rw-r--r--drivers/usb/gadget/function/u_midi.h40
-rw-r--r--drivers/usb/gadget/function/u_uac1.c3
-rw-r--r--drivers/usb/gadget/legacy/Kconfig2
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c26
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c43
-rw-r--r--drivers/usb/gadget/legacy/hid.c79
-rw-r--r--drivers/usb/gadget/legacy/printer.c65
-rw-r--r--drivers/usb/gadget/udc/Kconfig2
-rw-r--r--drivers/usb/gadget/udc/Makefile1
-rw-r--r--drivers/usb/gadget/udc/amd5536udc.c12
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c89
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c25
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c13
-rw-r--r--drivers/usb/gadget/udc/bdc/Kconfig21
-rw-r--r--drivers/usb/gadget/udc/bdc/Makefile8
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc.h490
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_cmd.c376
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_cmd.h29
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_core.c533
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_dbg.c123
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_dbg.h37
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.c2023
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_ep.h22
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_pci.c132
-rw-r--r--drivers/usb/gadget/udc/bdc/bdc_udc.c587
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c36
-rw-r--r--drivers/usb/gadget/udc/fotg210-udc.c3
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c13
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c19
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c3
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c6
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c8
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c11
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c5
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c5
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c32
-rw-r--r--drivers/usb/gadget/udc/net2272.c66
-rw-r--r--drivers/usb/gadget/udc/net2280.c65
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c6
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c11
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c43
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c152
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.h6
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c10
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c16
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c6
-rw-r--r--drivers/usb/gadget/udc/udc-core.c28
-rw-r--r--drivers/usb/gadget/udc/udc-xilinx.c3
-rw-r--r--drivers/usb/host/ohci-omap.c2
-rw-r--r--drivers/usb/musb/Kconfig5
-rw-r--r--drivers/usb/musb/am35x.c32
-rw-r--r--drivers/usb/musb/blackfin.c74
-rw-r--r--drivers/usb/musb/da8xx.c30
-rw-r--r--drivers/usb/musb/davinci.c22
-rw-r--r--drivers/usb/musb/jz4740.c2
-rw-r--r--drivers/usb/musb/musb_core.c331
-rw-r--r--drivers/usb/musb/musb_core.h87
-rw-r--r--drivers/usb/musb/musb_cppi41.c10
-rw-r--r--drivers/usb/musb/musb_debugfs.c57
-rw-r--r--drivers/usb/musb/musb_dsps.c34
-rw-r--r--drivers/usb/musb/musb_gadget.c58
-rw-r--r--drivers/usb/musb/musb_host.c8
-rw-r--r--drivers/usb/musb/musb_io.h106
-rw-r--r--drivers/usb/musb/musb_regs.h26
-rw-r--r--drivers/usb/musb/musb_virthub.c23
-rw-r--r--drivers/usb/musb/musbhsdma.c7
-rw-r--r--drivers/usb/musb/omap2430.c52
-rw-r--r--drivers/usb/musb/tusb6010.c102
-rw-r--r--drivers/usb/musb/ux500.c16
-rw-r--r--drivers/usb/musb/ux500_dma.c11
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c33
-rw-r--r--drivers/usb/phy/phy-am335x-control.c4
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c27
-rw-r--r--drivers/usb/phy/phy-fsl-usb.h2
-rw-r--r--drivers/usb/phy/phy-generic.c8
-rw-r--r--drivers/usb/phy/phy-gpio-vbus-usb.c16
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c104
-rw-r--r--drivers/usb/phy/phy-msm-usb.c107
-rw-r--r--drivers/usb/phy/phy-mv-usb.c56
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c4
-rw-r--r--drivers/usb/phy/phy-rcar-gen2-usb.c4
-rw-r--r--drivers/usb/phy/phy-rcar-usb.c4
-rw-r--r--drivers/usb/phy/phy-tahvo.c34
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c9
-rw-r--r--drivers/usb/phy/phy-ulpi.c6
-rw-r--r--drivers/usb/phy/phy.c16
-rw-r--r--drivers/usb/renesas_usbhs/common.c6
-rw-r--r--drivers/usb/renesas_usbhs/common.h7
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c111
-rw-r--r--drivers/usb/renesas_usbhs/fifo.h10
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c41
-rw-r--r--drivers/usb/renesas_usbhs/mod_host.c2
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c8
-rw-r--r--drivers/usb/renesas_usbhs/rcar2.c73
-rw-r--r--include/linux/pci_ids.h1
-rw-r--r--include/linux/platform_data/dwc3-exynos.h24
-rw-r--r--include/linux/usb/chipidea.h4
-rw-r--r--include/linux/usb/composite.h7
-rw-r--r--include/linux/usb/gadget.h5
-rw-r--r--include/linux/usb/otg.h7
-rw-r--r--include/linux/usb/phy.h6
-rw-r--r--include/linux/usb/renesas_usbhs.h4
154 files changed, 7983 insertions, 1957 deletions
diff --git a/Documentation/ABI/stable/sysfs-class-udc b/Documentation/ABI/stable/sysfs-class-udc
new file mode 100644
index 000000000000..85d3dac2e204
--- /dev/null
+++ b/Documentation/ABI/stable/sysfs-class-udc
@@ -0,0 +1,93 @@
+What: /sys/class/udc/<udc>/a_alt_hnp_support
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates if an OTG A-Host supports HNP at an alternate port.
+Users:
+
+What: /sys/class/udc/<udc>/a_hnp_support
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates if an OTG A-Host supports HNP at this port.
+Users:
+
+What: /sys/class/udc/<udc>/b_hnp_enable
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates if an OTG A-Host enabled HNP support.
+Users:
+
+What: /sys/class/udc/<udc>/current_speed
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates the current negotiated speed at this port.
+Users:
+
+What: /sys/class/udc/<udc>/is_a_peripheral
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates that this port is the default Host on an OTG session
+ but HNP was used to switch roles.
+Users:
+
+What: /sys/class/udc/<udc>/is_otg
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates that this port support OTG.
+Users:
+
+What: /sys/class/udc/<udc>/maximum_speed
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates the maximum USB speed supported by this port.
+Users:
+
+What: /sys/class/udc/<udc>/maximum_speed
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates the maximum USB speed supported by this port.
+Users:
+
+What: /sys/class/udc/<udc>/soft_connect
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Allows users to disconnect data pullup resistors thus causing a
+ logical disconnection from the USB Host.
+Users:
+
+What: /sys/class/udc/<udc>/srp
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Allows users to manually start Session Request Protocol.
+Users:
+
+What: /sys/class/udc/<udc>/state
+Date: June 2011
+KernelVersion: 3.1
+Contact: Felipe Balbi <balbi@kernel.org>
+Description:
+ Indicates current state of the USB Device Controller. Valid
+ states are: 'not-attached', 'attached', 'powered',
+ 'reconnecting', 'unauthenticated', 'default', 'addressed',
+ 'configured', and 'suspended'; however not all USB Device
+ Controllers support reporting all states.
+Users:
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-hid b/Documentation/ABI/testing/configfs-usb-gadget-hid
new file mode 100644
index 000000000000..f12e00e6baa3
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-hid
@@ -0,0 +1,11 @@
+What: /config/usb-gadget/gadget/functions/hid.name
+Date: Nov 2014
+KernelVersion: 3.19
+Description:
+ The attributes:
+
+ protocol - HID protocol to use
+ report_desc - blob corresponding to HID report descriptors
+ except the data passed through /dev/hidg<N>
+ report_length - HID report length
+ subclass - HID device subclass to use
diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi b/Documentation/ABI/testing/configfs-usb-gadget-midi
new file mode 100644
index 000000000000..6b341df7249c
--- /dev/null
+++ b/Documentation/ABI/testing/configfs-usb-gadget-midi
@@ -0,0 +1,12 @@
+What: /config/usb-gadget/gadget/functions/midi.name
+Date: Nov 2014
+KernelVersion: 3.19
+Description:
+ The attributes:
+
+ index - index value for the USB MIDI adapter
+ id - ID string for the USB MIDI adapter
+ buflen - MIDI buffer length
+ qlen - USB read request queue length
+ in_ports - number of MIDI input ports
+ out_ports - number of MIDI output ports
diff --git a/Documentation/devicetree/bindings/usb/dwc3.txt b/Documentation/devicetree/bindings/usb/dwc3.txt
index 471366d6a129..cd7f0454e13a 100644
--- a/Documentation/devicetree/bindings/usb/dwc3.txt
+++ b/Documentation/devicetree/bindings/usb/dwc3.txt
@@ -14,6 +14,29 @@ Optional properties:
- phys: from the *Generic PHY* bindings
- phy-names: from the *Generic PHY* bindings
- tx-fifo-resize: determines if the FIFO *has* to be reallocated.
+ - snps,disable_scramble_quirk: true when SW should disable data scrambling.
+ Only really useful for FPGA builds.
+ - snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
+ - snps,lpm-nyet-threshold: LPM NYET threshold
+ - snps,u2exit_lfps_quirk: set if we want to enable u2exit lfps quirk
+ - snps,u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
+ - snps,req_p1p2p3_quirk: when set, the core will always request for
+ P1/P2/P3 transition sequence.
+ - snps,del_p1p2p3_quirk: when set core will delay P1/P2/P3 until a certain
+ amount of 8B10B errors occur.
+ - snps,del_phy_power_chg_quirk: when set core will delay PHY power change
+ from P0 to P1/P2/P3.
+ - snps,lfps_filter_quirk: when set core will filter LFPS reception.
+ - snps,rx_detect_poll_quirk: when set core will disable a 400us delay to start
+ Polling LFPS after RX.Detect.
+ - snps,tx_de_emphasis_quirk: when set core will set Tx de-emphasis value.
+ - snps,tx_de_emphasis: the value driven to the PHY is controlled by the
+ LTSSM during USB3 Compliance mode.
+ - snps,dis_u3_susphy_quirk: when set core will disable USB3 suspend phy.
+ - snps,dis_u2_susphy_quirk: when set core will disable USB2 suspend phy.
+ - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
+ utmi_l1_suspend_n, false when asserts utmi_sleep_n
+ - snps,hird-threshold: HIRD threshold
This is usually a subnode to DWC3 glue to which it is connected.
diff --git a/Documentation/devicetree/bindings/usb/exynos-usb.txt b/Documentation/devicetree/bindings/usb/exynos-usb.txt
index a3b5990d0f2c..9b4dbe3b2acc 100644
--- a/Documentation/devicetree/bindings/usb/exynos-usb.txt
+++ b/Documentation/devicetree/bindings/usb/exynos-usb.txt
@@ -82,8 +82,10 @@ Example:
DWC3
Required properties:
- - compatible: should be "samsung,exynos5250-dwusb3" for USB 3.0 DWC3
- controller.
+ - compatible: should be one of the following -
+ "samsung,exynos5250-dwusb3": for USB 3.0 DWC3 controller on
+ Exynos5250/5420.
+ "samsung,exynos7-dwusb3": for USB 3.0 DWC3 controller on Exynos7.
- #address-cells, #size-cells : should be '1' if the device has sub-nodes
with 'reg' property.
- ranges: allows valid 1:1 translation between child's address space and
diff --git a/Documentation/devicetree/bindings/usb/pxa-usb.txt b/Documentation/devicetree/bindings/usb/pxa-usb.txt
index 79729a948d5a..9c331799b87c 100644
--- a/Documentation/devicetree/bindings/usb/pxa-usb.txt
+++ b/Documentation/devicetree/bindings/usb/pxa-usb.txt
@@ -29,3 +29,25 @@ Example:
marvell,port-mode = <2>; /* PMM_GLOBAL_MODE */
};
+UDC
+
+Required properties:
+ - compatible: Should be "marvell,pxa270-udc" for USB controllers
+ used in device mode.
+ - reg: usb device MMIO address space
+ - interrupts: single interrupt generated by the UDC IP
+ - clocks: input clock of the UDC IP (see clock-bindings.txt)
+
+Optional properties:
+ - gpios:
+ - gpio activated to control the USB D+ pullup (see gpio.txt)
+
+Example:
+
+ pxa27x_udc: udc@40600000 {
+ compatible = "marvell,pxa270-udc";
+ reg = <0x40600000 0x10000>;
+ interrupts = <11>;
+ clocks = <&pxa2xx_clks 11>;
+ gpios = <&gpio 22 GPIO_ACTIVE_LOW>;
+ };
diff --git a/Documentation/usb/gadget_hid.txt b/Documentation/usb/gadget_hid.txt
index 12696c2e43fb..7a0fb8e16e27 100644
--- a/Documentation/usb/gadget_hid.txt
+++ b/Documentation/usb/gadget_hid.txt
@@ -74,6 +74,13 @@ static struct platform_device my_hid = {
You can add as many HID functions as you want, only limited by
the amount of interrupt endpoints your gadget driver supports.
+Configuration with configfs
+
+ Instead of adding fake platform devices and drivers in order to pass
+ some data to the kernel, if HID is a part of a gadget composed with
+ configfs the hidg_func_descriptor.report_desc is passed to the kernel
+ by writing the appropriate stream of bytes to a configfs attribute.
+
Send and receive HID reports
HID reports can be sent/received using read/write on the
diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi
index 46660ffd2b65..e19068d24737 100644
--- a/arch/arm/boot/dts/am4372.dtsi
+++ b/arch/arm/boot/dts/am4372.dtsi
@@ -817,6 +817,8 @@
maximum-speed = "high-speed";
dr_mode = "otg";
status = "disabled";
+ snps,dis_u3_susphy_quirk;
+ snps,dis_u2_susphy_quirk;
};
};
@@ -839,6 +841,8 @@
maximum-speed = "high-speed";
dr_mode = "otg";
status = "disabled";
+ snps,dis_u3_susphy_quirk;
+ snps,dis_u2_susphy_quirk;
};
};
diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c
index 90acb32c85b1..ed6f89b6efe5 100644
--- a/drivers/pci/quirks.c
+++ b/drivers/pci/quirks.c
@@ -379,6 +379,26 @@ static void quirk_ati_exploding_mce(struct pci_dev *dev)
DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_RS100, quirk_ati_exploding_mce);
/*
+ * In the AMD NL platform, this device ([1022:7912]) has a class code of
+ * PCI_CLASS_SERIAL_USB_XHCI (0x0c0330), which means the xhci driver will
+ * claim it.
+ * But the dwc3 driver is a more specific driver for this device, and we'd
+ * prefer to use it instead of xhci. To prevent xhci from claiming the
+ * device, change the class code to 0x0c03fe, which the PCI r3.0 spec
+ * defines as "USB device (not host controller)". The dwc3 driver can then
+ * claim it based on its Vendor and Device ID.
+ */
+static void quirk_amd_nl_class(struct pci_dev *pdev)
+{
+ /*
+ * Use 'USB Device' (0x0c03fe) instead of PCI header provided
+ */
+ pdev->class = 0x0c03fe;
+}
+DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB,
+ quirk_amd_nl_class);
+
+/*
* Let's make the southbridge information explicit instead
* of having to worry about people probing the ACPI areas,
* for example.. (Yes, it happens, and if you read the wrong
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
index f091576b6449..08f2da270837 100644
--- a/drivers/phy/phy-omap-usb2.c
+++ b/drivers/phy/phy-omap-usb2.c
@@ -60,7 +60,7 @@ EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
{
- struct omap_usb *phy = phy_to_omapusb(otg->phy);
+ struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
if (!phy->comparator)
return -ENODEV;
@@ -70,7 +70,7 @@ static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
static int omap_usb_start_srp(struct usb_otg *otg)
{
- struct omap_usb *phy = phy_to_omapusb(otg->phy);
+ struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
if (!phy->comparator)
return -ENODEV;
@@ -80,11 +80,9 @@ static int omap_usb_start_srp(struct usb_otg *otg)
static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
{
- struct usb_phy *phy = otg->phy;
-
otg->host = host;
if (!host)
- phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
return 0;
}
@@ -92,11 +90,9 @@ static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
static int omap_usb_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
- struct usb_phy *phy = otg->phy;
-
otg->gadget = gadget;
if (!gadget)
- phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
return 0;
}
@@ -255,7 +251,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
otg->set_vbus = omap_usb_set_vbus;
if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
otg->start_srp = omap_usb_start_srp;
- otg->phy = &phy->phy;
+ otg->usb_phy = &phy->phy;
platform_set_drvdata(pdev, phy);
pm_runtime_enable(phy->dev);
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
index 7b04befd5271..e2698d29f436 100644
--- a/drivers/phy/phy-twl4030-usb.c
+++ b/drivers/phy/phy-twl4030-usb.c
@@ -606,7 +606,7 @@ static int twl4030_set_peripheral(struct usb_otg *otg,
otg->gadget = gadget;
if (!gadget)
- otg->phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
return 0;
}
@@ -618,7 +618,7 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
otg->host = host;
if (!host)
- otg->phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
return 0;
}
@@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
twl->phy.otg = otg;
twl->phy.type = USB_PHY_TYPE_USB2;
- otg->phy = &twl->phy;
+ otg->usb_phy = &twl->phy;
otg->set_host = twl4030_set_host;
otg->set_peripheral = twl4030_set_peripheral;
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
index ea40626e0246..5bbfcc73cf70 100644
--- a/drivers/usb/chipidea/ci.h
+++ b/drivers/usb/chipidea/ci.h
@@ -161,7 +161,8 @@ struct hw_bank {
* @test_mode: the selected test mode
* @platdata: platform specific information supplied by parent device
* @vbus_active: is VBUS active
- * @transceiver: pointer to USB PHY, if any
+ * @phy: pointer to PHY, if any
+ * @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
* @hcd: pointer to usb_hcd for ehci host driver
* @debugfs: root dentry for this controller in debugfs
* @id_event: indicates there is an id event, and handled at ci_otg_work
@@ -177,6 +178,7 @@ struct ci_hdrc {
struct ci_role_driver *roles[CI_ROLE_END];
enum ci_role role;
bool is_otg;
+ struct usb_otg otg;
struct otg_fsm fsm;
struct ci_otg_fsm_timer_list *fsm_timer;
struct work_struct work;
@@ -201,7 +203,9 @@ struct ci_hdrc {
struct ci_hdrc_platform_data *platdata;
int vbus_active;
- struct usb_phy *transceiver;
+ struct phy *phy;
+ /* old usb_phy interface */
+ struct usb_phy *usb_phy;
struct usb_hcd *hcd;
struct dentry *debugfs;
bool id_event;
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
index a7ab0f15926e..6f8b1b1045b5 100644
--- a/drivers/usb/chipidea/ci_hdrc_imx.c
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -147,7 +147,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
goto err_clk;
}
- pdata.phy = data->phy;
+ pdata.usb_phy = data->phy;
if (imx_platform_flag->flags & CI_HDRC_IMX_IMX28_WRITE_FIX)
pdata.flags |= CI_HDRC_IMX28_WRITE_FIX;
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
index 4935ac38fd00..3edf969ed797 100644
--- a/drivers/usb/chipidea/ci_hdrc_msm.c
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -26,15 +26,15 @@ static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
writel(0, USB_AHBBURST);
writel(0, USB_AHBMODE);
- usb_phy_init(ci->transceiver);
+ usb_phy_init(ci->usb_phy);
break;
case CI_HDRC_CONTROLLER_STOPPED_EVENT:
dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
/*
- * Put the transceiver in non-driving mode. Otherwise host
+ * Put the phy in non-driving mode. Otherwise host
* may not detect soft-disconnection.
*/
- usb_phy_notify_disconnect(ci->transceiver, USB_SPEED_UNKNOWN);
+ usb_phy_notify_disconnect(ci->usb_phy, USB_SPEED_UNKNOWN);
break;
default:
dev_dbg(dev, "unknown ci_hdrc event\n");
@@ -68,7 +68,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
if (IS_ERR(phy))
return PTR_ERR(phy);
- ci_hdrc_msm_platdata.phy = phy;
+ ci_hdrc_msm_platdata.usb_phy = phy;
plat_ci = ci_hdrc_add_device(&pdev->dev,
pdev->resource, pdev->num_resources,
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
index 9bdc6bd73432..60578d9c896d 100644
--- a/drivers/usb/chipidea/core.c
+++ b/drivers/usb/chipidea/core.c
@@ -47,6 +47,7 @@
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
+#include <linux/phy/phy.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/idr.h>
@@ -299,6 +300,49 @@ static void hw_phymode_configure(struct ci_hdrc *ci)
}
/**
+ * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
+ * interfaces
+ * @ci: the controller
+ *
+ * This function returns an error code if the phy failed to init
+ */
+static int _ci_usb_phy_init(struct ci_hdrc *ci)
+{
+ int ret;
+
+ if (ci->phy) {
+ ret = phy_init(ci->phy);
+ if (ret)
+ return ret;
+
+ ret = phy_power_on(ci->phy);
+ if (ret) {
+ phy_exit(ci->phy);
+ return ret;
+ }
+ } else {
+ ret = usb_phy_init(ci->usb_phy);
+ }
+
+ return ret;
+}
+
+/**
+ * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
+ * interfaces
+ * @ci: the controller
+ */
+static void ci_usb_phy_exit(struct ci_hdrc *ci)
+{
+ if (ci->phy) {
+ phy_power_off(ci->phy);
+ phy_exit(ci->phy);
+ } else {
+ usb_phy_shutdown(ci->usb_phy);
+ }
+}
+
+/**
* ci_usb_phy_init: initialize phy according to different phy type
* @ci: the controller
*
@@ -312,7 +356,7 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
case USBPHY_INTERFACE_MODE_UTMI:
case USBPHY_INTERFACE_MODE_UTMIW:
case USBPHY_INTERFACE_MODE_HSIC:
- ret = usb_phy_init(ci->transceiver);
+ ret = _ci_usb_phy_init(ci);
if (ret)
return ret;
hw_phymode_configure(ci);
@@ -320,12 +364,12 @@ static int ci_usb_phy_init(struct ci_hdrc *ci)
case USBPHY_INTERFACE_MODE_ULPI:
case USBPHY_INTERFACE_MODE_SERIAL:
hw_phymode_configure(ci);
- ret = usb_phy_init(ci->transceiver);
+ ret = _ci_usb_phy_init(ci);
if (ret)
return ret;
break;
default:
- ret = usb_phy_init(ci->transceiver);
+ ret = _ci_usb_phy_init(ci);
}
return ret;
@@ -605,23 +649,26 @@ static int ci_hdrc_probe(struct platform_device *pdev)
return -ENODEV;
}
- if (ci->platdata->phy)
- ci->transceiver = ci->platdata->phy;
- else
- ci->transceiver = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-
- if (IS_ERR(ci->transceiver)) {
- ret = PTR_ERR(ci->transceiver);
- /*
- * if -ENXIO is returned, it means PHY layer wasn't
- * enabled, so it makes no sense to return -EPROBE_DEFER
- * in that case, since no PHY driver will ever probe.
- */
- if (ret == -ENXIO)
- return ret;
+ if (ci->platdata->phy) {
+ ci->phy = ci->platdata->phy;
+ } else if (ci->platdata->usb_phy) {
+ ci->usb_phy = ci->platdata->usb_phy;
+ } else {
+ ci->phy = devm_phy_get(dev, "usb-phy");
+ ci->usb_phy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
+
+ /* if both generic PHY and USB PHY layers aren't enabled */
+ if (PTR_ERR(ci->phy) == -ENOSYS &&
+ PTR_ERR(ci->usb_phy) == -ENXIO)
+ return -ENXIO;
+
+ if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy))
+ return -EPROBE_DEFER;
- dev_err(dev, "no usb2 phy configured\n");
- return -EPROBE_DEFER;
+ if (IS_ERR(ci->phy))
+ ci->phy = NULL;
+ else if (IS_ERR(ci->usb_phy))
+ ci->usb_phy = NULL;
}
ret = ci_usb_phy_init(ci);
@@ -728,7 +775,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
stop:
ci_role_destroy(ci);
deinit_phy:
- usb_phy_shutdown(ci->transceiver);
+ ci_usb_phy_exit(ci);
return ret;
}
@@ -741,7 +788,7 @@ static int ci_hdrc_remove(struct platform_device *pdev)
free_irq(ci->irq, ci);
ci_role_destroy(ci);
ci_hdrc_enter_lpm(ci, true);
- usb_phy_shutdown(ci->transceiver);
+ ci_usb_phy_exit(ci);
return 0;
}
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
index 795d6538d630..268e4236e84c 100644
--- a/drivers/usb/chipidea/debug.c
+++ b/drivers/usb/chipidea/debug.c
@@ -220,7 +220,7 @@ static int ci_otg_show(struct seq_file *s, void *unused)
/* ------ State ----- */
seq_printf(s, "OTG state: %s\n\n",
- usb_otg_state_string(ci->transceiver->state));
+ usb_otg_state_string(ci->otg.state));
/* ------ State Machine Variables ----- */
seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
index 33027abb5349..48731d0bab35 100644
--- a/drivers/usb/chipidea/host.c
+++ b/drivers/usb/chipidea/host.c
@@ -98,8 +98,11 @@ static int host_start(struct ci_hdrc *ci)
hcd->has_tt = 1;
hcd->power_budget = ci->platdata->power_budget;
- hcd->usb_phy = ci->transceiver;
hcd->tpl_support = ci->platdata->tpl_support;
+ if (ci->phy)
+ hcd->phy = ci->phy;
+ else
+ hcd->usb_phy = ci->usb_phy;
ehci = hcd_to_ehci(hcd);
ehci->caps = ci->hw_bank.cap;
@@ -117,10 +120,11 @@ static int host_start(struct ci_hdrc *ci)
if (ret) {
goto put_hcd;
} else {
- struct usb_otg *otg = ci->transceiver->otg;
+ struct usb_otg *otg = &ci->otg;
ci->hcd = hcd;
- if (otg) {
+
+ if (ci_otg_is_fsm_mode(ci)) {
otg->host = &hcd->self;
hcd->self.otg_port = 1;
}
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
index e683595f9e69..368cc8e94fd6 100644
--- a/drivers/usb/chipidea/otg_fsm.c
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -328,7 +328,7 @@ static void b_ssend_srp_tmout_func(void *ptr, unsigned long indicator)
set_tmout(ci, indicator);
/* only vbus fall below B_sess_vld in b_idle state */
- if (ci->transceiver->state == OTG_STATE_B_IDLE)
+ if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
ci_otg_queue_work(ci);
}
@@ -582,11 +582,11 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
* when there is no gadget class driver
*/
if (ci->fsm.id && !(ci->driver) &&
- ci->transceiver->state < OTG_STATE_A_IDLE)
+ ci->fsm.otg->state < OTG_STATE_A_IDLE)
return 0;
if (otg_statemachine(&ci->fsm)) {
- if (ci->transceiver->state == OTG_STATE_A_IDLE) {
+ if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
/*
* Further state change for cases:
* a_idle to b_idle; or
@@ -600,7 +600,7 @@ int ci_otg_fsm_work(struct ci_hdrc *ci)
ci_otg_queue_work(ci);
if (ci->id_event)
ci->id_event = false;
- } else if (ci->transceiver->state == OTG_STATE_B_IDLE) {
+ } else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
if (ci->fsm.b_sess_vld) {
ci->fsm.power_up = 0;
/*
@@ -627,7 +627,7 @@ static void ci_otg_fsm_event(struct ci_hdrc *ci)
otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
- switch (ci->transceiver->state) {
+ switch (ci->fsm.otg->state) {
case OTG_STATE_A_WAIT_BCON:
if (port_conn) {
fsm->b_conn = 1;
@@ -778,20 +778,17 @@ void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
{
int retval = 0;
- struct usb_otg *otg;
- otg = devm_kzalloc(ci->dev,
- sizeof(struct usb_otg), GFP_KERNEL);
- if (!otg)
- return -ENOMEM;
+ if (ci->phy)
+ ci->otg.phy = ci->phy;
+ else
+ ci->otg.usb_phy = ci->usb_phy;
- otg->phy = ci->transceiver;
- otg->gadget = &ci->gadget;
- ci->fsm.otg = otg;
- ci->transceiver->otg = ci->fsm.otg;
+ ci->otg.gadget = &ci->gadget;
+ ci->fsm.otg = &ci->otg;
ci->fsm.power_up = 1;
ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
- ci->transceiver->state = OTG_STATE_UNDEFINED;
+ ci->fsm.otg->state = OTG_STATE_UNDEFINED;
ci->fsm.ops = &ci_otg_ops;
mutex_init(&ci->fsm.lock);
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 0444d3f8971a..bdaa7bac6f4a 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -692,10 +692,8 @@ __acquires(ci->lock)
int retval;
spin_unlock(&ci->lock);
- if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
- if (ci->driver)
- ci->driver->disconnect(&ci->gadget);
- }
+ if (ci->gadget.speed != USB_SPEED_UNKNOWN)
+ usb_gadget_udc_reset(&ci->gadget, ci->driver);
retval = _gadget_stop_activity(&ci->gadget);
if (retval)
@@ -709,8 +707,6 @@ __acquires(ci->lock)
if (ci->status == NULL)
retval = -ENOMEM;
- usb_gadget_set_state(&ci->gadget, USB_STATE_DEFAULT);
-
done:
spin_lock(&ci->lock);
@@ -1519,8 +1515,8 @@ static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
{
struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
- if (ci->transceiver)
- return usb_phy_set_power(ci->transceiver, ma);
+ if (ci->usb_phy)
+ return usb_phy_set_power(ci->usb_phy, ma);
return -ENOTSUPP;
}
@@ -1544,8 +1540,7 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
static int ci_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
-static int ci_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver);
+static int ci_udc_stop(struct usb_gadget *gadget);
/**
* Device operations part of the API to the USB controller hardware,
* which don't involve endpoints (or i/o)
@@ -1682,8 +1677,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
/**
* ci_udc_stop: unregister a gadget driver
*/
-static int ci_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int ci_udc_stop(struct usb_gadget *gadget)
{
struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
unsigned long flags;
diff --git a/drivers/usb/common/usb-otg-fsm.c b/drivers/usb/common/usb-otg-fsm.c
index 98e8340a5bb1..c6b35b77dab7 100644
--- a/drivers/usb/common/usb-otg-fsm.c
+++ b/drivers/usb/common/usb-otg-fsm.c
@@ -124,10 +124,10 @@ static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
{
state_changed = 1;
- if (fsm->otg->phy->state == new_state)
+ if (fsm->otg->state == new_state)
return 0;
VDBG("Set state: %s\n", usb_otg_state_string(new_state));
- otg_leave_state(fsm, fsm->otg->phy->state);
+ otg_leave_state(fsm, fsm->otg->state);
switch (new_state) {
case OTG_STATE_B_IDLE:
otg_drv_vbus(fsm, 0);
@@ -236,7 +236,7 @@ static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
break;
}
- fsm->otg->phy->state = new_state;
+ fsm->otg->state = new_state;
return 0;
}
@@ -247,7 +247,7 @@ int otg_statemachine(struct otg_fsm *fsm)
mutex_lock(&fsm->lock);
- state = fsm->otg->phy->state;
+ state = fsm->otg->state;
state_changed = 0;
/* State machine state change judgement */
diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c
index a6efb4184f2b..2f2118fa36a8 100644
--- a/drivers/usb/core/hcd.c
+++ b/drivers/usb/core/hcd.c
@@ -2650,7 +2650,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
}
}
- if (IS_ENABLED(CONFIG_GENERIC_PHY)) {
+ if (IS_ENABLED(CONFIG_GENERIC_PHY) && !hcd->phy) {
struct phy *phy = phy_get(hcd->self.controller, "usb");
if (IS_ERR(phy)) {
@@ -2670,6 +2670,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
goto err_phy;
}
hcd->phy = phy;
+ hcd->remove_phy = 1;
}
}
@@ -2816,7 +2817,7 @@ err_allocate_root_hub:
err_register_bus:
hcd_buffer_destroy(hcd);
err_create_buf:
- if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
+ if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
phy_power_off(hcd->phy);
phy_exit(hcd->phy);
phy_put(hcd->phy);
@@ -2900,7 +2901,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
usb_deregister_bus(&hcd->self);
hcd_buffer_destroy(hcd);
- if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->phy) {
+ if (IS_ENABLED(CONFIG_GENERIC_PHY) && hcd->remove_phy && hcd->phy) {
phy_power_off(hcd->phy);
phy_exit(hcd->phy);
phy_put(hcd->phy);
diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig
index f93807b3631a..b323c4c11b0a 100644
--- a/drivers/usb/dwc2/Kconfig
+++ b/drivers/usb/dwc2/Kconfig
@@ -1,6 +1,6 @@
config USB_DWC2
- bool "DesignWare USB2 DRD Core Support"
- depends on USB
+ tristate "DesignWare USB2 DRD Core Support"
+ depends on USB || USB_GADGET
help
Say Y here if your system has a Dual Role Hi-Speed USB
controller based on the DesignWare HSOTG IP Core.
@@ -10,49 +10,61 @@ config USB_DWC2
bus interface module (if you have a PCI bus system) will be
called dwc2_pci.ko, and the platform interface module (for
controllers directly connected to the CPU) will be called
- dwc2_platform.ko. For gadget mode, there will be a single
- module called dwc2_gadget.ko.
-
- NOTE: The s3c-hsotg driver is now renamed to dwc2_gadget. The
- host and gadget drivers are still currently separate drivers.
- There are plans to merge the dwc2_gadget driver with the dwc2
- host driver in the near future to create a dual-role driver.
+ dwc2_platform.ko. For all modes(host, gadget and dual-role), there
+ will be an additional module named dwc2.ko.
if USB_DWC2
+choice
+ bool "DWC2 Mode Selection"
+ default USB_DWC2_DUAL_ROLE if (USB && USB_GADGET)
+ default USB_DWC2_HOST if (USB && !USB_GADGET)
+ default USB_DWC2_PERIPHERAL if (!USB && USB_GADGET)
+
config USB_DWC2_HOST
- tristate "Host only mode"
+ bool "Host only mode"
depends on USB
help
The Designware USB2.0 high-speed host controller
- integrated into many SoCs.
+ integrated into many SoCs. Select this option if you want the
+ driver to operate in Host-only mode.
-config USB_DWC2_PLATFORM
- bool "DWC2 Platform"
- depends on USB_DWC2_HOST
- default USB_DWC2_HOST
+comment "Gadget/Dual-role mode requires USB Gadget support to be enabled"
+
+config USB_DWC2_PERIPHERAL
+ bool "Gadget only mode"
+ depends on USB_GADGET=y || USB_GADGET=USB_DWC2
help
- The Designware USB2.0 platform interface module for
- controllers directly connected to the CPU. This is only
- used for host mode.
+ The Designware USB2.0 high-speed gadget controller
+ integrated into many SoCs. Select this option if you want the
+ driver to operate in Peripheral-only mode. This option requires
+ USB_GADGET to be enabled.
+
+config USB_DWC2_DUAL_ROLE
+ bool "Dual Role mode"
+ depends on (USB=y || USB=USB_DWC2) && (USB_GADGET=y || USB_GADGET=USB_DWC2)
+ help
+ Select this option if you want the driver to work in a dual-role
+ mode. In this mode both host and gadget features are enabled, and
+ the role will be determined by the cable that gets plugged-in. This
+ option requires USB_GADGET to be enabled.
+endchoice
+
+config USB_DWC2_PLATFORM
+ tristate "DWC2 Platform"
+ default USB_DWC2_HOST || USB_DWC2_PERIPHERAL
+ help
+ The Designware USB2.0 platform interface module for
+ controllers directly connected to the CPU.
config USB_DWC2_PCI
- bool "DWC2 PCI"
+ tristate "DWC2 PCI"
depends on USB_DWC2_HOST && PCI
default USB_DWC2_HOST
help
The Designware USB2.0 PCI interface module for controllers
connected to a PCI bus. This is only used for host mode.
-comment "Gadget mode requires USB Gadget support to be enabled"
-
-config USB_DWC2_PERIPHERAL
- tristate "Gadget only mode"
- depends on USB_GADGET
- help
- The Designware USB2.0 high-speed gadget controller
- integrated into many SoCs.
-
config USB_DWC2_DEBUG
bool "Enable Debugging Messages"
help
diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile
index b73d2a527970..8f752679752a 100644
--- a/drivers/usb/dwc2/Makefile
+++ b/drivers/usb/dwc2/Makefile
@@ -1,28 +1,28 @@
ccflags-$(CONFIG_USB_DWC2_DEBUG) += -DDEBUG
ccflags-$(CONFIG_USB_DWC2_VERBOSE) += -DVERBOSE_DEBUG
-obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o
+obj-$(CONFIG_USB_DWC2) += dwc2.o
dwc2-y := core.o core_intr.o
-dwc2-y += hcd.o hcd_intr.o
-dwc2-y += hcd_queue.o hcd_ddma.o
+
+ifneq ($(filter y,$(CONFIG_USB_DWC2_HOST) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
+ dwc2-y += hcd.o hcd_intr.o
+ dwc2-y += hcd_queue.o hcd_ddma.o
+endif
+
+ifneq ($(filter y,$(CONFIG_USB_DWC2_PERIPHERAL) $(CONFIG_USB_DWC2_DUAL_ROLE)),)
+ dwc2-y += gadget.o
+endif
# NOTE: The previous s3c-hsotg peripheral mode only driver has been moved to
# this location and renamed gadget.c. When building for dynamically linked
-# modules, dwc2_gadget.ko will get built for peripheral mode. For host mode,
-# the core module will be dwc2.ko, the PCI bus interface module will called
-# dwc2_pci.ko and the platform interface module will be called dwc2_platform.ko.
-# At present the host and gadget driver will be separate drivers, but there
-# are plans in the near future to create a dual-role driver.
+# modules, dwc2.ko will get built for host mode, peripheral mode, and dual-role
+# mode. The PCI bus interface module will called dwc2_pci.ko and the platform
+# interface module will be called dwc2_platform.ko.
ifneq ($(CONFIG_USB_DWC2_PCI),)
- obj-$(CONFIG_USB_DWC2_HOST) += dwc2_pci.o
+ obj-$(CONFIG_USB_DWC2) += dwc2_pci.o
dwc2_pci-y := pci.o
endif
-ifneq ($(CONFIG_USB_DWC2_PLATFORM),)
- obj-$(CONFIG_USB_DWC2_HOST) += dwc2_platform.o
- dwc2_platform-y := platform.o
-endif
-
-obj-$(CONFIG_USB_DWC2_PERIPHERAL) += dwc2_gadget.o
-dwc2_gadget-y := gadget.o
+obj-$(CONFIG_USB_DWC2_PLATFORM) += dwc2_platform.o
+dwc2_platform-y := platform.o
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c
index d9269459d481..7605850b7a9c 100644
--- a/drivers/usb/dwc2/core.c
+++ b/drivers/usb/dwc2/core.c
@@ -458,16 +458,6 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
/* Clear the SRP success bit for FS-I2c */
hsotg->srp_success = 0;
- if (irq >= 0) {
- dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
- irq);
- retval = devm_request_irq(hsotg->dev, irq,
- dwc2_handle_common_intr, IRQF_SHARED,
- dev_name(hsotg->dev), hsotg);
- if (retval)
- return retval;
- }
-
/* Enable common interrupts */
dwc2_enable_common_interrupts(hsotg);
diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h
index 55c90c53f2d6..7a70a1349334 100644
--- a/drivers/usb/dwc2/core.h
+++ b/drivers/usb/dwc2/core.h
@@ -84,7 +84,7 @@ static const char * const s3c_hsotg_supply_names[] = {
*/
#define EP0_MPS_LIMIT 64
-struct s3c_hsotg;
+struct dwc2_hsotg;
struct s3c_hsotg_req;
/**
@@ -130,7 +130,7 @@ struct s3c_hsotg_req;
struct s3c_hsotg_ep {
struct usb_ep ep;
struct list_head queue;
- struct s3c_hsotg *parent;
+ struct dwc2_hsotg *parent;
struct s3c_hsotg_req *req;
struct dentry *debugfs;
@@ -155,67 +155,6 @@ struct s3c_hsotg_ep {
};
/**
- * struct s3c_hsotg - driver state.
- * @dev: The parent device supplied to the probe function
- * @driver: USB gadget driver
- * @phy: The otg phy transceiver structure for phy control.
- * @uphy: The otg phy transceiver structure for old USB phy control.
- * @plat: The platform specific configuration data. This can be removed once
- * all SoCs support usb transceiver.
- * @regs: The memory area mapped for accessing registers.
- * @irq: The IRQ number we are using
- * @supplies: Definition of USB power supplies
- * @phyif: PHY interface width
- * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
- * @num_of_eps: Number of available EPs (excluding EP0)
- * @debug_root: root directrory for debugfs.
- * @debug_file: main status file for debugfs.
- * @debug_fifo: FIFO status file for debugfs.
- * @ep0_reply: Request used for ep0 reply.
- * @ep0_buff: Buffer for EP0 reply data, if needed.
- * @ctrl_buff: Buffer for EP0 control requests.
- * @ctrl_req: Request for EP0 control packets.
- * @setup: NAK management for EP0 SETUP
- * @last_rst: Time of last reset
- * @eps: The endpoints being supplied to the gadget framework
- */
-struct s3c_hsotg {
- struct device *dev;
- struct usb_gadget_driver *driver;
- struct phy *phy;
- struct usb_phy *uphy;
- struct s3c_hsotg_plat *plat;
-
- spinlock_t lock;
-
- void __iomem *regs;
- int irq;
- struct clk *clk;
-
- struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
-
- u32 phyif;
- int fifo_mem;
- unsigned int dedicated_fifos:1;
- unsigned char num_of_eps;
- u32 fifo_map;
-
- struct dentry *debug_root;
- struct dentry *debug_file;
- struct dentry *debug_fifo;
-
- struct usb_request *ep0_reply;
- struct usb_request *ctrl_req;
- u8 ep0_buff[8];
- u8 ctrl_buff[8];
-
- struct usb_gadget gadget;
- unsigned int setup;
- unsigned long last_rst;
- struct s3c_hsotg_ep *eps;
-};
-
-/**
* struct s3c_hsotg_req - data transfer request
* @req: The USB gadget request
* @queue: The list of requests for the endpoint this is queued for.
@@ -229,6 +168,7 @@ struct s3c_hsotg_req {
unsigned char mapped;
};
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
#define call_gadget(_hs, _entry) \
do { \
if ((_hs)->gadget.speed != USB_SPEED_UNKNOWN && \
@@ -238,6 +178,9 @@ do { \
spin_lock(&_hs->lock); \
} \
} while (0)
+#else
+#define call_gadget(_hs, _entry) do {} while (0)
+#endif
struct dwc2_hsotg;
struct dwc2_host_chan;
@@ -495,11 +438,13 @@ struct dwc2_hw_params {
* struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
* and periodic schedules
*
+ * These are common for both host and peripheral modes:
+ *
* @dev: The struct device pointer
* @regs: Pointer to controller regs
- * @core_params: Parameters that define how the core should be configured
* @hw_params: Parameters that were autodetected from the
* hardware registers
+ * @core_params: Parameters that define how the core should be configured
* @op_state: The operational State, during transitions (a_host=>
* a_peripheral and b_device=>b_host) this may not match
* the core, but allows the software to determine
@@ -508,6 +453,8 @@ struct dwc2_hw_params {
* - USB_DR_MODE_PERIPHERAL
* - USB_DR_MODE_HOST
* - USB_DR_MODE_OTG
+ * @lock: Spinlock that protects all the driver data structures
+ * @priv: Stores a pointer to the struct usb_hcd
* @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
* transfer are in process of being queued
* @srp_success: Stores status of SRP request in the case of a FS PHY
@@ -517,6 +464,9 @@ struct dwc2_hw_params {
* interrupt
* @wkp_timer: Timer object for handling Wakeup Detected interrupt
* @lx_state: Lx state of connected device
+ *
+ * These are for host mode:
+ *
* @flags: Flags for handling root port state changes
* @non_periodic_sched_inactive: Inactive QHs in the non-periodic schedule.
* Transfers associated with these QHs are not currently
@@ -585,11 +535,31 @@ struct dwc2_hw_params {
* @status_buf_dma: DMA address for status_buf
* @start_work: Delayed work for handling host A-cable connection
* @reset_work: Delayed work for handling a port reset
- * @lock: Spinlock that protects all the driver data structures
- * @priv: Stores a pointer to the struct usb_hcd
* @otg_port: OTG port number
* @frame_list: Frame list
* @frame_list_dma: Frame list DMA address
+ *
+ * These are for peripheral mode:
+ *
+ * @driver: USB gadget driver
+ * @phy: The otg phy transceiver structure for phy control.
+ * @uphy: The otg phy transceiver structure for old USB phy control.
+ * @plat: The platform specific configuration data. This can be removed once
+ * all SoCs support usb transceiver.
+ * @supplies: Definition of USB power supplies
+ * @phyif: PHY interface width
+ * @dedicated_fifos: Set if the hardware has dedicated IN-EP fifos.
+ * @num_of_eps: Number of available EPs (excluding EP0)
+ * @debug_root: Root directrory for debugfs.
+ * @debug_file: Main status file for debugfs.
+ * @debug_fifo: FIFO status file for debugfs.
+ * @ep0_reply: Request used for ep0 reply.
+ * @ep0_buff: Buffer for EP0 reply data, if needed.
+ * @ctrl_buff: Buffer for EP0 control requests.
+ * @ctrl_req: Request for EP0 control packets.
+ * @setup: NAK management for EP0 SETUP
+ * @last_rst: Time of last reset
+ * @eps: The endpoints being supplied to the gadget framework
*/
struct dwc2_hsotg {
struct device *dev;
@@ -601,6 +571,16 @@ struct dwc2_hsotg {
enum usb_otg_state op_state;
enum usb_dr_mode dr_mode;
+ struct phy *phy;
+ struct usb_phy *uphy;
+ struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)];
+
+ spinlock_t lock;
+ struct mutex init_mutex;
+ void *priv;
+ int irq;
+ struct clk *clk;
+
unsigned int queuing_high_bandwidth:1;
unsigned int srp_success:1;
@@ -609,6 +589,18 @@ struct dwc2_hsotg {
struct timer_list wkp_timer;
enum dwc2_lx_state lx_state;
+ struct dentry *debug_root;
+ struct dentry *debug_file;
+ struct dentry *debug_fifo;
+
+ /* DWC OTG HW Release versions */
+#define DWC2_CORE_REV_2_71a 0x4f54271a
+#define DWC2_CORE_REV_2_90a 0x4f54290a
+#define DWC2_CORE_REV_2_92a 0x4f54292a
+#define DWC2_CORE_REV_2_94a 0x4f54294a
+#define DWC2_CORE_REV_3_00a 0x4f54300a
+
+#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
union dwc2_hcd_internal_flags {
u32 d32;
struct {
@@ -655,19 +647,10 @@ struct dwc2_hsotg {
struct delayed_work start_work;
struct delayed_work reset_work;
- spinlock_t lock;
- void *priv;
u8 otg_port;
u32 *frame_list;
dma_addr_t frame_list_dma;
- /* DWC OTG HW Release versions */
-#define DWC2_CORE_REV_2_71a 0x4f54271a
-#define DWC2_CORE_REV_2_90a 0x4f54290a
-#define DWC2_CORE_REV_2_92a 0x4f54292a
-#define DWC2_CORE_REV_2_94a 0x4f54294a
-#define DWC2_CORE_REV_3_00a 0x4f54300a
-
#ifdef DEBUG
u32 frrem_samples;
u64 frrem_accum;
@@ -686,6 +669,31 @@ struct dwc2_hsotg {
u32 hfnum_other_samples_b;
u64 hfnum_other_frrem_accum_b;
#endif
+#endif /* CONFIG_USB_DWC2_HOST || CONFIG_USB_DWC2_DUAL_ROLE */
+
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+ /* Gadget structures */
+ struct usb_gadget_driver *driver;
+ struct s3c_hsotg_plat *plat;
+
+ u32 phyif;
+ int fifo_mem;
+ unsigned int dedicated_fifos:1;
+ unsigned char num_of_eps;
+ u32 fifo_map;
+
+ struct usb_request *ep0_reply;
+ struct usb_request *ctrl_req;
+ u8 ep0_buff[8];
+ u8 ctrl_buff[8];
+
+ struct usb_gadget gadget;
+ unsigned int enabled:1;
+ unsigned int connected:1;
+ unsigned int setup:1;
+ unsigned long last_rst;
+ struct s3c_hsotg_ep *eps;
+#endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
};
/* Reasons for halting a host channel */
@@ -955,4 +963,43 @@ extern void dwc2_dump_global_registers(struct dwc2_hsotg *hsotg);
*/
extern u16 dwc2_get_otg_version(struct dwc2_hsotg *hsotg);
+/* Gadget defines */
+#if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg);
+extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2);
+extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2);
+extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
+extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2);
+extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
+extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
+#else
+static inline int s3c_hsotg_remove(struct dwc2_hsotg *dwc2)
+{ return 0; }
+static inline int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2)
+{ return 0; }
+static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2)
+{ return 0; }
+static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
+{ return 0; }
+static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2) {}
+static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
+static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
+#endif
+
+#if IS_ENABLED(CONFIG_USB_DWC2_HOST) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
+extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
+extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
+extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
+#else
+static inline void dwc2_set_all_params(struct dwc2_core_params *params, int value) {}
+static inline int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg)
+{ return 0; }
+static inline void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_hcd_start(struct dwc2_hsotg *hsotg) {}
+static inline void dwc2_hcd_remove(struct dwc2_hsotg *hsotg) {}
+static inline int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
+ const struct dwc2_core_params *params)
+{ return 0; }
+#endif
+
#endif /* __DWC2_CORE_H__ */
diff --git a/drivers/usb/dwc2/core_intr.c b/drivers/usb/dwc2/core_intr.c
index c93918b70d03..ad43c5bc1ef1 100644
--- a/drivers/usb/dwc2/core_intr.c
+++ b/drivers/usb/dwc2/core_intr.c
@@ -128,6 +128,9 @@ static void dwc2_handle_otg_intr(struct dwc2_hsotg *hsotg)
dwc2_op_state_str(hsotg));
gotgctl = readl(hsotg->regs + GOTGCTL);
+ if (dwc2_is_device_mode(hsotg))
+ s3c_hsotg_disconnect(hsotg);
+
if (hsotg->op_state == OTG_STATE_B_HOST) {
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
} else {
@@ -287,9 +290,11 @@ static void dwc2_handle_conn_id_status_change_intr(struct dwc2_hsotg *hsotg)
* Release lock before scheduling workq as it holds spinlock during
* scheduling.
*/
- spin_unlock(&hsotg->lock);
- queue_work(hsotg->wq_otg, &hsotg->wf_otg);
- spin_lock(&hsotg->lock);
+ if (hsotg->wq_otg) {
+ spin_unlock(&hsotg->lock);
+ queue_work(hsotg->wq_otg, &hsotg->wf_otg);
+ spin_lock(&hsotg->lock);
+ }
/* Clear interrupt */
writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
@@ -312,6 +317,12 @@ static void dwc2_handle_session_req_intr(struct dwc2_hsotg *hsotg)
/* Clear interrupt */
writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
+
+ /*
+ * Report disconnect if there is any previous session established
+ */
+ if (dwc2_is_device_mode(hsotg))
+ s3c_hsotg_disconnect(hsotg);
}
/*
diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c
index 8b5c079c7b7d..200168ec2d75 100644
--- a/drivers/usb/dwc2/gadget.c
+++ b/drivers/usb/dwc2/gadget.c
@@ -21,6 +21,7 @@
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/debugfs.h>
+#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/delay.h>
#include <linux/io.h>
@@ -36,6 +37,7 @@
#include <linux/platform_data/s3c-hsotg.h>
#include "core.h"
+#include "hw.h"
/* conversion functions */
static inline struct s3c_hsotg_req *our_req(struct usb_request *req)
@@ -48,9 +50,9 @@ static inline struct s3c_hsotg_ep *our_ep(struct usb_ep *ep)
return container_of(ep, struct s3c_hsotg_ep, ep);
}
-static inline struct s3c_hsotg *to_hsotg(struct usb_gadget *gadget)
+static inline struct dwc2_hsotg *to_hsotg(struct usb_gadget *gadget)
{
- return container_of(gadget, struct s3c_hsotg, gadget);
+ return container_of(gadget, struct dwc2_hsotg, gadget);
}
static inline void __orr32(void __iomem *ptr, u32 val)
@@ -64,7 +66,7 @@ static inline void __bic32(void __iomem *ptr, u32 val)
}
/* forward decleration of functions */
-static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
+static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
/**
* using_dma - return the DMA status of the driver.
@@ -85,7 +87,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg);
*
* Until this issue is sorted out, we always return 'false'.
*/
-static inline bool using_dma(struct s3c_hsotg *hsotg)
+static inline bool using_dma(struct dwc2_hsotg *hsotg)
{
return false; /* support is not complete */
}
@@ -95,7 +97,7 @@ static inline bool using_dma(struct s3c_hsotg *hsotg)
* @hsotg: The device state
* @ints: A bitmask of the interrupts to enable
*/
-static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
+static void s3c_hsotg_en_gsint(struct dwc2_hsotg *hsotg, u32 ints)
{
u32 gsintmsk = readl(hsotg->regs + GINTMSK);
u32 new_gsintmsk;
@@ -113,7 +115,7 @@ static void s3c_hsotg_en_gsint(struct s3c_hsotg *hsotg, u32 ints)
* @hsotg: The device state
* @ints: A bitmask of the interrupts to enable
*/
-static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
+static void s3c_hsotg_disable_gsint(struct dwc2_hsotg *hsotg, u32 ints)
{
u32 gsintmsk = readl(hsotg->regs + GINTMSK);
u32 new_gsintmsk;
@@ -134,7 +136,7 @@ static void s3c_hsotg_disable_gsint(struct s3c_hsotg *hsotg, u32 ints)
* Set or clear the mask for an individual endpoint's interrupt
* request.
*/
-static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_ctrl_epint(struct dwc2_hsotg *hsotg,
unsigned int ep, unsigned int dir_in,
unsigned int en)
{
@@ -159,7 +161,7 @@ static void s3c_hsotg_ctrl_epint(struct s3c_hsotg *hsotg,
* s3c_hsotg_init_fifo - initialise non-periodic FIFOs
* @hsotg: The device instance.
*/
-static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
{
unsigned int ep;
unsigned int addr;
@@ -283,7 +285,7 @@ static inline int is_ep_periodic(struct s3c_hsotg_ep *hs_ep)
* This is the reverse of s3c_hsotg_map_dma(), called for the completion
* of a request to ensure the buffer is ready for access by the caller.
*/
-static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_unmap_dma(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
struct s3c_hsotg_req *hs_req)
{
@@ -312,7 +314,7 @@ static void s3c_hsotg_unmap_dma(struct s3c_hsotg *hsotg,
*
* This routine is only needed for PIO
*/
-static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_write_fifo(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
struct s3c_hsotg_req *hs_req)
{
@@ -517,7 +519,7 @@ static unsigned get_ep_limit(struct s3c_hsotg_ep *hs_ep)
* Start the given request running by setting the endpoint registers
* appropriately, and writing any data to the FIFOs.
*/
-static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
struct s3c_hsotg_req *hs_req,
bool continuing)
@@ -707,7 +709,7 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
* DMA memory, then we map the memory and mark our request to allow us to
* cleanup on completion.
*/
-static int s3c_hsotg_map_dma(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_map_dma(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
struct usb_request *req)
{
@@ -736,7 +738,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
{
struct s3c_hsotg_req *hs_req = our_req(req);
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
+ struct dwc2_hsotg *hs = hs_ep->parent;
bool first;
dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
@@ -768,7 +770,7 @@ static int s3c_hsotg_ep_queue_lock(struct usb_ep *ep, struct usb_request *req,
gfp_t gfp_flags)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
+ struct dwc2_hsotg *hs = hs_ep->parent;
unsigned long flags = 0;
int ret = 0;
@@ -799,7 +801,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
struct usb_request *req)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
dev_dbg(hsotg->dev, "%s: ep %p, req %p\n", __func__, ep, req);
@@ -814,7 +816,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
* Convert the given wIndex into a pointer to an driver endpoint
* structure, or return NULL if it is not a valid endpoint.
*/
-static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
+static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
u32 windex)
{
struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
@@ -843,7 +845,7 @@ static struct s3c_hsotg_ep *ep_from_windex(struct s3c_hsotg *hsotg,
* Create a request and queue it on the given endpoint. This is useful as
* an internal method of sending replies to certain control requests, etc.
*/
-static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *ep,
void *buff,
int length)
@@ -884,7 +886,7 @@ static int s3c_hsotg_send_reply(struct s3c_hsotg *hsotg,
* @hsotg: The device state
* @ctrl: USB control request
*/
-static int s3c_hsotg_process_req_status(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
struct usb_ctrlrequest *ctrl)
{
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
@@ -955,7 +957,7 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
* @hsotg: The device state
* @ctrl: USB control request
*/
-static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
struct usb_ctrlrequest *ctrl)
{
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
@@ -1028,8 +1030,7 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
return 1;
}
-static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
-static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
+static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
/**
* s3c_hsotg_stall_ep0 - stall ep0
@@ -1037,7 +1038,7 @@ static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
*
* Set stall for ep0 as response for setup request.
*/
-static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
{
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
u32 reg;
@@ -1076,7 +1077,7 @@ static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg)
* needs to work out what to do next (and whether to pass it on to the
* gadget driver).
*/
-static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg,
struct usb_ctrlrequest *ctrl)
{
struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
@@ -1107,7 +1108,6 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
switch (ctrl->bRequest) {
case USB_REQ_SET_ADDRESS:
- s3c_hsotg_disconnect(hsotg);
dcfg = readl(hsotg->regs + DCFG);
dcfg &= ~DCFG_DEVADDR_MASK;
dcfg |= (le16_to_cpu(ctrl->wValue) <<
@@ -1161,7 +1161,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
struct usb_request *req)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
if (req->status < 0) {
dev_dbg(hsotg->dev, "%s: failed %d\n", __func__, req->status);
@@ -1183,7 +1183,7 @@ static void s3c_hsotg_complete_setup(struct usb_ep *ep,
* Enqueue a request on EP0 if necessary to received any SETUP packets
* received from the host.
*/
-static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
{
struct usb_request *req = hsotg->ctrl_req;
struct s3c_hsotg_req *hs_req = our_req(req);
@@ -1226,7 +1226,7 @@ static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg)
*
* Note, expects the ep to already be locked as appropriate.
*/
-static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
struct s3c_hsotg_req *hs_req,
int result)
@@ -1291,7 +1291,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg,
* endpoint, so sort out whether we need to read the data into a request
* that has been made for that endpoint.
*/
-static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
+static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
{
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
struct s3c_hsotg_req *hs_req = hs_ep->req;
@@ -1356,7 +1356,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
* currently believed that we do not need to wait for any space in
* the TxFIFO.
*/
-static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_req *req)
{
u32 ctrl;
@@ -1398,7 +1398,7 @@ static void s3c_hsotg_send_zlp(struct s3c_hsotg *hsotg,
* transfer for an OUT endpoint has been completed, either by a short
* packet or by the finish of a transfer.
*/
-static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
int epnum, bool was_setup)
{
u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
@@ -1471,7 +1471,7 @@ static void s3c_hsotg_handle_outdone(struct s3c_hsotg *hsotg,
*
* Return the current frame number
*/
-static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
+static u32 s3c_hsotg_read_frameno(struct dwc2_hsotg *hsotg)
{
u32 dsts;
@@ -1498,7 +1498,7 @@ static u32 s3c_hsotg_read_frameno(struct s3c_hsotg *hsotg)
* as the actual data should be sent to the memory directly and we turn
* on the completion interrupts to get notifications of transfer completion.
*/
-static void s3c_hsotg_handle_rx(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
{
u32 grxstsr = readl(hsotg->regs + GRXSTSP);
u32 epnum, status, size;
@@ -1590,7 +1590,7 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
* Configure the maximum packet size for the given endpoint, updating
* the hardware control registers to reflect this.
*/
-static void s3c_hsotg_set_ep_maxpacket(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
unsigned int ep, unsigned int mps)
{
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
@@ -1645,7 +1645,7 @@ bad_mps:
* @hsotg: The driver state
* @idx: The index for the endpoint (0..15)
*/
-static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
+static void s3c_hsotg_txfifo_flush(struct dwc2_hsotg *hsotg, unsigned int idx)
{
int timeout;
int val;
@@ -1681,7 +1681,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx)
* Check to see if there is a request that has data to send, and if so
* make an attempt to write data into the FIFO.
*/
-static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
+static int s3c_hsotg_trytx(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep)
{
struct s3c_hsotg_req *hs_req = hs_ep->req;
@@ -1714,7 +1714,7 @@ static int s3c_hsotg_trytx(struct s3c_hsotg *hsotg,
* An IN transfer has been completed, update the transfer's state and then
* call the relevant completion routines.
*/
-static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep)
{
struct s3c_hsotg_req *hs_req = hs_ep->req;
@@ -1791,7 +1791,7 @@ static void s3c_hsotg_complete_in(struct s3c_hsotg *hsotg,
*
* Process and clear any interrupt pending for an individual endpoint
*/
-static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
+static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
int dir_in)
{
struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
@@ -1916,7 +1916,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx,
* Handle updating the device settings after the enumeration phase has
* been completed.
*/
-static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
{
u32 dsts = readl(hsotg->regs + DSTS);
int ep0_mps = 0, ep_mps = 8;
@@ -1993,7 +1993,7 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
* Go through the requests on the given endpoint and mark them
* completed with the given result code.
*/
-static void kill_all_requests(struct s3c_hsotg *hsotg,
+static void kill_all_requests(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *ep,
int result, bool force)
{
@@ -2027,22 +2027,27 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
* transactions and signal the gadget driver that this
* has happened.
*/
-static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg)
+void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
{
unsigned ep;
+ if (!hsotg->connected)
+ return;
+
+ hsotg->connected = 0;
for (ep = 0; ep < hsotg->num_of_eps; ep++)
kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
call_gadget(hsotg, disconnect);
}
+EXPORT_SYMBOL_GPL(s3c_hsotg_disconnect);
/**
* s3c_hsotg_irq_fifoempty - TX FIFO empty interrupt handler
* @hsotg: The device state:
* @periodic: True if this is a periodic FIFO interrupt
*/
-static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
+static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
{
struct s3c_hsotg_ep *ep;
int epno, ret;
@@ -2076,7 +2081,7 @@ static void s3c_hsotg_irq_fifoempty(struct s3c_hsotg *hsotg, bool periodic)
*
* Issue a soft reset to the core, and await the core finishing it.
*/
-static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
+static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg)
{
int timeout;
u32 grstctl;
@@ -2124,7 +2129,7 @@ static int s3c_hsotg_corereset(struct s3c_hsotg *hsotg)
*
* Issue a soft reset to the core, and await the core finishing it.
*/
-static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
+void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
{
s3c_hsotg_corereset(hsotg);
@@ -2241,12 +2246,23 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
readl(hsotg->regs + DOEPCTL0));
/* clear global NAKs */
- writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK,
+ writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
hsotg->regs + DCTL);
/* must be at-least 3ms to allow bus to see disconnect */
mdelay(3);
+ hsotg->last_rst = jiffies;
+}
+
+static void s3c_hsotg_core_disconnect(struct dwc2_hsotg *hsotg)
+{
+ /* set the soft-disconnect bit */
+ __orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+}
+
+void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg)
+{
/* remove the soft-disconnect and let's go */
__bic32(hsotg->regs + DCTL, DCTL_SFTDISCON);
}
@@ -2258,7 +2274,7 @@ static void s3c_hsotg_core_init(struct s3c_hsotg *hsotg)
*/
static irqreturn_t s3c_hsotg_irq(int irq, void *pw)
{
- struct s3c_hsotg *hsotg = pw;
+ struct dwc2_hsotg *hsotg = pw;
int retry_count = 8;
u32 gintsts;
u32 gintmsk;
@@ -2273,31 +2289,11 @@ irq_retry:
gintsts &= gintmsk;
- if (gintsts & GINTSTS_OTGINT) {
- u32 otgint = readl(hsotg->regs + GOTGINT);
-
- dev_info(hsotg->dev, "OTGInt: %08x\n", otgint);
-
- writel(otgint, hsotg->regs + GOTGINT);
- }
-
- if (gintsts & GINTSTS_SESSREQINT) {
- dev_dbg(hsotg->dev, "%s: SessReqInt\n", __func__);
- writel(GINTSTS_SESSREQINT, hsotg->regs + GINTSTS);
- }
-
if (gintsts & GINTSTS_ENUMDONE) {
writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
s3c_hsotg_irq_enumdone(hsotg);
- }
-
- if (gintsts & GINTSTS_CONIDSTSCHNG) {
- dev_dbg(hsotg->dev, "ConIDStsChg (DSTS=0x%08x, GOTCTL=%08x)\n",
- readl(hsotg->regs + DSTS),
- readl(hsotg->regs + GOTGCTL));
-
- writel(GINTSTS_CONIDSTSCHNG, hsotg->regs + GINTSTS);
+ hsotg->connected = 1;
}
if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
@@ -2340,8 +2336,8 @@ irq_retry:
kill_all_requests(hsotg, &hsotg->eps[0],
-ECONNRESET, true);
- s3c_hsotg_core_init(hsotg);
- hsotg->last_rst = jiffies;
+ s3c_hsotg_core_init_disconnected(hsotg);
+ s3c_hsotg_core_connect(hsotg);
}
}
}
@@ -2380,25 +2376,6 @@ irq_retry:
s3c_hsotg_handle_rx(hsotg);
}
- if (gintsts & GINTSTS_MODEMIS) {
- dev_warn(hsotg->dev, "warning, mode mismatch triggered\n");
- writel(GINTSTS_MODEMIS, hsotg->regs + GINTSTS);
- }
-
- if (gintsts & GINTSTS_USBSUSP) {
- dev_info(hsotg->dev, "GINTSTS_USBSusp\n");
- writel(GINTSTS_USBSUSP, hsotg->regs + GINTSTS);
-
- call_gadget(hsotg, suspend);
- }
-
- if (gintsts & GINTSTS_WKUPINT) {
- dev_info(hsotg->dev, "GINTSTS_WkUpIn\n");
- writel(GINTSTS_WKUPINT, hsotg->regs + GINTSTS);
-
- call_gadget(hsotg, resume);
- }
-
if (gintsts & GINTSTS_ERLYSUSP) {
dev_dbg(hsotg->dev, "GINTSTS_ErlySusp\n");
writel(GINTSTS_ERLYSUSP, hsotg->regs + GINTSTS);
@@ -2450,7 +2427,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
const struct usb_endpoint_descriptor *desc)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
unsigned long flags;
int index = hs_ep->index;
u32 epctrl_reg;
@@ -2593,7 +2570,7 @@ error:
static int s3c_hsotg_ep_disable(struct usb_ep *ep)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hsotg = hs_ep->parent;
+ struct dwc2_hsotg *hsotg = hs_ep->parent;
int dir_in = hs_ep->dir_in;
int index = hs_ep->index;
unsigned long flags;
@@ -2658,7 +2635,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
{
struct s3c_hsotg_req *hs_req = our_req(req);
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
+ struct dwc2_hsotg *hs = hs_ep->parent;
unsigned long flags;
dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req);
@@ -2684,7 +2661,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
+ struct dwc2_hsotg *hs = hs_ep->parent;
int index = hs_ep->index;
u32 epreg;
u32 epctl;
@@ -2748,7 +2725,7 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
static int s3c_hsotg_ep_sethalt_lock(struct usb_ep *ep, int value)
{
struct s3c_hsotg_ep *hs_ep = our_ep(ep);
- struct s3c_hsotg *hs = hs_ep->parent;
+ struct dwc2_hsotg *hs = hs_ep->parent;
unsigned long flags = 0;
int ret = 0;
@@ -2777,7 +2754,7 @@ static struct usb_ep_ops s3c_hsotg_ep_ops = {
* A wrapper for platform code responsible for controlling
* low-level USB code
*/
-static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_phy_enable(struct dwc2_hsotg *hsotg)
{
struct platform_device *pdev = to_platform_device(hsotg->dev);
@@ -2800,7 +2777,7 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg)
* A wrapper for platform code responsible for controlling
* low-level USB code
*/
-static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg)
{
struct platform_device *pdev = to_platform_device(hsotg->dev);
@@ -2818,7 +2795,7 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg)
* s3c_hsotg_init - initalize the usb core
* @hsotg: The driver state
*/
-static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
{
/* unmask subset of endpoint interrupts */
@@ -2868,7 +2845,8 @@ static void s3c_hsotg_init(struct s3c_hsotg *hsotg)
static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver)
{
- struct s3c_hsotg *hsotg = to_hsotg(gadget);
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+ unsigned long flags;
int ret;
if (!hsotg) {
@@ -2889,6 +2867,7 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
return -EINVAL;
}
+ mutex_lock(&hsotg->init_mutex);
WARN_ON(hsotg->driver);
driver->driver.bus = NULL;
@@ -2905,11 +2884,22 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
goto err;
}
- hsotg->last_rst = jiffies;
+ s3c_hsotg_phy_enable(hsotg);
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ s3c_hsotg_init(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg);
+ hsotg->enabled = 0;
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
dev_info(hsotg->dev, "bound driver %s\n", driver->driver.name);
+
+ mutex_unlock(&hsotg->init_mutex);
+
return 0;
err:
+ mutex_unlock(&hsotg->init_mutex);
hsotg->driver = NULL;
return ret;
}
@@ -2921,16 +2911,17 @@ err:
*
* Stop udc hw block and stay tunned for future transmissions
*/
-static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
{
- struct s3c_hsotg *hsotg = to_hsotg(gadget);
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
unsigned long flags = 0;
int ep;
if (!hsotg)
return -ENODEV;
+ mutex_lock(&hsotg->init_mutex);
+
/* all endpoints should be shutdown */
for (ep = 1; ep < hsotg->num_of_eps; ep++)
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
@@ -2939,13 +2930,18 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget,
hsotg->driver = NULL;
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+ hsotg->enabled = 0;
spin_unlock_irqrestore(&hsotg->lock, flags);
+ s3c_hsotg_phy_disable(hsotg);
+
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
clk_disable(hsotg->clk);
+ mutex_unlock(&hsotg->init_mutex);
+
return 0;
}
@@ -2969,23 +2965,26 @@ static int s3c_hsotg_gadget_getframe(struct usb_gadget *gadget)
*/
static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
{
- struct s3c_hsotg *hsotg = to_hsotg(gadget);
+ struct dwc2_hsotg *hsotg = to_hsotg(gadget);
unsigned long flags = 0;
dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on);
+ mutex_lock(&hsotg->init_mutex);
spin_lock_irqsave(&hsotg->lock, flags);
if (is_on) {
- s3c_hsotg_phy_enable(hsotg);
clk_enable(hsotg->clk);
- s3c_hsotg_core_init(hsotg);
+ hsotg->enabled = 1;
+ s3c_hsotg_core_connect(hsotg);
} else {
+ s3c_hsotg_core_disconnect(hsotg);
+ hsotg->enabled = 0;
clk_disable(hsotg->clk);
- s3c_hsotg_phy_disable(hsotg);
}
hsotg->gadget.speed = USB_SPEED_UNKNOWN;
spin_unlock_irqrestore(&hsotg->lock, flags);
+ mutex_unlock(&hsotg->init_mutex);
return 0;
}
@@ -3007,7 +3006,7 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
* creation) to give to the gadget driver. Setup the endpoint name, any
* direction information and other state that may be required.
*/
-static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
+static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
struct s3c_hsotg_ep *hs_ep,
int epnum)
{
@@ -3056,7 +3055,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
*
* Read the USB core HW configuration registers
*/
-static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
{
u32 cfg2, cfg3, cfg4;
/* check hardware configuration */
@@ -3080,7 +3079,7 @@ static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg)
* s3c_hsotg_dump - dump state of the udc
* @param: The device state
*/
-static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
{
#ifdef DEBUG
struct device *dev = hsotg->dev;
@@ -3139,7 +3138,7 @@ static void s3c_hsotg_dump(struct s3c_hsotg *hsotg)
*/
static int state_show(struct seq_file *seq, void *v)
{
- struct s3c_hsotg *hsotg = seq->private;
+ struct dwc2_hsotg *hsotg = seq->private;
void __iomem *regs = hsotg->regs;
int idx;
@@ -3209,7 +3208,7 @@ static const struct file_operations state_fops = {
*/
static int fifo_show(struct seq_file *seq, void *v)
{
- struct s3c_hsotg *hsotg = seq->private;
+ struct dwc2_hsotg *hsotg = seq->private;
void __iomem *regs = hsotg->regs;
u32 val;
int idx;
@@ -3265,7 +3264,7 @@ static const char *decode_direction(int is_in)
static int ep_show(struct seq_file *seq, void *v)
{
struct s3c_hsotg_ep *ep = seq->private;
- struct s3c_hsotg *hsotg = ep->parent;
+ struct dwc2_hsotg *hsotg = ep->parent;
struct s3c_hsotg_req *req;
void __iomem *regs = hsotg->regs;
int index = ep->index;
@@ -3342,7 +3341,7 @@ static const struct file_operations ep_fops = {
* with the same name as the device itself, in case we end up
* with multiple blocks in future systems.
*/
-static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
{
struct dentry *root;
unsigned epidx;
@@ -3388,7 +3387,7 @@ static void s3c_hsotg_create_debug(struct s3c_hsotg *hsotg)
*
* Cleanup (remove) the debugfs files for use on module exit.
*/
-static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
+static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
{
unsigned epidx;
@@ -3403,27 +3402,21 @@ static void s3c_hsotg_delete_debug(struct s3c_hsotg *hsotg)
}
/**
- * s3c_hsotg_probe - probe function for hsotg driver
- * @pdev: The platform information for the driver
+ * dwc2_gadget_init - init function for gadget
+ * @dwc2: The data structure for the DWC2 driver.
+ * @irq: The IRQ number for the controller.
*/
-
-static int s3c_hsotg_probe(struct platform_device *pdev)
+int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
{
- struct s3c_hsotg_plat *plat = dev_get_platdata(&pdev->dev);
+ struct device *dev = hsotg->dev;
+ struct s3c_hsotg_plat *plat = dev->platform_data;
struct phy *phy;
struct usb_phy *uphy;
- struct device *dev = &pdev->dev;
struct s3c_hsotg_ep *eps;
- struct s3c_hsotg *hsotg;
- struct resource *res;
int epnum;
int ret;
int i;
- hsotg = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsotg), GFP_KERNEL);
- if (!hsotg)
- return -ENOMEM;
-
/* Set default UTMI width */
hsotg->phyif = GUSBCFG_PHYIF16;
@@ -3431,14 +3424,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
* Attempt to find a generic PHY, then look for an old style
* USB PHY, finally fall back to pdata
*/
- phy = devm_phy_get(&pdev->dev, "usb2-phy");
+ phy = devm_phy_get(dev, "usb2-phy");
if (IS_ERR(phy)) {
uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
if (IS_ERR(uphy)) {
/* Fallback for pdata */
- plat = dev_get_platdata(&pdev->dev);
+ plat = dev_get_platdata(dev);
if (!plat) {
- dev_err(&pdev->dev,
+ dev_err(dev,
"no platform data or transceiver defined\n");
return -EPROBE_DEFER;
}
@@ -3455,43 +3448,24 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
hsotg->phyif = GUSBCFG_PHYIF8;
}
- hsotg->dev = dev;
-
- hsotg->clk = devm_clk_get(&pdev->dev, "otg");
+ hsotg->clk = devm_clk_get(dev, "otg");
if (IS_ERR(hsotg->clk)) {
- dev_err(dev, "cannot get otg clock\n");
- return PTR_ERR(hsotg->clk);
- }
-
- platform_set_drvdata(pdev, hsotg);
-
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-
- hsotg->regs = devm_ioremap_resource(&pdev->dev, res);
- if (IS_ERR(hsotg->regs)) {
- ret = PTR_ERR(hsotg->regs);
- goto err_clk;
+ hsotg->clk = NULL;
+ dev_dbg(dev, "cannot get otg clock\n");
}
- ret = platform_get_irq(pdev, 0);
- if (ret < 0) {
- dev_err(dev, "cannot find IRQ\n");
- goto err_clk;
- }
-
- spin_lock_init(&hsotg->lock);
-
- hsotg->irq = ret;
-
- dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq);
-
hsotg->gadget.max_speed = USB_SPEED_HIGH;
hsotg->gadget.ops = &s3c_hsotg_gadget_ops;
hsotg->gadget.name = dev_name(dev);
/* reset the system */
- clk_prepare_enable(hsotg->clk);
+ ret = clk_prepare_enable(hsotg->clk);
+ if (ret) {
+ dev_err(dev, "failed to enable otg clk\n");
+ goto err_clk;
+ }
+
/* regulators */
@@ -3509,7 +3483,7 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
hsotg->supplies);
if (ret) {
- dev_err(hsotg->dev, "failed to enable supplies: %d\n", ret);
+ dev_err(dev, "failed to enable supplies: %d\n", ret);
goto err_supplies;
}
@@ -3520,14 +3494,14 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
s3c_hsotg_hw_cfg(hsotg);
s3c_hsotg_init(hsotg);
- ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0,
- dev_name(dev), hsotg);
+ ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED,
+ dev_name(hsotg->dev), hsotg);
if (ret < 0) {
s3c_hsotg_phy_disable(hsotg);
clk_disable_unprepare(hsotg->clk);
regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
- dev_err(dev, "cannot claim IRQ\n");
+ dev_err(dev, "cannot claim IRQ for gadget\n");
goto err_clk;
}
@@ -3573,11 +3547,11 @@ static int s3c_hsotg_probe(struct platform_device *pdev)
ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
hsotg->supplies);
if (ret) {
- dev_err(hsotg->dev, "failed to disable supplies: %d\n", ret);
+ dev_err(dev, "failed to disable supplies: %d\n", ret);
goto err_ep_mem;
}
- ret = usb_add_gadget_udc(&pdev->dev, &hsotg->gadget);
+ ret = usb_add_gadget_udc(dev, &hsotg->gadget);
if (ret)
goto err_ep_mem;
@@ -3596,47 +3570,44 @@ err_clk:
return ret;
}
+EXPORT_SYMBOL_GPL(dwc2_gadget_init);
/**
* s3c_hsotg_remove - remove function for hsotg driver
* @pdev: The platform information for the driver
*/
-static int s3c_hsotg_remove(struct platform_device *pdev)
+int s3c_hsotg_remove(struct dwc2_hsotg *hsotg)
{
- struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
-
usb_del_gadget_udc(&hsotg->gadget);
-
s3c_hsotg_delete_debug(hsotg);
-
- if (hsotg->driver) {
- /* should have been done already by driver model core */
- usb_gadget_unregister_driver(hsotg->driver);
- }
-
clk_disable_unprepare(hsotg->clk);
return 0;
}
+EXPORT_SYMBOL_GPL(s3c_hsotg_remove);
-static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
+int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
{
- struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
unsigned long flags;
int ret = 0;
- if (hsotg->driver)
+ mutex_lock(&hsotg->init_mutex);
+
+ if (hsotg->driver) {
+ int ep;
+
dev_info(hsotg->dev, "suspending usb gadget %s\n",
hsotg->driver->driver.name);
- spin_lock_irqsave(&hsotg->lock, flags);
- s3c_hsotg_disconnect(hsotg);
- s3c_hsotg_phy_disable(hsotg);
- hsotg->gadget.speed = USB_SPEED_UNKNOWN;
- spin_unlock_irqrestore(&hsotg->lock, flags);
+ spin_lock_irqsave(&hsotg->lock, flags);
+ if (hsotg->enabled)
+ s3c_hsotg_core_disconnect(hsotg);
+ s3c_hsotg_disconnect(hsotg);
+ hsotg->gadget.speed = USB_SPEED_UNKNOWN;
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+
+ s3c_hsotg_phy_disable(hsotg);
- if (hsotg->driver) {
- int ep;
for (ep = 0; ep < hsotg->num_of_eps; ep++)
s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
@@ -3645,57 +3616,37 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
clk_disable(hsotg->clk);
}
+ mutex_unlock(&hsotg->init_mutex);
+
return ret;
}
+EXPORT_SYMBOL_GPL(s3c_hsotg_suspend);
-static int s3c_hsotg_resume(struct platform_device *pdev)
+int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
{
- struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
unsigned long flags;
int ret = 0;
+ mutex_lock(&hsotg->init_mutex);
+
if (hsotg->driver) {
dev_info(hsotg->dev, "resuming usb gadget %s\n",
hsotg->driver->driver.name);
clk_enable(hsotg->clk);
ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies),
- hsotg->supplies);
- }
+ hsotg->supplies);
- spin_lock_irqsave(&hsotg->lock, flags);
- hsotg->last_rst = jiffies;
- s3c_hsotg_phy_enable(hsotg);
- s3c_hsotg_core_init(hsotg);
- spin_unlock_irqrestore(&hsotg->lock, flags);
+ s3c_hsotg_phy_enable(hsotg);
+
+ spin_lock_irqsave(&hsotg->lock, flags);
+ s3c_hsotg_core_init_disconnected(hsotg);
+ if (hsotg->enabled)
+ s3c_hsotg_core_connect(hsotg);
+ spin_unlock_irqrestore(&hsotg->lock, flags);
+ }
+ mutex_unlock(&hsotg->init_mutex);
return ret;
}
-
-#ifdef CONFIG_OF
-static const struct of_device_id s3c_hsotg_of_ids[] = {
- { .compatible = "samsung,s3c6400-hsotg", },
- { .compatible = "snps,dwc2", },
- { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, s3c_hsotg_of_ids);
-#endif
-
-static struct platform_driver s3c_hsotg_driver = {
- .driver = {
- .name = "s3c-hsotg",
- .owner = THIS_MODULE,
- .of_match_table = of_match_ptr(s3c_hsotg_of_ids),
- },
- .probe = s3c_hsotg_probe,
- .remove = s3c_hsotg_remove,
- .suspend = s3c_hsotg_suspend,
- .resume = s3c_hsotg_resume,
-};
-
-module_platform_driver(s3c_hsotg_driver);
-
-MODULE_DESCRIPTION("Samsung S3C USB High-speed/OtG device");
-MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:s3c-hsotg");
+EXPORT_SYMBOL_GPL(s3c_hsotg_resume);
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
index 0a0e6f0ad15f..a0cd9db6f4cd 100644
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -1371,6 +1371,8 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
hsotg->op_state = OTG_STATE_B_PERIPHERAL;
dwc2_core_init(hsotg, false, -1);
dwc2_enable_global_interrupts(hsotg);
+ s3c_hsotg_core_init_disconnected(hsotg);
+ s3c_hsotg_core_connect(hsotg);
} else {
/* A-Device connector (Host Mode) */
dev_dbg(hsotg->dev, "connId A\n");
@@ -1471,6 +1473,30 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
}
}
+static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
+{
+ u32 hprt0;
+
+ /* After clear the Stop PHY clock bit, we should wait for a moment
+ * for PLL work stable with clock output.
+ */
+ writel(0, hsotg->regs + PCGCTL);
+ usleep_range(2000, 4000);
+
+ hprt0 = dwc2_read_hprt0(hsotg);
+ hprt0 |= HPRT0_RES;
+ writel(hprt0, hsotg->regs + HPRT0);
+ hprt0 &= ~HPRT0_SUSP;
+ /* according to USB2.0 Spec 7.1.7.7, the host must send the resume
+ * signal for at least 20ms
+ */
+ usleep_range(20000, 25000);
+
+ hprt0 &= ~HPRT0_RES;
+ writel(hprt0, hsotg->regs + HPRT0);
+ hsotg->lx_state = DWC2_L0;
+}
+
/* Handles hub class-specific requests */
static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
u16 wvalue, u16 windex, char *buf, u16 wlength)
@@ -1516,17 +1542,7 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
case USB_PORT_FEAT_SUSPEND:
dev_dbg(hsotg->dev,
"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
- writel(0, hsotg->regs + PCGCTL);
- usleep_range(20000, 40000);
-
- hprt0 = dwc2_read_hprt0(hsotg);
- hprt0 |= HPRT0_RES;
- writel(hprt0, hsotg->regs + HPRT0);
- hprt0 &= ~HPRT0_SUSP;
- usleep_range(100000, 150000);
-
- hprt0 &= ~HPRT0_RES;
- writel(hprt0, hsotg->regs + HPRT0);
+ dwc2_port_resume(hsotg);
break;
case USB_PORT_FEAT_POWER:
@@ -2299,6 +2315,55 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
usleep_range(1000, 3000);
}
+static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
+{
+ struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+ u32 hprt0;
+
+ if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
+ (hsotg->op_state == OTG_STATE_A_HOST)))
+ return 0;
+
+ /* TODO: We get into suspend from 'on' state, maybe we need to do
+ * something if we get here from DWC2_L1(LPM sleep) state one day.
+ */
+ if (hsotg->lx_state != DWC2_L0)
+ return 0;
+
+ hprt0 = dwc2_read_hprt0(hsotg);
+ if (hprt0 & HPRT0_CONNSTS) {
+ dwc2_port_suspend(hsotg, 1);
+ } else {
+ u32 pcgctl = readl(hsotg->regs + PCGCTL);
+
+ pcgctl |= PCGCTL_STOPPCLK;
+ writel(pcgctl, hsotg->regs + PCGCTL);
+ }
+
+ return 0;
+}
+
+static int _dwc2_hcd_resume(struct usb_hcd *hcd)
+{
+ struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
+ u32 hprt0;
+
+ if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
+ (hsotg->op_state == OTG_STATE_A_HOST)))
+ return 0;
+
+ if (hsotg->lx_state != DWC2_L2)
+ return 0;
+
+ hprt0 = dwc2_read_hprt0(hsotg);
+ if ((hprt0 & HPRT0_CONNSTS) && (hprt0 & HPRT0_SUSP))
+ dwc2_port_resume(hsotg);
+ else
+ writel(0, hsotg->regs + PCGCTL);
+
+ return 0;
+}
+
/* Returns the current frame number */
static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
{
@@ -2669,6 +2734,9 @@ static struct hc_driver dwc2_hc_driver = {
.hub_status_data = _dwc2_hcd_hub_status_data,
.hub_control = _dwc2_hcd_hub_control,
.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
+
+ .bus_suspend = _dwc2_hcd_suspend,
+ .bus_resume = _dwc2_hcd_resume,
};
/*
@@ -2778,6 +2846,9 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
int i, num_channels;
int retval;
+ if (usb_disabled())
+ return -ENODEV;
+
dev_dbg(hsotg->dev, "DWC OTG HCD INIT\n");
/* Detect config values from hardware */
@@ -2839,7 +2910,6 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg, int irq,
hcd->has_tt = 1;
- spin_lock_init(&hsotg->lock);
((struct wrapper_priv_data *) &hcd->hcd_priv)->hsotg = hsotg;
hsotg->priv = hcd;
diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h
index a12bb1538666..e69a843d8928 100644
--- a/drivers/usb/dwc2/hcd.h
+++ b/drivers/usb/dwc2/hcd.h
@@ -668,9 +668,6 @@ extern irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg);
*/
extern void dwc2_hcd_stop(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_start(struct dwc2_hsotg *hsotg);
-extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
-
/**
* dwc2_hcd_is_b_host() - Returns 1 if core currently is acting as B host,
* and 0 otherwise
@@ -680,13 +677,6 @@ extern void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg);
extern int dwc2_hcd_is_b_host(struct dwc2_hsotg *hsotg);
/**
- * dwc2_hcd_get_frame_number() - Returns current frame number
- *
- * @hsotg: The DWC2 HCD
- */
-extern int dwc2_hcd_get_frame_number(struct dwc2_hsotg *hsotg);
-
-/**
* dwc2_hcd_dump_state() - Dumps hsotg state
*
* @hsotg: The DWC2 HCD
diff --git a/drivers/usb/dwc2/pci.c b/drivers/usb/dwc2/pci.c
index c291fca5d21f..a4e724b0a62e 100644
--- a/drivers/usb/dwc2/pci.c
+++ b/drivers/usb/dwc2/pci.c
@@ -141,6 +141,13 @@ static int dwc2_driver_probe(struct pci_dev *dev,
pci_set_master(dev);
+ retval = devm_request_irq(hsotg->dev, dev->irq,
+ dwc2_handle_common_intr, IRQF_SHARED,
+ dev_name(hsotg->dev), hsotg);
+ if (retval)
+ return retval;
+
+ spin_lock_init(&hsotg->lock);
retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params);
if (retval) {
pci_disable_device(dev);
diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c
index 121dbdafc06b..6a795aa2ff05 100644
--- a/drivers/usb/dwc2/platform.c
+++ b/drivers/usb/dwc2/platform.c
@@ -40,6 +40,7 @@
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/of_device.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/usb/of.h>
@@ -121,6 +122,7 @@ static int dwc2_driver_remove(struct platform_device *dev)
struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
dwc2_hcd_remove(hsotg);
+ s3c_hsotg_remove(hsotg);
return 0;
}
@@ -129,6 +131,7 @@ static const struct of_device_id dwc2_of_match_table[] = {
{ .compatible = "brcm,bcm2835-usb", .data = &params_bcm2835 },
{ .compatible = "rockchip,rk3066-usb", .data = &params_rk3066 },
{ .compatible = "snps,dwc2", .data = NULL },
+ { .compatible = "samsung,s3c6400-hsotg", .data = NULL},
{},
};
MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
@@ -155,9 +158,6 @@ static int dwc2_driver_probe(struct platform_device *dev)
int retval;
int irq;
- if (usb_disabled())
- return -ENODEV;
-
match = of_match_device(dwc2_of_match_table, &dev->dev);
if (match && match->data) {
params = match->data;
@@ -194,6 +194,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
return irq;
}
+ dev_dbg(hsotg->dev, "registering common handler for irq%d\n",
+ irq);
+ retval = devm_request_irq(hsotg->dev, irq,
+ dwc2_handle_common_intr, IRQF_SHARED,
+ dev_name(hsotg->dev), hsotg);
+ if (retval)
+ return retval;
+
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
hsotg->regs = devm_ioremap_resource(&dev->dev, res);
if (IS_ERR(hsotg->regs))
@@ -204,6 +212,11 @@ static int dwc2_driver_probe(struct platform_device *dev)
hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
+ spin_lock_init(&hsotg->lock);
+ mutex_init(&hsotg->init_mutex);
+ retval = dwc2_gadget_init(hsotg, irq);
+ if (retval)
+ return retval;
retval = dwc2_hcd_init(hsotg, irq, params);
if (retval)
return retval;
@@ -213,10 +226,35 @@ static int dwc2_driver_probe(struct platform_device *dev)
return retval;
}
+static int __maybe_unused dwc2_suspend(struct device *dev)
+{
+ struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (dwc2_is_device_mode(dwc2))
+ ret = s3c_hsotg_suspend(dwc2);
+ return ret;
+}
+
+static int __maybe_unused dwc2_resume(struct device *dev)
+{
+ struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
+ int ret = 0;
+
+ if (dwc2_is_device_mode(dwc2))
+ ret = s3c_hsotg_resume(dwc2);
+ return ret;
+}
+
+static const struct dev_pm_ops dwc2_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(dwc2_suspend, dwc2_resume)
+};
+
static struct platform_driver dwc2_platform_driver = {
.driver = {
.name = dwc2_driver_name,
.of_match_table = dwc2_of_match_table,
+ .pm = &dwc2_dev_pm_ops,
},
.probe = dwc2_driver_probe,
.remove = dwc2_driver_remove,
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index f4e5cc60db0b..58b5b2cde4c5 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -55,7 +55,7 @@ config USB_DWC3_OMAP
config USB_DWC3_EXYNOS
tristate "Samsung Exynos Platform"
- depends on ARCH_EXYNOS || COMPILE_TEST
+ depends on ARCH_EXYNOS && OF || COMPILE_TEST
default USB_DWC3
help
Recent Exynos5 SoCs ship with one DesignWare Core USB3 IP inside,
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index b0f4d52b7f04..25ddc39efad8 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
+#include <linux/version.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
@@ -32,6 +33,7 @@
#include <linux/delay.h>
#include <linux/dma-mapping.h>
#include <linux/of.h>
+#include <linux/acpi.h>
#include <linux/usb/ch9.h>
#include <linux/usb/gadget.h>
@@ -363,6 +365,72 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
}
/**
+ * dwc3_phy_setup - Configure USB PHY Interface of DWC3 Core
+ * @dwc: Pointer to our controller context structure
+ */
+static void dwc3_phy_setup(struct dwc3 *dwc)
+{
+ u32 reg;
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
+
+ /*
+ * Above 1.94a, it is recommended to set DWC3_GUSB3PIPECTL_SUSPHY
+ * to '0' during coreConsultant configuration. So default value
+ * will be '0' when the core is reset. Application needs to set it
+ * to '1' after the core initialization is completed.
+ */
+ if (dwc->revision > DWC3_REVISION_194A)
+ reg |= DWC3_GUSB3PIPECTL_SUSPHY;
+
+ if (dwc->u2ss_inp3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_U2SSINP3OK;
+
+ if (dwc->req_p1p2p3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_REQP1P2P3;
+
+ if (dwc->del_p1p2p3_quirk)
+ reg |= DWC3_GUSB3PIPECTL_DEP1P2P3_EN;
+
+ if (dwc->del_phy_power_chg_quirk)
+ reg |= DWC3_GUSB3PIPECTL_DEPOCHANGE;
+
+ if (dwc->lfps_filter_quirk)
+ reg |= DWC3_GUSB3PIPECTL_LFPSFILT;
+
+ if (dwc->rx_detect_poll_quirk)
+ reg |= DWC3_GUSB3PIPECTL_RX_DETOPOLL;
+
+ if (dwc->tx_de_emphasis_quirk)
+ reg |= DWC3_GUSB3PIPECTL_TX_DEEPH(dwc->tx_de_emphasis);
+
+ if (dwc->dis_u3_susphy_quirk)
+ reg &= ~DWC3_GUSB3PIPECTL_SUSPHY;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg);
+
+ mdelay(100);
+
+ reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0));
+
+ /*
+ * Above 1.94a, it is recommended to set DWC3_GUSB2PHYCFG_SUSPHY to
+ * '0' during coreConsultant configuration. So default value will
+ * be '0' when the core is reset. Application needs to set it to
+ * '1' after the core initialization is completed.
+ */
+ if (dwc->revision > DWC3_REVISION_194A)
+ reg |= DWC3_GUSB2PHYCFG_SUSPHY;
+
+ if (dwc->dis_u2_susphy_quirk)
+ reg &= ~DWC3_GUSB2PHYCFG_SUSPHY;
+
+ dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg);
+
+ mdelay(100);
+}
+
+/**
* dwc3_core_init - Low-level initialization of DWC3 Core
* @dwc: Pointer to our controller context structure
*
@@ -384,6 +452,12 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
dwc->revision = reg;
+ /*
+ * Write Linux Version Code to our GUID register so it's easy to figure
+ * out which kernel version a bug was found.
+ */
+ dwc3_writel(dwc->regs, DWC3_GUID, LINUX_VERSION_CODE);
+
/* Handle USB2.0-only core configuration */
if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
@@ -414,7 +488,6 @@ static int dwc3_core_init(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_GCTL);
reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
- reg &= ~DWC3_GCTL_DISSCRAMBLE;
switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
@@ -441,11 +514,34 @@ static int dwc3_core_init(struct dwc3 *dwc)
case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
/* enable hibernation here */
dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
+
+ /*
+ * REVISIT Enabling this bit so that host-mode hibernation
+ * will work. Device-mode hibernation is not yet implemented.
+ */
+ reg |= DWC3_GCTL_GBLHIBERNATIONEN;
break;
default:
dev_dbg(dwc->dev, "No power optimization available\n");
}
+ /* check if current dwc3 is on simulation board */
+ if (dwc->hwparams.hwparams6 & DWC3_GHWPARAMS6_EN_FPGA) {
+ dev_dbg(dwc->dev, "it is on FPGA board\n");
+ dwc->is_fpga = true;
+ }
+
+ WARN_ONCE(dwc->disable_scramble_quirk && !dwc->is_fpga,
+ "disable_scramble cannot be used on non-FPGA builds\n");
+
+ if (dwc->disable_scramble_quirk && dwc->is_fpga)
+ reg |= DWC3_GCTL_DISSCRAMBLE;
+ else
+ reg &= ~DWC3_GCTL_DISSCRAMBLE;
+
+ if (dwc->u2exit_lfps_quirk)
+ reg |= DWC3_GCTL_U2EXIT_LFPS;
+
/*
* WORKAROUND: DWC3 revisions <1.90a have a bug
* where the device can fail to connect at SuperSpeed
@@ -459,6 +555,8 @@ static int dwc3_core_init(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+ dwc3_phy_setup(dwc);
+
ret = dwc3_alloc_scratch_buffers(dwc);
if (ret)
goto err1;
@@ -630,6 +728,9 @@ static int dwc3_probe(struct platform_device *pdev)
struct device_node *node = dev->of_node;
struct resource *res;
struct dwc3 *dwc;
+ u8 lpm_nyet_threshold;
+ u8 tx_de_emphasis;
+ u8 hird_threshold;
int ret;
@@ -685,22 +786,96 @@ static int dwc3_probe(struct platform_device *pdev)
*/
res->start -= DWC3_GLOBALS_REGS_START;
+ /* default to highest possible threshold */
+ lpm_nyet_threshold = 0xff;
+
+ /* default to -3.5dB de-emphasis */
+ tx_de_emphasis = 1;
+
+ /*
+ * default to assert utmi_sleep_n and use maximum allowed HIRD
+ * threshold value of 0b1100
+ */
+ hird_threshold = 12;
+
if (node) {
dwc->maximum_speed = of_usb_get_maximum_speed(node);
-
- dwc->needs_fifo_resize = of_property_read_bool(node, "tx-fifo-resize");
+ dwc->has_lpm_erratum = of_property_read_bool(node,
+ "snps,has-lpm-erratum");
+ of_property_read_u8(node, "snps,lpm-nyet-threshold",
+ &lpm_nyet_threshold);
+ dwc->is_utmi_l1_suspend = of_property_read_bool(node,
+ "snps,is-utmi-l1-suspend");
+ of_property_read_u8(node, "snps,hird-threshold",
+ &hird_threshold);
+
+ dwc->needs_fifo_resize = of_property_read_bool(node,
+ "tx-fifo-resize");
dwc->dr_mode = of_usb_get_dr_mode(node);
+
+ dwc->disable_scramble_quirk = of_property_read_bool(node,
+ "snps,disable_scramble_quirk");
+ dwc->u2exit_lfps_quirk = of_property_read_bool(node,
+ "snps,u2exit_lfps_quirk");
+ dwc->u2ss_inp3_quirk = of_property_read_bool(node,
+ "snps,u2ss_inp3_quirk");
+ dwc->req_p1p2p3_quirk = of_property_read_bool(node,
+ "snps,req_p1p2p3_quirk");
+ dwc->del_p1p2p3_quirk = of_property_read_bool(node,
+ "snps,del_p1p2p3_quirk");
+ dwc->del_phy_power_chg_quirk = of_property_read_bool(node,
+ "snps,del_phy_power_chg_quirk");
+ dwc->lfps_filter_quirk = of_property_read_bool(node,
+ "snps,lfps_filter_quirk");
+ dwc->rx_detect_poll_quirk = of_property_read_bool(node,
+ "snps,rx_detect_poll_quirk");
+ dwc->dis_u3_susphy_quirk = of_property_read_bool(node,
+ "snps,dis_u3_susphy_quirk");
+ dwc->dis_u2_susphy_quirk = of_property_read_bool(node,
+ "snps,dis_u2_susphy_quirk");
+
+ dwc->tx_de_emphasis_quirk = of_property_read_bool(node,
+ "snps,tx_de_emphasis_quirk");
+ of_property_read_u8(node, "snps,tx_de_emphasis",
+ &tx_de_emphasis);
} else if (pdata) {
dwc->maximum_speed = pdata->maximum_speed;
+ dwc->has_lpm_erratum = pdata->has_lpm_erratum;
+ if (pdata->lpm_nyet_threshold)
+ lpm_nyet_threshold = pdata->lpm_nyet_threshold;
+ dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend;
+ if (pdata->hird_threshold)
+ hird_threshold = pdata->hird_threshold;
dwc->needs_fifo_resize = pdata->tx_fifo_resize;
dwc->dr_mode = pdata->dr_mode;
+
+ dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
+ dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk;
+ dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk;
+ dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk;
+ dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk;
+ dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk;
+ dwc->lfps_filter_quirk = pdata->lfps_filter_quirk;
+ dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk;
+ dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk;
+ dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk;
+
+ dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk;
+ if (pdata->tx_de_emphasis)
+ tx_de_emphasis = pdata->tx_de_emphasis;
}
/* default to superspeed if no maximum_speed passed */
if (dwc->maximum_speed == USB_SPEED_UNKNOWN)
dwc->maximum_speed = USB_SPEED_SUPER;
+ dwc->lpm_nyet_threshold = lpm_nyet_threshold;
+ dwc->tx_de_emphasis = tx_de_emphasis;
+
+ dwc->hird_threshold = hird_threshold
+ | (dwc->is_utmi_l1_suspend << 4);
+
ret = dwc3_core_get_phy(dwc);
if (ret)
return ret;
@@ -708,9 +883,11 @@ static int dwc3_probe(struct platform_device *pdev)
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
- dev->dma_mask = dev->parent->dma_mask;
- dev->dma_parms = dev->parent->dma_parms;
- dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
+ if (!dev->dma_mask) {
+ dev->dma_mask = dev->parent->dma_mask;
+ dev->dma_parms = dev->parent->dma_parms;
+ dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
+ }
pm_runtime_enable(dev);
pm_runtime_get_sync(dev);
@@ -815,50 +992,6 @@ static int dwc3_remove(struct platform_device *pdev)
}
#ifdef CONFIG_PM_SLEEP
-static int dwc3_prepare(struct device *dev)
-{
- struct dwc3 *dwc = dev_get_drvdata(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&dwc->lock, flags);
-
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
- dwc3_gadget_prepare(dwc);
- /* FALLTHROUGH */
- case USB_DR_MODE_HOST:
- default:
- dwc3_event_buffers_cleanup(dwc);
- break;
- }
-
- spin_unlock_irqrestore(&dwc->lock, flags);
-
- return 0;
-}
-
-static void dwc3_complete(struct device *dev)
-{
- struct dwc3 *dwc = dev_get_drvdata(dev);
- unsigned long flags;
-
- spin_lock_irqsave(&dwc->lock, flags);
-
- dwc3_event_buffers_setup(dwc);
- switch (dwc->dr_mode) {
- case USB_DR_MODE_PERIPHERAL:
- case USB_DR_MODE_OTG:
- dwc3_gadget_complete(dwc);
- /* FALLTHROUGH */
- case USB_DR_MODE_HOST:
- default:
- break;
- }
-
- spin_unlock_irqrestore(&dwc->lock, flags);
-}
-
static int dwc3_suspend(struct device *dev)
{
struct dwc3 *dwc = dev_get_drvdata(dev);
@@ -873,7 +1006,7 @@ static int dwc3_suspend(struct device *dev)
/* FALLTHROUGH */
case USB_DR_MODE_HOST:
default:
- /* do nothing */
+ dwc3_event_buffers_cleanup(dwc);
break;
}
@@ -906,6 +1039,7 @@ static int dwc3_resume(struct device *dev)
spin_lock_irqsave(&dwc->lock, flags);
+ dwc3_event_buffers_setup(dwc);
dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl);
switch (dwc->dr_mode) {
@@ -934,9 +1068,6 @@ err_usb2phy_init:
}
static const struct dev_pm_ops dwc3_dev_pm_ops = {
- .prepare = dwc3_prepare,
- .complete = dwc3_complete,
-
SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume)
};
@@ -958,12 +1089,24 @@ static const struct of_device_id of_dwc3_match[] = {
MODULE_DEVICE_TABLE(of, of_dwc3_match);
#endif
+#ifdef CONFIG_ACPI
+
+#define ACPI_ID_INTEL_BSW "808622B7"
+
+static const struct acpi_device_id dwc3_acpi_match[] = {
+ { ACPI_ID_INTEL_BSW, 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, dwc3_acpi_match);
+#endif
+
static struct platform_driver dwc3_driver = {
.probe = dwc3_probe,
.remove = dwc3_remove,
.driver = {
.name = "dwc3",
.of_match_table = of_match_ptr(of_dwc3_match),
+ .acpi_match_table = ACPI_PTR(dwc3_acpi_match),
.pm = DWC3_PM_OPS,
},
};
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 66f62563bcf9..4bb9aa696ede 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -166,6 +166,7 @@
#define DWC3_GCTL_SCALEDOWN(n) ((n) << 4)
#define DWC3_GCTL_SCALEDOWN_MASK DWC3_GCTL_SCALEDOWN(3)
#define DWC3_GCTL_DISSCRAMBLE (1 << 3)
+#define DWC3_GCTL_U2EXIT_LFPS (1 << 2)
#define DWC3_GCTL_GBLHIBERNATIONEN (1 << 1)
#define DWC3_GCTL_DSBLCLKGTNG (1 << 0)
@@ -175,7 +176,17 @@
/* Global USB3 PIPE Control Register */
#define DWC3_GUSB3PIPECTL_PHYSOFTRST (1 << 31)
+#define DWC3_GUSB3PIPECTL_U2SSINP3OK (1 << 29)
+#define DWC3_GUSB3PIPECTL_REQP1P2P3 (1 << 24)
+#define DWC3_GUSB3PIPECTL_DEP1P2P3(n) ((n) << 19)
+#define DWC3_GUSB3PIPECTL_DEP1P2P3_MASK DWC3_GUSB3PIPECTL_DEP1P2P3(7)
+#define DWC3_GUSB3PIPECTL_DEP1P2P3_EN DWC3_GUSB3PIPECTL_DEP1P2P3(1)
+#define DWC3_GUSB3PIPECTL_DEPOCHANGE (1 << 18)
#define DWC3_GUSB3PIPECTL_SUSPHY (1 << 17)
+#define DWC3_GUSB3PIPECTL_LFPSFILT (1 << 9)
+#define DWC3_GUSB3PIPECTL_RX_DETOPOLL (1 << 8)
+#define DWC3_GUSB3PIPECTL_TX_DEEPH_MASK DWC3_GUSB3PIPECTL_TX_DEEPH(3)
+#define DWC3_GUSB3PIPECTL_TX_DEEPH(n) ((n) << 1)
/* Global TX Fifo Size Register */
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
@@ -210,6 +221,9 @@
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
+/* Global HWPARAMS6 Register */
+#define DWC3_GHWPARAMS6_EN_FPGA (1 << 7)
+
/* Device Configuration Register */
#define DWC3_DCFG_DEVADDR(addr) ((addr) << 3)
#define DWC3_DCFG_DEVADDR_MASK DWC3_DCFG_DEVADDR(0x7f)
@@ -243,16 +257,19 @@
#define DWC3_DCTL_TRGTULST_SS_INACT (DWC3_DCTL_TRGTULST(6))
/* These apply for core versions 1.94a and later */
-#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
-#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
-#define DWC3_DCTL_CRS (1 << 17)
-#define DWC3_DCTL_CSS (1 << 16)
+#define DWC3_DCTL_LPM_ERRATA_MASK DWC3_DCTL_LPM_ERRATA(0xf)
+#define DWC3_DCTL_LPM_ERRATA(n) ((n) << 20)
+
+#define DWC3_DCTL_KEEP_CONNECT (1 << 19)
+#define DWC3_DCTL_L1_HIBER_EN (1 << 18)
+#define DWC3_DCTL_CRS (1 << 17)
+#define DWC3_DCTL_CSS (1 << 16)
-#define DWC3_DCTL_INITU2ENA (1 << 12)
-#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
-#define DWC3_DCTL_INITU1ENA (1 << 10)
-#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
-#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
+#define DWC3_DCTL_INITU2ENA (1 << 12)
+#define DWC3_DCTL_ACCEPTU2ENA (1 << 11)
+#define DWC3_DCTL_INITU1ENA (1 << 10)
+#define DWC3_DCTL_ACCEPTU1ENA (1 << 9)
+#define DWC3_DCTL_TSTCTRL_MASK (0xf << 1)
#define DWC3_DCTL_ULSTCHNGREQ_MASK (0x0f << 5)
#define DWC3_DCTL_ULSTCHNGREQ(n) (((n) << 5) & DWC3_DCTL_ULSTCHNGREQ_MASK)
@@ -657,17 +674,41 @@ struct dwc3_scratchpad_array {
* @regset: debugfs pointer to regdump file
* @test_mode: true when we're entering a USB test mode
* @test_mode_nr: test feature selector
+ * @lpm_nyet_threshold: LPM NYET response threshold
+ * @hird_threshold: HIRD threshold
* @delayed_status: true when gadget driver asks for delayed status
* @ep0_bounced: true when we used bounce buffer
* @ep0_expect_in: true when we expect a DATA IN transfer
* @has_hibernation: true when dwc3 was configured with Hibernation
+ * @has_lpm_erratum: true when core was configured with LPM Erratum. Note that
+ * there's now way for software to detect this in runtime.
+ * @is_utmi_l1_suspend: the core asserts output signal
+ * 0 - utmi_sleep_n
+ * 1 - utmi_l1_suspend_n
* @is_selfpowered: true when we are selfpowered
+ * @is_fpga: true when we are using the FPGA board
* @needs_fifo_resize: not all users might want fifo resizing, flag it
* @pullups_connected: true when Run/Stop bit is set
* @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
* @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
* @start_config_issued: true when StartConfig command has been issued
* @three_stage_setup: set if we perform a three phase setup
+ * @disable_scramble_quirk: set if we enable the disable scramble quirk
+ * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
+ * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
+ * @req_p1p2p3_quirk: set if we enable request p1p2p3 quirk
+ * @del_p1p2p3_quirk: set if we enable delay p1p2p3 quirk
+ * @del_phy_power_chg_quirk: set if we enable delay phy power change quirk
+ * @lfps_filter_quirk: set if we enable LFPS filter quirk
+ * @rx_detect_poll_quirk: set if we enable rx_detect to polling lfps quirk
+ * @dis_u3_susphy_quirk: set if we disable usb3 suspend phy
+ * @dis_u2_susphy_quirk: set if we disable usb2 suspend phy
+ * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
+ * @tx_de_emphasis: Tx de-emphasis value
+ * 0 - -6dB de-emphasis
+ * 1 - -3.5dB de-emphasis
+ * 2 - No de-emphasis
+ * 3 - Reserved
*/
struct dwc3 {
struct usb_ctrlrequest *ctrl_req;
@@ -759,18 +800,37 @@ struct dwc3 {
u8 test_mode;
u8 test_mode_nr;
+ u8 lpm_nyet_threshold;
+ u8 hird_threshold;
unsigned delayed_status:1;
unsigned ep0_bounced:1;
unsigned ep0_expect_in:1;
unsigned has_hibernation:1;
+ unsigned has_lpm_erratum:1;
+ unsigned is_utmi_l1_suspend:1;
unsigned is_selfpowered:1;
+ unsigned is_fpga:1;
unsigned needs_fifo_resize:1;
unsigned pullups_connected:1;
unsigned resize_fifos:1;
unsigned setup_packet_pending:1;
unsigned start_config_issued:1;
unsigned three_stage_setup:1;
+
+ unsigned disable_scramble_quirk:1;
+ unsigned u2exit_lfps_quirk:1;
+ unsigned u2ss_inp3_quirk:1;
+ unsigned req_p1p2p3_quirk:1;
+ unsigned del_p1p2p3_quirk:1;
+ unsigned del_phy_power_chg_quirk:1;
+ unsigned lfps_filter_quirk:1;
+ unsigned rx_detect_poll_quirk:1;
+ unsigned dis_u3_susphy_quirk:1;
+ unsigned dis_u2_susphy_quirk:1;
+
+ unsigned tx_de_emphasis_quirk:1;
+ unsigned tx_de_emphasis:2;
};
/* -------------------------------------------------------------------------- */
@@ -964,20 +1024,9 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
/* power management interface */
#if !IS_ENABLED(CONFIG_USB_DWC3_HOST)
-int dwc3_gadget_prepare(struct dwc3 *dwc);
-void dwc3_gadget_complete(struct dwc3 *dwc);
int dwc3_gadget_suspend(struct dwc3 *dwc);
int dwc3_gadget_resume(struct dwc3 *dwc);
#else
-static inline int dwc3_gadget_prepare(struct dwc3 *dwc)
-{
- return 0;
-}
-
-static inline void dwc3_gadget_complete(struct dwc3 *dwc)
-{
-}
-
static inline int dwc3_gadget_suspend(struct dwc3 *dwc)
{
return 0;
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 3951a65fea04..7bd0a95b2815 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
-#include <linux/platform_data/dwc3-exynos.h>
#include <linux/dma-mapping.h>
#include <linux/clk.h>
#include <linux/usb/otg.h>
@@ -35,6 +34,9 @@ struct dwc3_exynos {
struct device *dev;
struct clk *clk;
+ struct clk *susp_clk;
+ struct clk *axius_clk;
+
struct regulator *vdd33;
struct regulator *vdd10;
};
@@ -106,7 +108,6 @@ static int dwc3_exynos_remove_child(struct device *dev, void *unused)
static int dwc3_exynos_probe(struct platform_device *pdev)
{
struct dwc3_exynos *exynos;
- struct clk *clk;
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
@@ -133,16 +134,32 @@ static int dwc3_exynos_probe(struct platform_device *pdev)
return ret;
}
- clk = devm_clk_get(dev, "usbdrd30");
- if (IS_ERR(clk)) {
+ exynos->dev = dev;
+
+ exynos->clk = devm_clk_get(dev, "usbdrd30");
+ if (IS_ERR(exynos->clk)) {
dev_err(dev, "couldn't get clock\n");
return -EINVAL;
}
+ clk_prepare_enable(exynos->clk);
- exynos->dev = dev;
- exynos->clk = clk;
+ exynos->susp_clk = devm_clk_get(dev, "usbdrd30_susp_clk");
+ if (IS_ERR(exynos->susp_clk)) {
+ dev_dbg(dev, "no suspend clk specified\n");
+ exynos->susp_clk = NULL;
+ }
+ clk_prepare_enable(exynos->susp_clk);
- clk_prepare_enable(exynos->clk);
+ if (of_device_is_compatible(node, "samsung,exynos7-dwusb3")) {
+ exynos->axius_clk = devm_clk_get(dev, "usbdrd30_axius_clk");
+ if (IS_ERR(exynos->axius_clk)) {
+ dev_err(dev, "no AXI UpScaler clk specified\n");
+ return -ENODEV;
+ }
+ clk_prepare_enable(exynos->axius_clk);
+ } else {
+ exynos->axius_clk = NULL;
+ }
exynos->vdd33 = devm_regulator_get(dev, "vdd33");
if (IS_ERR(exynos->vdd33)) {
@@ -185,7 +202,9 @@ err4:
err3:
regulator_disable(exynos->vdd33);
err2:
- clk_disable_unprepare(clk);
+ clk_disable_unprepare(exynos->axius_clk);
+ clk_disable_unprepare(exynos->susp_clk);
+ clk_disable_unprepare(exynos->clk);
return ret;
}
@@ -197,6 +216,8 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
platform_device_unregister(exynos->usb2_phy);
platform_device_unregister(exynos->usb3_phy);
+ clk_disable_unprepare(exynos->axius_clk);
+ clk_disable_unprepare(exynos->susp_clk);
clk_disable_unprepare(exynos->clk);
regulator_disable(exynos->vdd33);
@@ -205,19 +226,19 @@ static int dwc3_exynos_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_OF
static const struct of_device_id exynos_dwc3_match[] = {
{ .compatible = "samsung,exynos5250-dwusb3" },
+ { .compatible = "samsung,exynos7-dwusb3" },
{},
};
MODULE_DEVICE_TABLE(of, exynos_dwc3_match);
-#endif
#ifdef CONFIG_PM_SLEEP
static int dwc3_exynos_suspend(struct device *dev)
{
struct dwc3_exynos *exynos = dev_get_drvdata(dev);
+ clk_disable(exynos->axius_clk);
clk_disable(exynos->clk);
regulator_disable(exynos->vdd33);
@@ -243,6 +264,7 @@ static int dwc3_exynos_resume(struct device *dev)
}
clk_enable(exynos->clk);
+ clk_enable(exynos->axius_clk);
/* runtime set active to reflect active state. */
pm_runtime_disable(dev);
@@ -266,7 +288,7 @@ static struct platform_driver dwc3_exynos_driver = {
.remove = dwc3_exynos_remove,
.driver = {
.name = "exynos-dwc3",
- .of_match_table = of_match_ptr(exynos_dwc3_match),
+ .of_match_table = exynos_dwc3_match,
.pm = DEV_PM_OPS,
},
};
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
index dd8d2df6db31..fe3b9335a74e 100644
--- a/drivers/usb/dwc3/dwc3-keystone.c
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -123,6 +123,7 @@ static int kdwc3_probe(struct platform_device *pdev)
irq = platform_get_irq(pdev, 0);
if (irq < 0) {
dev_err(&pdev->dev, "missing irq\n");
+ error = irq;
goto err_irq;
}
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index a0aa9f3da441..172d64e585b6 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -593,27 +593,12 @@ static const struct of_device_id of_dwc3_match[] = {
MODULE_DEVICE_TABLE(of, of_dwc3_match);
#ifdef CONFIG_PM_SLEEP
-static int dwc3_omap_prepare(struct device *dev)
-{
- struct dwc3_omap *omap = dev_get_drvdata(dev);
-
- dwc3_omap_disable_irqs(omap);
-
- return 0;
-}
-
-static void dwc3_omap_complete(struct device *dev)
-{
- struct dwc3_omap *omap = dev_get_drvdata(dev);
-
- dwc3_omap_enable_irqs(omap);
-}
-
static int dwc3_omap_suspend(struct device *dev)
{
struct dwc3_omap *omap = dev_get_drvdata(dev);
omap->utmi_otg_status = dwc3_omap_read_utmi_status(omap);
+ dwc3_omap_disable_irqs(omap);
return 0;
}
@@ -623,6 +608,7 @@ static int dwc3_omap_resume(struct device *dev)
struct dwc3_omap *omap = dev_get_drvdata(dev);
dwc3_omap_write_utmi_status(omap, omap->utmi_otg_status);
+ dwc3_omap_enable_irqs(omap);
pm_runtime_disable(dev);
pm_runtime_set_active(dev);
@@ -632,8 +618,6 @@ static int dwc3_omap_resume(struct device *dev)
}
static const struct dev_pm_ops dwc3_omap_dev_pm_ops = {
- .prepare = dwc3_omap_prepare,
- .complete = dwc3_omap_complete,
SET_SYSTEM_SLEEP_PM_OPS(dwc3_omap_suspend, dwc3_omap_resume)
};
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index a36cf66302fb..7c4faf738747 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -25,6 +25,8 @@
#include <linux/usb/otg.h>
#include <linux/usb/usb_phy_generic.h>
+#include "platform_data.h"
+
/* FIXME define these in <linux/pci_ids.h> */
#define PCI_VENDOR_ID_SYNOPSYS 0x16c3
#define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd
@@ -102,6 +104,9 @@ static int dwc3_pci_probe(struct pci_dev *pci,
struct dwc3_pci *glue;
int ret;
struct device *dev = &pci->dev;
+ struct dwc3_platform_data dwc3_pdata;
+
+ memset(&dwc3_pdata, 0x00, sizeof(dwc3_pdata));
glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
if (!glue)
@@ -140,6 +145,31 @@ static int dwc3_pci_probe(struct pci_dev *pci,
res[1].name = "dwc_usb3";
res[1].flags = IORESOURCE_IRQ;
+ if (pci->vendor == PCI_VENDOR_ID_AMD &&
+ pci->device == PCI_DEVICE_ID_AMD_NL_USB) {
+ dwc3_pdata.has_lpm_erratum = true;
+ dwc3_pdata.lpm_nyet_threshold = 0xf;
+
+ dwc3_pdata.u2exit_lfps_quirk = true;
+ dwc3_pdata.u2ss_inp3_quirk = true;
+ dwc3_pdata.req_p1p2p3_quirk = true;
+ dwc3_pdata.del_p1p2p3_quirk = true;
+ dwc3_pdata.del_phy_power_chg_quirk = true;
+ dwc3_pdata.lfps_filter_quirk = true;
+ dwc3_pdata.rx_detect_poll_quirk = true;
+
+ dwc3_pdata.tx_de_emphasis_quirk = true;
+ dwc3_pdata.tx_de_emphasis = 1;
+
+ /*
+ * FIXME these quirks should be removed when AMD NL
+ * taps out
+ */
+ dwc3_pdata.disable_scramble_quirk = true;
+ dwc3_pdata.dis_u3_susphy_quirk = true;
+ dwc3_pdata.dis_u2_susphy_quirk = true;
+ }
+
ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
if (ret) {
dev_err(dev, "couldn't add resources to dwc3 device\n");
@@ -148,6 +178,10 @@ static int dwc3_pci_probe(struct pci_dev *pci,
pci_set_drvdata(pci, glue);
+ ret = platform_device_add_data(dwc3, &dwc3_pdata, sizeof(dwc3_pdata));
+ if (ret)
+ goto err3;
+
dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
dwc3->dev.dma_mask = dev->dma_mask;
@@ -185,6 +219,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BSW), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_BYT), },
{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_MRFLD), },
+ { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_NL_USB), },
{ } /* Terminating Entry */
};
MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
diff --git a/drivers/usb/dwc3/dwc3-st.c b/drivers/usb/dwc3/dwc3-st.c
index c7602b5362ad..4a1a543deeda 100644
--- a/drivers/usb/dwc3/dwc3-st.c
+++ b/drivers/usb/dwc3/dwc3-st.c
@@ -243,7 +243,7 @@ static int st_dwc3_probe(struct platform_device *pdev)
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);
+ ret = PTR_ERR(dwc3_data->rstc_rst);
goto undo_powerdown;
}
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
index 711b23019d54..baeedbd67f77 100644
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -86,6 +86,8 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma,
params.param0 = upper_32_bits(dwc->ep0_trb_addr);
params.param1 = lower_32_bits(dwc->ep0_trb_addr);
+ trace_dwc3_prepare_trb(dep, trb);
+
ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
DWC3_DEPCMD_STARTTRANSFER, &params);
if (ret < 0) {
@@ -441,7 +443,6 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
case USB_DEVICE_LTM_ENABLE:
return -EINVAL;
- break;
case USB_DEVICE_TEST_MODE:
if ((wIndex & 0xff) != 0)
@@ -550,7 +551,6 @@ static int dwc3_ep0_set_config(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (state) {
case USB_STATE_DEFAULT:
return -EINVAL;
- break;
case USB_STATE_ADDRESS:
ret = dwc3_ep0_delegate_req(dwc, ctrl);
@@ -700,35 +700,35 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
switch (ctrl->bRequest) {
case USB_REQ_GET_STATUS:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_GET_STATUS");
ret = dwc3_ep0_handle_status(dwc, ctrl);
break;
case USB_REQ_CLEAR_FEATURE:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_CLEAR_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 0);
break;
case USB_REQ_SET_FEATURE:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_FEATURE");
ret = dwc3_ep0_handle_feature(dwc, ctrl, 1);
break;
case USB_REQ_SET_ADDRESS:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ADDRESS");
ret = dwc3_ep0_set_address(dwc, ctrl);
break;
case USB_REQ_SET_CONFIGURATION:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_CONFIGURATION");
ret = dwc3_ep0_set_config(dwc, ctrl);
break;
case USB_REQ_SET_SEL:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_SEL");
ret = dwc3_ep0_set_sel(dwc, ctrl);
break;
case USB_REQ_SET_ISOCH_DELAY:
- dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY\n");
+ dwc3_trace(trace_dwc3_ep0, "USB_REQ_SET_ISOCH_DELAY");
ret = dwc3_ep0_set_isoch_delay(dwc, ctrl);
break;
default:
- dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver\n");
+ dwc3_trace(trace_dwc3_ep0, "Forwarding to gadget driver");
ret = dwc3_ep0_delegate_req(dwc, ctrl);
break;
}
@@ -791,6 +791,8 @@ static void dwc3_ep0_complete_data(struct dwc3 *dwc,
trb = dwc->ep0_trb;
+ trace_dwc3_complete_trb(ep0, trb);
+
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING) {
dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
@@ -855,6 +857,8 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
dep = dwc->eps[0];
trb = dwc->ep0_trb;
+ trace_dwc3_complete_trb(dep, trb);
+
if (!list_empty(&dep->request_list)) {
r = next_request(&dep->request_list);
@@ -875,7 +879,7 @@ static void dwc3_ep0_complete_status(struct dwc3 *dwc,
status = DWC3_TRB_SIZE_TRBSTS(trb->size);
if (status == DWC3_TRBSTS_SETUP_PENDING)
- dwc3_trace(trace_dwc3_ep0, "Setup Pending received\n");
+ dwc3_trace(trace_dwc3_ep0, "Setup Pending received");
dwc->ep0state = EP0_SETUP_PHASE;
dwc3_ep0_out_start(dwc);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 546ea5431b8c..f03b136ecfce 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1140,8 +1140,14 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
if (!dep->endpoint.desc) {
dev_dbg(dwc->dev, "trying to queue request %p to disabled %s\n",
request, ep->name);
- spin_unlock_irqrestore(&dwc->lock, flags);
- return -ESHUTDOWN;
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
+ if (WARN(req->dep != dep, "request %p belongs to '%s'\n",
+ request, req->dep->name)) {
+ ret = -EINVAL;
+ goto out;
}
dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
@@ -1149,6 +1155,8 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
trace_dwc3_ep_queue(req);
ret = __dwc3_gadget_ep_queue(dep, req);
+
+out:
spin_unlock_irqrestore(&dwc->lock, flags);
return ret;
@@ -1622,8 +1630,7 @@ err0:
return ret;
}
-static int dwc3_gadget_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int dwc3_gadget_stop(struct usb_gadget *g)
{
struct dwc3 *dwc = gadget_to_dwc(g);
unsigned long flags;
@@ -2034,6 +2041,17 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
if (dwc->gadget_driver && dwc->gadget_driver->resume) {
spin_unlock(&dwc->lock);
dwc->gadget_driver->resume(&dwc->gadget);
+ }
+}
+
+static void dwc3_reset_gadget(struct dwc3 *dwc)
+{
+ if (!dwc->gadget_driver)
+ return;
+
+ if (dwc->gadget.speed != USB_SPEED_UNKNOWN) {
+ spin_unlock(&dwc->lock);
+ usb_gadget_udc_reset(&dwc->gadget, dwc->gadget_driver);
spin_lock(&dwc->lock);
}
}
@@ -2140,6 +2158,7 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc)
dwc->gadget.speed = USB_SPEED_UNKNOWN;
dwc->setup_packet_pending = false;
+ usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED);
}
static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
@@ -2177,11 +2196,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc3_gadget_disconnect_interrupt(dwc);
}
- /* after reset -> Default State */
- usb_gadget_set_state(&dwc->gadget, USB_STATE_DEFAULT);
-
- if (dwc->gadget.speed != USB_SPEED_UNKNOWN)
- dwc3_disconnect_gadget(dwc);
+ dwc3_reset_gadget(dwc);
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~DWC3_DCTL_TSTCTRL_MASK;
@@ -2287,11 +2302,20 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
reg = dwc3_readl(dwc->regs, DWC3_DCTL);
reg &= ~(DWC3_DCTL_HIRD_THRES_MASK | DWC3_DCTL_L1_HIBER_EN);
+ reg |= DWC3_DCTL_HIRD_THRES(dwc->hird_threshold);
+
/*
- * TODO: This should be configurable. For now using
- * maximum allowed HIRD threshold value of 0b1100
+ * When dwc3 revisions >= 2.40a, LPM Erratum is enabled and
+ * DCFG.LPMCap is set, core responses with an ACK and the
+ * BESL value in the LPM token is less than or equal to LPM
+ * NYET threshold.
*/
- reg |= DWC3_DCTL_HIRD_THRES(12);
+ WARN_ONCE(dwc->revision < DWC3_REVISION_240A
+ && dwc->has_lpm_erratum,
+ "LPM Erratum not available on dwc3 revisisions < 2.40a\n");
+
+ if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
+ reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold);
dwc3_writel(dwc->regs, DWC3_DCTL, reg);
} else {
@@ -2744,26 +2768,13 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
dwc->ctrl_req, dwc->ctrl_req_addr);
}
-int dwc3_gadget_prepare(struct dwc3 *dwc)
+int dwc3_gadget_suspend(struct dwc3 *dwc)
{
if (dwc->pullups_connected) {
dwc3_gadget_disable_irq(dwc);
dwc3_gadget_run_stop(dwc, true, true);
}
- return 0;
-}
-
-void dwc3_gadget_complete(struct dwc3 *dwc)
-{
- if (dwc->pullups_connected) {
- dwc3_gadget_enable_irq(dwc);
- dwc3_gadget_run_stop(dwc, true, false);
- }
-}
-
-int dwc3_gadget_suspend(struct dwc3 *dwc)
-{
__dwc3_gadget_ep_disable(dwc->eps[0]);
__dwc3_gadget_ep_disable(dwc->eps[1]);
@@ -2798,6 +2809,11 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg);
+ if (dwc->pullups_connected) {
+ dwc3_gadget_enable_irq(dwc);
+ dwc3_gadget_run_stop(dwc, true, false);
+ }
+
return 0;
err1:
diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h
index 7db34f00b89a..a3a3b6d5668c 100644
--- a/drivers/usb/dwc3/platform_data.h
+++ b/drivers/usb/dwc3/platform_data.h
@@ -24,4 +24,24 @@ struct dwc3_platform_data {
enum usb_device_speed maximum_speed;
enum usb_dr_mode dr_mode;
bool tx_fifo_resize;
+
+ unsigned is_utmi_l1_suspend:1;
+ u8 hird_threshold;
+
+ u8 lpm_nyet_threshold;
+
+ unsigned disable_scramble_quirk:1;
+ unsigned has_lpm_erratum:1;
+ unsigned u2exit_lfps_quirk:1;
+ unsigned u2ss_inp3_quirk:1;
+ unsigned req_p1p2p3_quirk:1;
+ unsigned del_p1p2p3_quirk:1;
+ unsigned del_phy_power_chg_quirk:1;
+ unsigned lfps_filter_quirk:1;
+ unsigned rx_detect_poll_quirk:1;
+ unsigned dis_u3_susphy_quirk:1;
+ unsigned dis_u2_susphy_quirk:1;
+
+ unsigned tx_de_emphasis_quirk:1;
+ unsigned tx_de_emphasis:2;
};
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index 60b0f41eafc4..9fc20b33dd8e 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -61,7 +61,7 @@ DECLARE_EVENT_CLASS(dwc3_log_event,
TP_fast_assign(
__entry->event = event;
),
- TP_printk("event %08x\n", __entry->event)
+ TP_printk("event %08x", __entry->event)
);
DEFINE_EVENT(dwc3_log_event, dwc3_event,
@@ -157,7 +157,7 @@ DECLARE_EVENT_CLASS(dwc3_log_generic_cmd,
__entry->cmd = cmd;
__entry->param = param;
),
- TP_printk("cmd '%s' [%d] param %08x\n",
+ TP_printk("cmd '%s' [%d] param %08x",
dwc3_gadget_generic_cmd_string(__entry->cmd),
__entry->cmd, __entry->param
)
@@ -175,17 +175,21 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd,
TP_STRUCT__entry(
__dynamic_array(char, name, DWC3_MSG_MAX)
__field(unsigned int, cmd)
- __field(struct dwc3_gadget_ep_cmd_params *, params)
+ __field(u32, param0)
+ __field(u32, param1)
+ __field(u32, param2)
),
TP_fast_assign(
snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name);
__entry->cmd = cmd;
- __entry->params = params;
+ __entry->param0 = params->param0;
+ __entry->param1 = params->param1;
+ __entry->param2 = params->param2;
),
- TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x\n",
+ TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x",
__get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd),
- __entry->cmd, __entry->params->param0,
- __entry->params->param1, __entry->params->param2
+ __entry->cmd, __entry->param0,
+ __entry->param1, __entry->param2
)
);
@@ -214,7 +218,7 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
__entry->size = trb->size;
__entry->ctrl = trb->ctrl;
),
- TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x\n",
+ TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x",
__get_str(name), __entry->trb, __entry->bph, __entry->bpl,
__entry->size, __entry->ctrl
)
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index c4880fc0d86e..747ef53bda14 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -190,6 +190,12 @@ config USB_F_UAC2
config USB_F_UVC
tristate
+config USB_F_MIDI
+ tristate
+
+config USB_F_HID
+ tristate
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
@@ -362,6 +368,61 @@ config USB_CONFIGFS_F_FS
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
+config USB_CONFIGFS_F_UAC1
+ boolean "Audio Class 1.0"
+ depends on USB_CONFIGFS
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_PCM
+ select USB_F_UAC1
+ help
+ This Audio function implements 1 AudioControl interface,
+ 1 AudioStreaming Interface each for USB-OUT and USB-IN.
+ This driver requires a real Audio codec to be present
+ on the device.
+
+config USB_CONFIGFS_F_UAC2
+ boolean "Audio Class 2.0"
+ depends on USB_CONFIGFS
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_PCM
+ select USB_F_UAC2
+ help
+ This Audio function is compatible with USB Audio Class
+ specification 2.0. It implements 1 AudioControl interface,
+ 1 AudioStreaming Interface each for USB-OUT and USB-IN.
+ This driver doesn't expect any real Audio codec to be present
+ on the device - the audio streams are simply sinked to and
+ sourced from a virtual ALSA sound card created. The user-space
+ application may choose to do whatever it wants with the data
+ received from the USB Host and choose to provide whatever it
+ wants as audio data to the USB Host.
+
+config USB_CONFIGFS_F_MIDI
+ boolean "MIDI function"
+ depends on USB_CONFIGFS
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_RAWMIDI
+ select USB_F_MIDI
+ help
+ The MIDI Function acts as a USB Audio device, with one MIDI
+ input and one MIDI output. These MIDI jacks appear as
+ a sound "card" in the ALSA sound system. Other MIDI
+ connections can then be made on the gadget system, using
+ ALSA's aconnect utility etc.
+
+config USB_CONFIGFS_F_HID
+ boolean "HID function"
+ depends on USB_CONFIGFS
+ select USB_F_HID
+ help
+ The HID function driver provides generic emulation of USB
+ Human Interface Devices (HID).
+
+ For more information, see Documentation/usb/gadget_hid.txt.
+
source "drivers/usb/gadget/legacy/Kconfig"
endchoice
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index f6a51fddd5b5..617835348569 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1246,10 +1246,49 @@ EXPORT_SYMBOL_GPL(usb_string_ids_n);
static void composite_setup_complete(struct usb_ep *ep, struct usb_request *req)
{
+ struct usb_composite_dev *cdev;
+
if (req->status || req->actual != req->length)
DBG((struct usb_composite_dev *) ep->driver_data,
"setup complete --> %d, %d/%d\n",
req->status, req->actual, req->length);
+
+ /*
+ * REVIST The same ep0 requests are shared with function drivers
+ * so they don't have to maintain the same ->complete() stubs.
+ *
+ * Because of that, we need to check for the validity of ->context
+ * here, even though we know we've set it to something useful.
+ */
+ if (!req->context)
+ return;
+
+ cdev = req->context;
+
+ if (cdev->req == req)
+ cdev->setup_pending = false;
+ else if (cdev->os_desc_req == req)
+ cdev->os_desc_pending = false;
+ else
+ WARN(1, "unknown request %p\n", req);
+}
+
+static int composite_ep0_queue(struct usb_composite_dev *cdev,
+ struct usb_request *req, gfp_t gfp_flags)
+{
+ int ret;
+
+ ret = usb_ep_queue(cdev->gadget->ep0, req, gfp_flags);
+ if (ret == 0) {
+ if (cdev->req == req)
+ cdev->setup_pending = true;
+ else if (cdev->os_desc_req == req)
+ cdev->os_desc_pending = true;
+ else
+ WARN(1, "unknown request %p\n", req);
+ }
+
+ return ret;
}
static int count_ext_compat(struct usb_configuration *c)
@@ -1428,6 +1467,7 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
* when we delegate to it.
*/
req->zero = 0;
+ req->context = cdev;
req->complete = composite_setup_complete;
req->length = 0;
gadget->ep0->driver_data = cdev;
@@ -1624,6 +1664,7 @@ unknown:
int count = 0;
req = cdev->os_desc_req;
+ req->context = cdev;
req->complete = composite_setup_complete;
buf = req->buf;
os_desc_cfg = cdev->os_desc_config;
@@ -1686,8 +1727,9 @@ unknown:
break;
}
req->length = value;
+ req->context = cdev;
req->zero = value < w_length;
- value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
@@ -1757,8 +1799,9 @@ unknown:
/* respond with data transfer before status phase? */
if (value >= 0 && value != USB_GADGET_DELAYED_STATUS) {
req->length = value;
+ req->context = cdev;
req->zero = value < w_length;
- value = usb_ep_queue(gadget->ep0, req, GFP_ATOMIC);
+ value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
@@ -1893,6 +1936,7 @@ int composite_dev_prepare(struct usb_composite_driver *composite,
goto fail_dev;
cdev->req->complete = composite_setup_complete;
+ cdev->req->context = cdev;
gadget->ep0->driver_data = cdev;
cdev->driver = composite;
@@ -1937,6 +1981,7 @@ int composite_os_desc_req_prepare(struct usb_composite_dev *cdev,
kfree(cdev->os_desc_req);
goto end;
}
+ cdev->os_desc_req->context = cdev;
cdev->os_desc_req->complete = composite_setup_complete;
end:
return ret;
@@ -1951,10 +1996,16 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
kfree(uc);
}
if (cdev->os_desc_req) {
+ if (cdev->os_desc_pending)
+ usb_ep_dequeue(cdev->gadget->ep0, cdev->os_desc_req);
+
kfree(cdev->os_desc_req->buf);
usb_ep_free_request(cdev->gadget->ep0, cdev->os_desc_req);
}
if (cdev->req) {
+ if (cdev->setup_pending)
+ usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
+
kfree(cdev->req->buf);
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
}
@@ -2013,8 +2064,7 @@ fail:
/*-------------------------------------------------------------------------*/
-static void
-composite_suspend(struct usb_gadget *gadget)
+void composite_suspend(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
@@ -2037,8 +2087,7 @@ composite_suspend(struct usb_gadget *gadget)
usb_gadget_vbus_draw(gadget, 2);
}
-static void
-composite_resume(struct usb_gadget *gadget)
+void composite_resume(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
@@ -2158,7 +2207,8 @@ void usb_composite_setup_continue(struct usb_composite_dev *cdev)
} else if (--cdev->delayed_status == 0) {
DBG(cdev, "%s: Completing delayed status\n", __func__);
req->length = 0;
- value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+ req->context = cdev;
+ value = composite_ep0_queue(cdev, req, GFP_ATOMIC);
if (value < 0) {
DBG(cdev, "ep_queue --> %d\n", value);
req->status = 0;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 34034333f7f6..75648145dc1b 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -271,7 +271,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct gadget_info *gi,
ret = -EBUSY;
goto err;
}
- ret = udc_attach_driver(name, &gi->composite.gadget_driver);
+ ret = usb_udc_attach_driver(name, &gi->composite.gadget_driver);
if (ret)
goto err;
gi->udc_name = name;
@@ -1453,6 +1453,9 @@ static const struct usb_gadget_driver configfs_driver_template = {
.reset = composite_disconnect,
.disconnect = composite_disconnect,
+ .suspend = composite_suspend,
+ .resume = composite_resume,
+
.max_speed = USB_SPEED_SUPER,
.driver = {
.owner = THIS_MODULE,
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
index 90701aa5a826..dd68091d92f0 100644
--- a/drivers/usb/gadget/function/Makefile
+++ b/drivers/usb/gadget/function/Makefile
@@ -38,3 +38,7 @@ usb_f_uac2-y := f_uac2.o
obj-$(CONFIG_USB_F_UAC2) += usb_f_uac2.o
usb_f_uvc-y := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
obj-$(CONFIG_USB_F_UVC) += usb_f_uvc.o
+usb_f_midi-y := f_midi.o
+obj-$(CONFIG_USB_F_MIDI) += usb_f_midi.o
+usb_f_hid-y := f_hid.o
+obj-$(CONFIG_USB_F_HID) += usb_f_hid.o
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index 59ab62c92b66..488ac66aae9e 100644
--- a/drivers/usb/gadget/function/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
@@ -12,6 +12,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/hid.h>
+#include <linux/idr.h>
#include <linux/cdev.h>
#include <linux/mutex.h>
#include <linux/poll.h>
@@ -21,9 +22,14 @@
#include <linux/usb/g_hid.h>
#include "u_f.h"
+#include "u_hid.h"
+
+#define HIDG_MINORS 4
static int major, minors;
static struct class *hidg_class;
+static DEFINE_IDA(hidg_ida);
+static DEFINE_MUTEX(hidg_ida_lock); /* protects access to hidg_ida */
/*-------------------------------------------------------------------------*/
/* HID gadget struct */
@@ -161,6 +167,26 @@ static struct usb_descriptor_header *hidg_fs_descriptors[] = {
};
/*-------------------------------------------------------------------------*/
+/* Strings */
+
+#define CT_FUNC_HID_IDX 0
+
+static struct usb_string ct_func_string_defs[] = {
+ [CT_FUNC_HID_IDX].s = "HID Interface",
+ {}, /* end of list */
+};
+
+static struct usb_gadget_strings ct_func_string_table = {
+ .language = 0x0409, /* en-US */
+ .strings = ct_func_string_defs,
+};
+
+static struct usb_gadget_strings *ct_func_strings[] = {
+ &ct_func_string_table,
+ NULL,
+};
+
+/*-------------------------------------------------------------------------*/
/* Char Device */
static ssize_t f_hidg_read(struct file *file, char __user *buffer,
@@ -552,13 +578,22 @@ const struct file_operations f_hidg_fops = {
.llseek = noop_llseek,
};
-static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
+static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_ep *ep;
struct f_hidg *hidg = func_to_hidg(f);
+ struct usb_string *us;
+ struct device *device;
int status;
dev_t dev;
+ /* maybe allocate device-global string IDs, and patch descriptors */
+ us = usb_gstrings_attach(c->cdev, ct_func_strings,
+ ARRAY_SIZE(ct_func_string_defs));
+ if (IS_ERR(us))
+ return PTR_ERR(us);
+ hidg_interface_desc.iInterface = us[CT_FUNC_HID_IDX].id;
+
/* allocate instance-specific interface IDs, and patch descriptors */
status = usb_interface_id(c, f);
if (status < 0)
@@ -623,10 +658,16 @@ static int __init hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (status)
goto fail_free_descs;
- device_create(hidg_class, NULL, dev, NULL, "%s%d", "hidg", hidg->minor);
+ device = device_create(hidg_class, NULL, dev, NULL,
+ "%s%d", "hidg", hidg->minor);
+ if (IS_ERR(device)) {
+ status = PTR_ERR(device);
+ goto del;
+ }
return 0;
-
+del:
+ cdev_del(&hidg->cdev);
fail_free_descs:
usb_free_all_descriptors(f);
fail:
@@ -640,116 +681,313 @@ fail:
return status;
}
-static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
+static inline int hidg_get_minor(void)
{
- struct f_hidg *hidg = func_to_hidg(f);
+ int ret;
- device_destroy(hidg_class, MKDEV(major, hidg->minor));
- cdev_del(&hidg->cdev);
+ ret = ida_simple_get(&hidg_ida, 0, 0, GFP_KERNEL);
- /* disable/free request and end point */
- usb_ep_disable(hidg->in_ep);
- usb_ep_dequeue(hidg->in_ep, hidg->req);
- kfree(hidg->req->buf);
- usb_ep_free_request(hidg->in_ep, hidg->req);
-
- usb_free_all_descriptors(f);
+ return ret;
+}
- kfree(hidg->report_desc);
- kfree(hidg);
+static inline struct f_hid_opts *to_f_hid_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_hid_opts,
+ func_inst.group);
}
-/*-------------------------------------------------------------------------*/
-/* Strings */
+CONFIGFS_ATTR_STRUCT(f_hid_opts);
+CONFIGFS_ATTR_OPS(f_hid_opts);
-#define CT_FUNC_HID_IDX 0
+static void hid_attr_release(struct config_item *item)
+{
+ struct f_hid_opts *opts = to_f_hid_opts(item);
-static struct usb_string ct_func_string_defs[] = {
- [CT_FUNC_HID_IDX].s = "HID Interface",
- {}, /* end of list */
-};
+ usb_put_function_instance(&opts->func_inst);
+}
-static struct usb_gadget_strings ct_func_string_table = {
- .language = 0x0409, /* en-US */
- .strings = ct_func_string_defs,
+static struct configfs_item_operations hidg_item_ops = {
+ .release = hid_attr_release,
+ .show_attribute = f_hid_opts_attr_show,
+ .store_attribute = f_hid_opts_attr_store,
};
-static struct usb_gadget_strings *ct_func_strings[] = {
- &ct_func_string_table,
+#define F_HID_OPT(name, prec, limit) \
+static ssize_t f_hid_opts_##name##_show(struct f_hid_opts *opts, char *page)\
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_hid_opts_##name##_store(struct f_hid_opts *opts, \
+ const char *page, size_t len) \
+{ \
+ int ret; \
+ u##prec num; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = kstrtou##prec(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (num > limit) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ opts->name = num; \
+ ret = len; \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_hid_opts_attribute f_hid_opts_##name = \
+ __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_hid_opts_##name##_show,\
+ f_hid_opts_##name##_store)
+
+F_HID_OPT(subclass, 8, 255);
+F_HID_OPT(protocol, 8, 255);
+F_HID_OPT(report_length, 16, 65536);
+
+static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = opts->report_desc_length;
+ memcpy(page, opts->report_desc, opts->report_desc_length);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_hid_opts_report_desc_store(struct f_hid_opts *opts,
+ const char *page, size_t len)
+{
+ int ret = -EBUSY;
+ char *d;
+
+ mutex_lock(&opts->lock);
+
+ if (opts->refcnt)
+ goto end;
+ if (len > PAGE_SIZE) {
+ ret = -ENOSPC;
+ goto end;
+ }
+ d = kmemdup(page, len, GFP_KERNEL);
+ if (!d) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ kfree(opts->report_desc);
+ opts->report_desc = d;
+ opts->report_desc_length = len;
+ opts->report_desc_alloc = true;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_hid_opts_attribute f_hid_opts_report_desc =
+ __CONFIGFS_ATTR(report_desc, S_IRUGO | S_IWUSR,
+ f_hid_opts_report_desc_show,
+ f_hid_opts_report_desc_store);
+
+static struct configfs_attribute *hid_attrs[] = {
+ &f_hid_opts_subclass.attr,
+ &f_hid_opts_protocol.attr,
+ &f_hid_opts_report_length.attr,
+ &f_hid_opts_report_desc.attr,
NULL,
};
-/*-------------------------------------------------------------------------*/
-/* usb_configuration */
+static struct config_item_type hid_func_type = {
+ .ct_item_ops = &hidg_item_ops,
+ .ct_attrs = hid_attrs,
+ .ct_owner = THIS_MODULE,
+};
-int __init hidg_bind_config(struct usb_configuration *c,
- struct hidg_func_descriptor *fdesc, int index)
+static inline void hidg_put_minor(int minor)
{
- struct f_hidg *hidg;
- int status;
+ ida_simple_remove(&hidg_ida, minor);
+}
- if (index >= minors)
- return -ENOENT;
+static void hidg_free_inst(struct usb_function_instance *f)
+{
+ struct f_hid_opts *opts;
- /* maybe allocate device-global string IDs, and patch descriptors */
- if (ct_func_string_defs[CT_FUNC_HID_IDX].id == 0) {
- status = usb_string_id(c->cdev);
- if (status < 0)
- return status;
- ct_func_string_defs[CT_FUNC_HID_IDX].id = status;
- hidg_interface_desc.iInterface = status;
+ opts = container_of(f, struct f_hid_opts, func_inst);
+
+ mutex_lock(&hidg_ida_lock);
+
+ hidg_put_minor(opts->minor);
+ if (idr_is_empty(&hidg_ida.idr))
+ ghid_cleanup();
+
+ mutex_unlock(&hidg_ida_lock);
+
+ if (opts->report_desc_alloc)
+ kfree(opts->report_desc);
+
+ kfree(opts);
+}
+
+static struct usb_function_instance *hidg_alloc_inst(void)
+{
+ struct f_hid_opts *opts;
+ struct usb_function_instance *ret;
+ int status = 0;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = hidg_free_inst;
+ ret = &opts->func_inst;
+
+ mutex_lock(&hidg_ida_lock);
+
+ if (idr_is_empty(&hidg_ida.idr)) {
+ status = ghid_setup(NULL, HIDG_MINORS);
+ if (status) {
+ ret = ERR_PTR(status);
+ kfree(opts);
+ goto unlock;
+ }
+ }
+
+ opts->minor = hidg_get_minor();
+ if (opts->minor < 0) {
+ ret = ERR_PTR(opts->minor);
+ kfree(opts);
+ if (idr_is_empty(&hidg_ida.idr))
+ ghid_cleanup();
+ goto unlock;
}
+ config_group_init_type_name(&opts->func_inst.group, "", &hid_func_type);
+
+unlock:
+ mutex_unlock(&hidg_ida_lock);
+ return ret;
+}
+
+static void hidg_free(struct usb_function *f)
+{
+ struct f_hidg *hidg;
+ struct f_hid_opts *opts;
+
+ hidg = func_to_hidg(f);
+ opts = container_of(f->fi, struct f_hid_opts, func_inst);
+ kfree(hidg->report_desc);
+ kfree(hidg);
+ mutex_lock(&opts->lock);
+ --opts->refcnt;
+ mutex_unlock(&opts->lock);
+}
+
+static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct f_hidg *hidg = func_to_hidg(f);
+
+ device_destroy(hidg_class, MKDEV(major, hidg->minor));
+ cdev_del(&hidg->cdev);
+
+ /* disable/free request and end point */
+ usb_ep_disable(hidg->in_ep);
+ usb_ep_dequeue(hidg->in_ep, hidg->req);
+ kfree(hidg->req->buf);
+ usb_ep_free_request(hidg->in_ep, hidg->req);
+
+ usb_free_all_descriptors(f);
+}
+
+static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
+{
+ struct f_hidg *hidg;
+ struct f_hid_opts *opts;
/* allocate and initialize one new instance */
- hidg = kzalloc(sizeof *hidg, GFP_KERNEL);
+ hidg = kzalloc(sizeof(*hidg), GFP_KERNEL);
if (!hidg)
- return -ENOMEM;
-
- hidg->minor = index;
- hidg->bInterfaceSubClass = fdesc->subclass;
- hidg->bInterfaceProtocol = fdesc->protocol;
- hidg->report_length = fdesc->report_length;
- hidg->report_desc_length = fdesc->report_desc_length;
- hidg->report_desc = kmemdup(fdesc->report_desc,
- fdesc->report_desc_length,
- GFP_KERNEL);
- if (!hidg->report_desc) {
- kfree(hidg);
- return -ENOMEM;
+ return ERR_PTR(-ENOMEM);
+
+ opts = container_of(fi, struct f_hid_opts, func_inst);
+
+ mutex_lock(&opts->lock);
+ ++opts->refcnt;
+
+ hidg->minor = opts->minor;
+ hidg->bInterfaceSubClass = opts->subclass;
+ hidg->bInterfaceProtocol = opts->protocol;
+ hidg->report_length = opts->report_length;
+ hidg->report_desc_length = opts->report_desc_length;
+ if (opts->report_desc) {
+ hidg->report_desc = kmemdup(opts->report_desc,
+ opts->report_desc_length,
+ GFP_KERNEL);
+ if (!hidg->report_desc) {
+ kfree(hidg);
+ mutex_unlock(&opts->lock);
+ return ERR_PTR(-ENOMEM);
+ }
}
+ mutex_unlock(&opts->lock);
+
hidg->func.name = "hid";
- hidg->func.strings = ct_func_strings;
hidg->func.bind = hidg_bind;
hidg->func.unbind = hidg_unbind;
hidg->func.set_alt = hidg_set_alt;
hidg->func.disable = hidg_disable;
hidg->func.setup = hidg_setup;
+ hidg->func.free_func = hidg_free;
/* this could me made configurable at some point */
hidg->qlen = 4;
- status = usb_add_function(c, &hidg->func);
- if (status)
- kfree(hidg);
-
- return status;
+ return &hidg->func;
}
-int __init ghid_setup(struct usb_gadget *g, int count)
+DECLARE_USB_FUNCTION_INIT(hid, hidg_alloc_inst, hidg_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Fabien Chouteau");
+
+int ghid_setup(struct usb_gadget *g, int count)
{
int status;
dev_t dev;
hidg_class = class_create(THIS_MODULE, "hidg");
+ if (IS_ERR(hidg_class)) {
+ status = PTR_ERR(hidg_class);
+ hidg_class = NULL;
+ return status;
+ }
status = alloc_chrdev_region(&dev, 0, count, "hidg");
- if (!status) {
- major = MAJOR(dev);
- minors = count;
+ if (status) {
+ class_destroy(hidg_class);
+ hidg_class = NULL;
+ return status;
}
- return status;
+ major = MAJOR(dev);
+ minors = count;
+
+ return 0;
}
void ghid_cleanup(void)
diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 807b31c0edc3..a90440300735 100644
--- a/drivers/usb/gadget/function/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
@@ -20,6 +20,7 @@
*/
#include <linux/kernel.h>
+#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>
@@ -33,6 +34,7 @@
#include <linux/usb/midi.h>
#include "u_f.h"
+#include "u_midi.h"
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
@@ -99,7 +101,7 @@ DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
/* B.3.1 Standard AC Interface Descriptor */
-static struct usb_interface_descriptor ac_interface_desc __initdata = {
+static struct usb_interface_descriptor ac_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
@@ -110,7 +112,7 @@ static struct usb_interface_descriptor ac_interface_desc __initdata = {
};
/* B.3.2 Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
+static struct uac1_ac_header_descriptor_1 ac_header_desc = {
.bLength = UAC_DT_AC_HEADER_SIZE(1),
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
@@ -121,7 +123,7 @@ static struct uac1_ac_header_descriptor_1 ac_header_desc __initdata = {
};
/* B.4.1 Standard MS Interface Descriptor */
-static struct usb_interface_descriptor ms_interface_desc __initdata = {
+static struct usb_interface_descriptor ms_interface_desc = {
.bLength = USB_DT_INTERFACE_SIZE,
.bDescriptorType = USB_DT_INTERFACE,
/* .bInterfaceNumber = DYNAMIC */
@@ -132,7 +134,7 @@ static struct usb_interface_descriptor ms_interface_desc __initdata = {
};
/* B.4.2 Class-Specific MS Interface Descriptor */
-static struct usb_ms_header_descriptor ms_header_desc __initdata = {
+static struct usb_ms_header_descriptor ms_header_desc = {
.bLength = USB_DT_MS_HEADER_SIZE,
.bDescriptorType = USB_DT_CS_INTERFACE,
.bDescriptorSubtype = USB_MS_HEADER,
@@ -387,29 +389,6 @@ static void f_midi_disable(struct usb_function *f)
usb_ep_disable(midi->out_ep);
}
-static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct usb_composite_dev *cdev = f->config->cdev;
- struct f_midi *midi = func_to_midi(f);
- struct snd_card *card;
-
- DBG(cdev, "unbind\n");
-
- /* just to be sure */
- f_midi_disable(f);
-
- card = midi->card;
- midi->card = NULL;
- if (card)
- snd_card_free(card);
-
- kfree(midi->id);
- midi->id = NULL;
-
- usb_free_all_descriptors(f);
- kfree(midi);
-}
-
static int f_midi_snd_free(struct snd_device *device)
{
return 0;
@@ -654,6 +633,14 @@ static struct snd_rawmidi_ops gmidi_out_ops = {
.trigger = f_midi_out_trigger
};
+static inline void f_midi_unregister_card(struct f_midi *midi)
+{
+ if (midi->card) {
+ snd_card_free(midi->card);
+ midi->card = NULL;
+ }
+}
+
/* register as a sound "card" */
static int f_midi_register_card(struct f_midi *midi)
{
@@ -715,17 +702,13 @@ static int f_midi_register_card(struct f_midi *midi)
return 0;
fail:
- if (midi->card) {
- snd_card_free(midi->card);
- midi->card = NULL;
- }
+ f_midi_unregister_card(midi);
return err;
}
/* MIDI function driver setup/binding */
-static int __init
-f_midi_bind(struct usb_configuration *c, struct usb_function *f)
+static int f_midi_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_descriptor_header **midi_function;
struct usb_midi_in_jack_descriptor jack_in_ext_desc[MAX_PORTS];
@@ -734,15 +717,23 @@ f_midi_bind(struct usb_configuration *c, struct usb_function *f)
struct usb_midi_out_jack_descriptor_1 jack_out_emb_desc[MAX_PORTS];
struct usb_composite_dev *cdev = c->cdev;
struct f_midi *midi = func_to_midi(f);
+ struct usb_string *us;
int status, n, jack = 1, i = 0;
+ midi->gadget = cdev->gadget;
+ tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
+ status = f_midi_register_card(midi);
+ if (status < 0)
+ goto fail_register;
+
/* maybe allocate device-global string ID */
- if (midi_string_defs[0].id == 0) {
- status = usb_string_id(c->cdev);
- if (status < 0)
- goto fail;
- midi_string_defs[0].id = status;
+ us = usb_gstrings_attach(c->cdev, midi_strings,
+ ARRAY_SIZE(midi_string_defs));
+ if (IS_ERR(us)) {
+ status = PTR_ERR(us);
+ goto fail;
}
+ ac_interface_desc.iInterface = us[STRING_FUNC_IDX].id;
/* We have two interfaces, AudioControl and MIDIStreaming */
status = usb_interface_id(c, f);
@@ -892,6 +883,8 @@ fail_f_midi:
kfree(midi_function);
usb_free_descriptors(f->hs_descriptors);
fail:
+ f_midi_unregister_card(midi);
+fail_register:
/* we might as well release our claims on endpoints */
if (midi->out_ep)
midi->out_ep->driver_data = NULL;
@@ -903,42 +896,235 @@ fail:
return status;
}
-/**
- * f_midi_bind_config - add USB MIDI function to a configuration
- * @c: the configuration to supcard the USB audio function
- * @index: the soundcard index to use for the ALSA device creation
- * @id: the soundcard id to use for the ALSA device creation
- * @buflen: the buffer length to use
- * @qlen the number of read requests to pre-allocate
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- */
-int __init f_midi_bind_config(struct usb_configuration *c,
- int index, char *id,
- unsigned int in_ports,
- unsigned int out_ports,
- unsigned int buflen,
- unsigned int qlen)
+static inline struct f_midi_opts *to_f_midi_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_midi_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_midi_opts);
+CONFIGFS_ATTR_OPS(f_midi_opts);
+
+static void midi_attr_release(struct config_item *item)
+{
+ struct f_midi_opts *opts = to_f_midi_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations midi_item_ops = {
+ .release = midi_attr_release,
+ .show_attribute = f_midi_opts_attr_show,
+ .store_attribute = f_midi_opts_attr_store,
+};
+
+#define F_MIDI_OPT(name, test_limit, limit) \
+static ssize_t f_midi_opts_##name##_show(struct f_midi_opts *opts, char *page) \
+{ \
+ int result; \
+ \
+ mutex_lock(&opts->lock); \
+ result = sprintf(page, "%d\n", opts->name); \
+ mutex_unlock(&opts->lock); \
+ \
+ return result; \
+} \
+ \
+static ssize_t f_midi_opts_##name##_store(struct f_midi_opts *opts, \
+ const char *page, size_t len) \
+{ \
+ int ret; \
+ u32 num; \
+ \
+ mutex_lock(&opts->lock); \
+ if (opts->refcnt) { \
+ ret = -EBUSY; \
+ goto end; \
+ } \
+ \
+ ret = kstrtou32(page, 0, &num); \
+ if (ret) \
+ goto end; \
+ \
+ if (test_limit && num > limit) { \
+ ret = -EINVAL; \
+ goto end; \
+ } \
+ opts->name = num; \
+ ret = len; \
+ \
+end: \
+ mutex_unlock(&opts->lock); \
+ return ret; \
+} \
+ \
+static struct f_midi_opts_attribute f_midi_opts_##name = \
+ __CONFIGFS_ATTR(name, S_IRUGO | S_IWUSR, f_midi_opts_##name##_show, \
+ f_midi_opts_##name##_store)
+
+F_MIDI_OPT(index, true, SNDRV_CARDS);
+F_MIDI_OPT(buflen, false, 0);
+F_MIDI_OPT(qlen, false, 0);
+F_MIDI_OPT(in_ports, true, MAX_PORTS);
+F_MIDI_OPT(out_ports, true, MAX_PORTS);
+
+static ssize_t f_midi_opts_id_show(struct f_midi_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = strlcpy(page, opts->id, PAGE_SIZE);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_midi_opts_id_store(struct f_midi_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ char *c;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ c = kstrndup(page, len, GFP_KERNEL);
+ if (!c) {
+ ret = -ENOMEM;
+ goto end;
+ }
+ if (opts->id_allocated)
+ kfree(opts->id);
+ opts->id = c;
+ opts->id_allocated = true;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_midi_opts_attribute f_midi_opts_id =
+ __CONFIGFS_ATTR(id, S_IRUGO | S_IWUSR, f_midi_opts_id_show,
+ f_midi_opts_id_store);
+
+static struct configfs_attribute *midi_attrs[] = {
+ &f_midi_opts_index.attr,
+ &f_midi_opts_buflen.attr,
+ &f_midi_opts_qlen.attr,
+ &f_midi_opts_in_ports.attr,
+ &f_midi_opts_out_ports.attr,
+ &f_midi_opts_id.attr,
+ NULL,
+};
+
+static struct config_item_type midi_func_type = {
+ .ct_item_ops = &midi_item_ops,
+ .ct_attrs = midi_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
+static void f_midi_free_inst(struct usb_function_instance *f)
+{
+ struct f_midi_opts *opts;
+
+ opts = container_of(f, struct f_midi_opts, func_inst);
+
+ if (opts->id_allocated)
+ kfree(opts->id);
+
+ kfree(opts);
+}
+
+static struct usb_function_instance *f_midi_alloc_inst(void)
+{
+ struct f_midi_opts *opts;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ mutex_init(&opts->lock);
+ opts->func_inst.free_func_inst = f_midi_free_inst;
+ opts->index = SNDRV_DEFAULT_IDX1;
+ opts->id = SNDRV_DEFAULT_STR1;
+ opts->buflen = 256;
+ opts->qlen = 32;
+ opts->in_ports = 1;
+ opts->out_ports = 1;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &midi_func_type);
+
+ return &opts->func_inst;
+}
+
+static void f_midi_free(struct usb_function *f)
+{
+ struct f_midi *midi;
+ struct f_midi_opts *opts;
+ int i;
+
+ midi = func_to_midi(f);
+ opts = container_of(f->fi, struct f_midi_opts, func_inst);
+ kfree(midi->id);
+ mutex_lock(&opts->lock);
+ for (i = opts->in_ports - 1; i >= 0; --i)
+ kfree(midi->in_port[i]);
+ kfree(midi);
+ --opts->refcnt;
+ mutex_unlock(&opts->lock);
+}
+
+static void f_midi_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+ struct usb_composite_dev *cdev = f->config->cdev;
+ struct f_midi *midi = func_to_midi(f);
+ struct snd_card *card;
+
+ DBG(cdev, "unbind\n");
+
+ /* just to be sure */
+ f_midi_disable(f);
+
+ card = midi->card;
+ midi->card = NULL;
+ if (card)
+ snd_card_free(card);
+
+ usb_free_all_descriptors(f);
+}
+
+static struct usb_function *f_midi_alloc(struct usb_function_instance *fi)
{
struct f_midi *midi;
+ struct f_midi_opts *opts;
int status, i;
+ opts = container_of(fi, struct f_midi_opts, func_inst);
+
+ mutex_lock(&opts->lock);
/* sanity check */
- if (in_ports > MAX_PORTS || out_ports > MAX_PORTS)
- return -EINVAL;
+ if (opts->in_ports > MAX_PORTS || opts->out_ports > MAX_PORTS) {
+ mutex_unlock(&opts->lock);
+ return ERR_PTR(-EINVAL);
+ }
/* allocate and initialize one new instance */
- midi = kzalloc(sizeof *midi, GFP_KERNEL);
+ midi = kzalloc(sizeof(*midi), GFP_KERNEL);
if (!midi) {
- status = -ENOMEM;
- goto fail;
+ mutex_unlock(&opts->lock);
+ return ERR_PTR(-ENOMEM);
}
- for (i = 0; i < in_ports; i++) {
+ for (i = 0; i < opts->in_ports; i++) {
struct gmidi_in_port *port = kzalloc(sizeof(*port), GFP_KERNEL);
+
if (!port) {
status = -ENOMEM;
+ mutex_unlock(&opts->lock);
goto setup_fail;
}
@@ -948,39 +1134,37 @@ int __init f_midi_bind_config(struct usb_configuration *c,
midi->in_port[i] = port;
}
- midi->gadget = c->cdev->gadget;
- tasklet_init(&midi->tasklet, f_midi_in_tasklet, (unsigned long) midi);
-
/* set up ALSA midi devices */
- midi->in_ports = in_ports;
- midi->out_ports = out_ports;
- status = f_midi_register_card(midi);
- if (status < 0)
- goto setup_fail;
-
- midi->func.name = "gmidi function";
- midi->func.strings = midi_strings;
- midi->func.bind = f_midi_bind;
- midi->func.unbind = f_midi_unbind;
- midi->func.set_alt = f_midi_set_alt;
- midi->func.disable = f_midi_disable;
-
- midi->id = kstrdup(id, GFP_KERNEL);
- midi->index = index;
- midi->buflen = buflen;
- midi->qlen = qlen;
-
- status = usb_add_function(c, &midi->func);
- if (status)
- goto setup_fail;
-
- return 0;
-
+ midi->id = kstrdup(opts->id, GFP_KERNEL);
+ if (opts->id && !midi->id) {
+ status = -ENOMEM;
+ mutex_unlock(&opts->lock);
+ goto kstrdup_fail;
+ }
+ midi->in_ports = opts->in_ports;
+ midi->out_ports = opts->out_ports;
+ midi->index = opts->index;
+ midi->buflen = opts->buflen;
+ midi->qlen = opts->qlen;
+ ++opts->refcnt;
+ mutex_unlock(&opts->lock);
+
+ midi->func.name = "gmidi function";
+ midi->func.bind = f_midi_bind;
+ midi->func.unbind = f_midi_unbind;
+ midi->func.set_alt = f_midi_set_alt;
+ midi->func.disable = f_midi_disable;
+ midi->func.free_func = f_midi_free;
+
+ return &midi->func;
+
+kstrdup_fail:
+ f_midi_unregister_card(midi);
setup_fail:
for (--i; i >= 0; i--)
kfree(midi->in_port[i]);
kfree(midi);
-fail:
- return status;
+ return ERR_PTR(status);
}
+DECLARE_USB_FUNCTION_INIT(midi, f_midi_alloc_inst, f_midi_alloc);
diff --git a/drivers/usb/gadget/function/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index 16361b0a8b46..bdcda9f5148e 100644
--- a/drivers/usb/gadget/function/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -1441,6 +1441,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
status = usb_assign_descriptors(f, ncm_fs_function, ncm_hs_function,
NULL);
+ if (status)
+ goto fail;
+
/*
* NOTE: all that is done without knowing or caring about
* the network link ... which is unavailable to this code
diff --git a/drivers/usb/gadget/function/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index f13fc6a58565..829edf878dac 100644
--- a/drivers/usb/gadget/function/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -375,8 +375,7 @@ static struct sk_buff *rndis_add_header(struct gether *port,
struct sk_buff *skb2;
skb2 = skb_realloc_headroom(skb, sizeof(struct rndis_packet_msg_type));
- if (skb2)
- rndis_add_hdr(skb2);
+ rndis_add_hdr(skb2);
dev_kfree_skb(skb);
return skb2;
diff --git a/drivers/usb/gadget/function/u_hid.h b/drivers/usb/gadget/function/u_hid.h
new file mode 100644
index 000000000000..aaa0e368a159
--- /dev/null
+++ b/drivers/usb/gadget/function/u_hid.h
@@ -0,0 +1,42 @@
+/*
+ * u_hid.h
+ *
+ * Utility definitions for the hid function
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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_HID_H
+#define U_HID_H
+
+#include <linux/usb/composite.h>
+
+struct f_hid_opts {
+ struct usb_function_instance func_inst;
+ int minor;
+ unsigned char subclass;
+ unsigned char protocol;
+ unsigned short report_length;
+ unsigned short report_desc_length;
+ unsigned char *report_desc;
+ bool report_desc_alloc;
+
+ /*
+ * Protect the data form concurrent access by read/write
+ * and create symlink/remove symlink.
+ */
+ struct mutex lock;
+ int refcnt;
+};
+
+int ghid_setup(struct usb_gadget *g, int count);
+void ghid_cleanup(void);
+
+#endif /* U_HID_H */
diff --git a/drivers/usb/gadget/function/u_midi.h b/drivers/usb/gadget/function/u_midi.h
new file mode 100644
index 000000000000..22510189758e
--- /dev/null
+++ b/drivers/usb/gadget/function/u_midi.h
@@ -0,0 +1,40 @@
+/*
+ * u_midi.h
+ *
+ * Utility definitions for the midi function
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * 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_MIDI_H
+#define U_MIDI_H
+
+#include <linux/usb/composite.h>
+
+struct f_midi_opts {
+ struct usb_function_instance func_inst;
+ int index;
+ char *id;
+ bool id_allocated;
+ unsigned int in_ports;
+ unsigned int out_ports;
+ unsigned int buflen;
+ unsigned int qlen;
+
+ /*
+ * Protect the data form concurrent access by read/write
+ * and create symlink/remove symlink.
+ */
+ struct mutex lock;
+ int refcnt;
+};
+
+#endif /* U_MIDI_H */
+
diff --git a/drivers/usb/gadget/function/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
index a44a07f30281..53842a1b947f 100644
--- a/drivers/usb/gadget/function/u_uac1.c
+++ b/drivers/usb/gadget/function/u_uac1.c
@@ -213,9 +213,6 @@ static int gaudio_open_snd_dev(struct gaudio *card)
fn_cap = opts->fn_cap;
fn_cntl = opts->fn_cntl;
- if (!card)
- return -ENODEV;
-
/* Open control device */
snd = &card->control;
snd->filp = filp_open(fn_cntl, O_RDWR, 0);
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
index 24392d269709..fd48ef3af4eb 100644
--- a/drivers/usb/gadget/legacy/Kconfig
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -287,6 +287,7 @@ config USB_MIDI_GADGET
depends on SND
select USB_LIBCOMPOSITE
select SND_RAWMIDI
+ select USB_F_MIDI
help
The MIDI Gadget acts as a USB Audio device, with one MIDI
input and one MIDI output. These MIDI jacks appear as
@@ -419,6 +420,7 @@ endif # TTY
config USB_G_HID
tristate "HID Gadget"
select USB_LIBCOMPOSITE
+ select USB_F_HID
help
The HID gadget driver provides generic emulation of USB
Human Interface Devices (HID).
diff --git a/drivers/usb/gadget/legacy/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index 1b075132f8f1..633683a72a11 100644
--- a/drivers/usb/gadget/legacy/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
@@ -237,7 +237,7 @@ static void dbgp_unbind(struct usb_gadget *gadget)
static unsigned char tty_line;
#endif
-static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
+static int dbgp_configure_endpoints(struct usb_gadget *gadget)
{
int stp;
@@ -273,19 +273,10 @@ static int __init dbgp_configure_endpoints(struct usb_gadget *gadget)
dbgp.serial->in->desc = &i_desc;
dbgp.serial->out->desc = &o_desc;
-
- if (gserial_alloc_line(&tty_line)) {
- stp = 3;
- goto fail_3;
- }
+#endif
return 0;
-fail_3:
- dbgp.o_ep->driver_data = NULL;
-#else
- return 0;
-#endif
fail_2:
dbgp.i_ep->driver_data = NULL;
fail_1:
@@ -324,10 +315,17 @@ static int __init dbgp_bind(struct usb_gadget *gadget,
err = -ENOMEM;
goto fail;
}
+
+ if (gserial_alloc_line(&tty_line)) {
+ stp = 4;
+ err = -ENODEV;
+ goto fail;
+ }
#endif
+
err = dbgp_configure_endpoints(gadget);
if (err < 0) {
- stp = 4;
+ stp = 5;
goto fail;
}
@@ -383,6 +381,10 @@ static int dbgp_setup(struct usb_gadget *gadget,
#ifdef CONFIG_USB_G_DBGP_PRINTK
err = dbgp_enable_ep();
#else
+ err = dbgp_configure_endpoints(gadget);
+ if (err < 0) {
+ goto fail;
+ }
err = gserial_connect(dbgp.serial, tty_line);
#endif
if (err < 0)
diff --git a/drivers/usb/gadget/legacy/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index 3d696b86ff76..e02a095294ac 100644
--- a/drivers/usb/gadget/legacy/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -37,7 +37,7 @@
#include "gadget_chips.h"
-#include "f_midi.c"
+#include "u_midi.h"
/*-------------------------------------------------------------------------*/
@@ -115,8 +115,13 @@ static struct usb_gadget_strings *dev_strings[] = {
NULL,
};
+static struct usb_function_instance *fi_midi;
+static struct usb_function *f_midi;
+
static int __exit midi_unbind(struct usb_composite_dev *dev)
{
+ usb_put_function(f_midi);
+ usb_put_function_instance(fi_midi);
return 0;
}
@@ -130,28 +135,54 @@ static struct usb_configuration midi_config = {
static int __init midi_bind_config(struct usb_configuration *c)
{
- return f_midi_bind_config(c, index, id,
- in_ports, out_ports,
- buflen, qlen);
+ int status;
+
+ f_midi = usb_get_function(fi_midi);
+ if (IS_ERR(f_midi))
+ return PTR_ERR(f_midi);
+
+ status = usb_add_function(c, f_midi);
+ if (status < 0) {
+ usb_put_function(f_midi);
+ return status;
+ }
+
+ return 0;
}
static int __init midi_bind(struct usb_composite_dev *cdev)
{
+ struct f_midi_opts *midi_opts;
int status;
+ fi_midi = usb_get_function_instance("midi");
+ if (IS_ERR(fi_midi))
+ return PTR_ERR(fi_midi);
+
+ midi_opts = container_of(fi_midi, struct f_midi_opts, func_inst);
+ midi_opts->index = index;
+ midi_opts->id = id;
+ midi_opts->in_ports = in_ports;
+ midi_opts->out_ports = out_ports;
+ midi_opts->buflen = buflen;
+ midi_opts->qlen = qlen;
+
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
- return status;
+ goto put;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
midi_config.iConfiguration = strings_dev[STRING_DESCRIPTION_IDX].id;
status = usb_add_config(cdev, &midi_config, midi_bind_config);
if (status < 0)
- return status;
+ goto put;
usb_composite_overwrite_options(cdev, &coverwrite);
pr_info("%s\n", longname);
return 0;
+put:
+ usb_put_function_instance(fi_midi);
+ return status;
}
static __refdata struct usb_composite_driver midi_driver = {
diff --git a/drivers/usb/gadget/legacy/hid.c b/drivers/usb/gadget/legacy/hid.c
index 778613eb37af..633fe7e07688 100644
--- a/drivers/usb/gadget/legacy/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
@@ -17,11 +17,14 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/usb/composite.h>
+#include <linux/usb/g_hid.h>
#include "gadget_chips.h"
#define DRIVER_DESC "HID Gadget"
#define DRIVER_VERSION "2010/03/16"
+#include "u_hid.h"
+
/*-------------------------------------------------------------------------*/
#define HIDG_VENDOR_NUM 0x0525 /* XXX NetChip */
@@ -29,17 +32,9 @@
/*-------------------------------------------------------------------------*/
-/*
- * kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
-#include "f_hid.c"
-
-
struct hidg_func_node {
+ struct usb_function_instance *fi;
+ struct usb_function *f;
struct list_head node;
struct hidg_func_descriptor *func;
};
@@ -113,8 +108,8 @@ static struct usb_gadget_strings *dev_strings[] = {
static int __init do_config(struct usb_configuration *c)
{
- struct hidg_func_node *e;
- int func = 0, status = 0;
+ struct hidg_func_node *e, *n;
+ int status = 0;
if (gadget_is_otg(c->cdev->gadget)) {
c->descriptors = otg_desc;
@@ -122,11 +117,24 @@ static int __init do_config(struct usb_configuration *c)
}
list_for_each_entry(e, &hidg_func_list, node) {
- status = hidg_bind_config(c, e->func, func++);
- if (status)
- break;
+ e->f = usb_get_function(e->fi);
+ if (IS_ERR(e->f))
+ goto put;
+ status = usb_add_function(c, e->f);
+ if (status < 0) {
+ usb_put_function(e->f);
+ goto put;
+ }
}
+ return 0;
+put:
+ list_for_each_entry(n, &hidg_func_list, node) {
+ if (n == e)
+ break;
+ usb_remove_function(c, n->f);
+ usb_put_function(n->f);
+ }
return status;
}
@@ -143,6 +151,8 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
{
struct usb_gadget *gadget = cdev->gadget;
struct list_head *tmp;
+ struct hidg_func_node *n, *m;
+ struct f_hid_opts *hid_opts;
int status, funcs = 0;
list_for_each(tmp, &hidg_func_list)
@@ -151,10 +161,20 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
if (!funcs)
return -ENODEV;
- /* set up HID */
- status = ghid_setup(cdev->gadget, funcs);
- if (status < 0)
- return status;
+ list_for_each_entry(n, &hidg_func_list, node) {
+ n->fi = usb_get_function_instance("hid");
+ if (IS_ERR(n->fi)) {
+ status = PTR_ERR(n->fi);
+ goto put;
+ }
+ hid_opts = container_of(n->fi, struct f_hid_opts, func_inst);
+ hid_opts->subclass = n->func->subclass;
+ hid_opts->protocol = n->func->protocol;
+ hid_opts->report_length = n->func->report_length;
+ hid_opts->report_desc_length = n->func->report_desc_length;
+ hid_opts->report_desc = n->func->report_desc;
+ }
+
/* Allocate string descriptor numbers ... note that string
* contents can be overridden by the composite_dev glue.
@@ -162,24 +182,37 @@ static int __init hid_bind(struct usb_composite_dev *cdev)
status = usb_string_ids_tab(cdev, strings_dev);
if (status < 0)
- return status;
+ goto put;
device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
/* register our configuration */
status = usb_add_config(cdev, &config_driver, do_config);
if (status < 0)
- return status;
+ goto put;
usb_composite_overwrite_options(cdev, &coverwrite);
dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");
return 0;
+
+put:
+ list_for_each_entry(m, &hidg_func_list, node) {
+ if (m == n)
+ break;
+ usb_put_function_instance(m->fi);
+ }
+ return status;
}
static int __exit hid_unbind(struct usb_composite_dev *cdev)
{
- ghid_cleanup();
+ struct hidg_func_node *n;
+
+ list_for_each_entry(n, &hidg_func_list, node) {
+ usb_put_function(n->f);
+ usb_put_function_instance(n->fi);
+ }
return 0;
}
@@ -260,7 +293,7 @@ module_init(hidg_init);
static void __exit hidg_cleanup(void)
{
- platform_driver_unregister(&hidg_plat_driver);
usb_composite_unregister(&hidg_driver);
+ platform_driver_unregister(&hidg_plat_driver);
}
module_exit(hidg_cleanup);
diff --git a/drivers/usb/gadget/legacy/printer.c b/drivers/usb/gadget/legacy/printer.c
index 6474081dcbaf..90545980542f 100644
--- a/drivers/usb/gadget/legacy/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
@@ -208,6 +208,43 @@ static struct usb_descriptor_header *hs_printer_function[] = {
NULL
};
+/*
+ * Added endpoint descriptors for 3.0 devices
+ */
+
+static struct usb_endpoint_descriptor ss_ep_in_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
+ .bLength = sizeof(ss_ep_in_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor ss_ep_out_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_BULK,
+ .wMaxPacketSize = cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
+ .bLength = sizeof(ss_ep_out_comp_desc),
+ .bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *ss_printer_function[] = {
+ (struct usb_descriptor_header *) &intf_desc,
+ (struct usb_descriptor_header *) &ss_ep_in_desc,
+ (struct usb_descriptor_header *) &ss_ep_in_comp_desc,
+ (struct usb_descriptor_header *) &ss_ep_out_desc,
+ (struct usb_descriptor_header *) &ss_ep_out_comp_desc,
+ NULL
+};
+
static struct usb_otg_descriptor otg_descriptor = {
.bLength = sizeof otg_descriptor,
.bDescriptorType = USB_DT_OTG,
@@ -220,7 +257,20 @@ static const struct usb_descriptor_header *otg_desc[] = {
};
/* maxpacket and other transfer characteristics vary by speed. */
-#define ep_desc(g, hs, fs) (((g)->speed == USB_SPEED_HIGH)?(hs):(fs))
+static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
+ struct usb_endpoint_descriptor *fs,
+ struct usb_endpoint_descriptor *hs,
+ struct usb_endpoint_descriptor *ss)
+{
+ switch (gadget->speed) {
+ case USB_SPEED_SUPER:
+ return ss;
+ case USB_SPEED_HIGH:
+ return hs;
+ default:
+ return fs;
+ }
+}
/*-------------------------------------------------------------------------*/
@@ -793,11 +843,12 @@ set_printer_interface(struct printer_dev *dev)
{
int result = 0;
- dev->in_ep->desc = ep_desc(dev->gadget, &hs_ep_in_desc, &fs_ep_in_desc);
+ dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
+ &ss_ep_in_desc);
dev->in_ep->driver_data = dev;
- dev->out_ep->desc = ep_desc(dev->gadget, &hs_ep_out_desc,
- &fs_ep_out_desc);
+ dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
+ &hs_ep_out_desc, &ss_ep_out_desc);
dev->out_ep->driver_data = dev;
result = usb_ep_enable(dev->in_ep);
@@ -1016,9 +1067,11 @@ autoconf_fail:
/* assumes that all endpoints are dual-speed */
hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+ ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+ ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
ret = usb_assign_descriptors(f, fs_printer_function,
- hs_printer_function, NULL);
+ hs_printer_function, ss_printer_function);
if (ret)
return ret;
@@ -1253,7 +1306,7 @@ static __refdata struct usb_composite_driver printer_driver = {
.name = shortname,
.dev = &device_desc,
.strings = dev_strings,
- .max_speed = USB_SPEED_HIGH,
+ .max_speed = USB_SPEED_SUPER,
.bind = printer_bind,
.unbind = printer_unbind,
};
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
index 217365d35a25..b8e213eb36cc 100644
--- a/drivers/usb/gadget/udc/Kconfig
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -241,6 +241,8 @@ config USB_M66592
dynamically linked module called "m66592_udc" and force all
gadget drivers to also be dynamically linked.
+source "drivers/usb/gadget/udc/bdc/Kconfig"
+
#
# Controllers available only in discrete form (and all PCI controllers)
#
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
index a7f4491593f1..fba2049bf985 100644
--- a/drivers/usb/gadget/udc/Makefile
+++ b/drivers/usb/gadget/udc/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o
+obj-$(CONFIG_USB_BDC_UDC) += bdc/
diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
index 3b9d13848a4f..de7e5e2ccf1c 100644
--- a/drivers/usb/gadget/udc/amd5536udc.c
+++ b/drivers/usb/gadget/udc/amd5536udc.c
@@ -1401,9 +1401,8 @@ static int udc_wakeup(struct usb_gadget *gadget)
static int amd5536_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int amd5536_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
-/* gadget operations */
+static int amd5536_udc_stop(struct usb_gadget *g);
+
static const struct usb_gadget_ops udc_ops = {
.wakeup = udc_wakeup,
.get_frame = udc_get_frame,
@@ -1962,8 +1961,7 @@ __acquires(dev->lock)
}
/* Called by gadget driver to unregister itself */
-static int amd5536_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int amd5536_udc_stop(struct usb_gadget *g)
{
struct udc *dev = to_amd5536_udc(g);
unsigned long flags;
@@ -1971,7 +1969,7 @@ static int amd5536_udc_stop(struct usb_gadget *g,
spin_lock_irqsave(&dev->lock, flags);
udc_mask_unused_interrupts(dev);
- shutdown(dev, driver);
+ shutdown(dev, NULL);
spin_unlock_irqrestore(&dev->lock, flags);
dev->driver = NULL;
@@ -2873,7 +2871,7 @@ __acquires(dev->lock)
dev->driver->resume(&dev->gadget);
dev->sys_suspended = 0;
}
- dev->driver->disconnect(&dev->gadget);
+ usb_gadget_udc_reset(&dev->gadget, dev->driver);
spin_lock(&dev->lock);
/* disable ep0 to empty req queue */
diff --git a/drivers/usb/gadget/udc/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index 9968f5331fe4..eb2999c5d03c 100644
--- a/drivers/usb/gadget/udc/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
@@ -840,6 +840,31 @@ static void udc_reinit(struct at91_udc *udc)
}
}
+static void reset_gadget(struct at91_udc *udc)
+{
+ struct usb_gadget_driver *driver = udc->driver;
+ int i;
+
+ if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ udc->gadget.speed = USB_SPEED_UNKNOWN;
+ udc->suspended = 0;
+
+ for (i = 0; i < NUM_ENDPOINTS; i++) {
+ struct at91_ep *ep = &udc->ep[i];
+
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ if (driver) {
+ spin_unlock(&udc->lock);
+ usb_gadget_udc_reset(&udc->gadget, driver);
+ spin_lock(&udc->lock);
+ }
+
+ udc_reinit(udc);
+}
+
static void stop_activity(struct at91_udc *udc)
{
struct usb_gadget_driver *driver = udc->driver;
@@ -870,12 +895,10 @@ static void clk_on(struct at91_udc *udc)
return;
udc->clocked = 1;
- if (IS_ENABLED(CONFIG_COMMON_CLK)) {
- clk_set_rate(udc->uclk, 48000000);
- clk_prepare_enable(udc->uclk);
- }
- clk_prepare_enable(udc->iclk);
- clk_prepare_enable(udc->fclk);
+ if (IS_ENABLED(CONFIG_COMMON_CLK))
+ clk_enable(udc->uclk);
+ clk_enable(udc->iclk);
+ clk_enable(udc->fclk);
}
static void clk_off(struct at91_udc *udc)
@@ -884,10 +907,10 @@ static void clk_off(struct at91_udc *udc)
return;
udc->clocked = 0;
udc->gadget.speed = USB_SPEED_UNKNOWN;
- clk_disable_unprepare(udc->fclk);
- clk_disable_unprepare(udc->iclk);
+ clk_disable(udc->fclk);
+ clk_disable(udc->iclk);
if (IS_ENABLED(CONFIG_COMMON_CLK))
- clk_disable_unprepare(udc->uclk);
+ clk_disable(udc->uclk);
}
/*
@@ -984,8 +1007,8 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
static int at91_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
-static int at91_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver);
+static int at91_stop(struct usb_gadget *gadget);
+
static const struct usb_gadget_ops at91_udc_ops = {
.get_frame = at91_get_frame,
.wakeup = at91_wakeup,
@@ -1426,7 +1449,7 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
at91_udp_write(udc, AT91_UDP_ICR, AT91_UDP_ENDBUSRES);
VDBG("end bus reset\n");
udc->addr = 0;
- stop_activity(udc);
+ reset_gadget(udc);
/* enable ep0 */
at91_udp_write(udc, AT91_UDP_CSR(0),
@@ -1512,20 +1535,11 @@ static irqreturn_t at91_udc_irq (int irq, void *_udc)
/*-------------------------------------------------------------------------*/
-static void nop_release(struct device *dev)
-{
- /* nothing to free */
-}
-
static struct at91_udc controller = {
.gadget = {
.ops = &at91_udc_ops,
.ep0 = &controller.ep[0].ep,
.name = driver_name,
- .dev = {
- .init_name = "gadget",
- .release = nop_release,
- }
},
.ep[0] = {
.ep = {
@@ -1641,12 +1655,10 @@ static int at91_start(struct usb_gadget *gadget,
udc->enabled = 1;
udc->selfpowered = 1;
- DBG("bound to %s\n", driver->driver.name);
return 0;
}
-static int at91_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int at91_stop(struct usb_gadget *gadget)
{
struct at91_udc *udc;
unsigned long flags;
@@ -1659,7 +1671,6 @@ static int at91_stop(struct usb_gadget *gadget,
udc->driver = NULL;
- DBG("unbound from %s\n", driver->driver.name);
return 0;
}
@@ -1780,14 +1791,24 @@ static int at91udc_probe(struct platform_device *pdev)
}
/* don't do anything until we have both gadget driver and VBUS */
+ if (IS_ENABLED(CONFIG_COMMON_CLK)) {
+ clk_set_rate(udc->uclk, 48000000);
+ retval = clk_prepare(udc->uclk);
+ if (retval)
+ goto fail1;
+ }
+ retval = clk_prepare(udc->fclk);
+ if (retval)
+ goto fail1a;
+
retval = clk_prepare_enable(udc->iclk);
if (retval)
- goto fail1;
+ goto fail1b;
at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS);
at91_udp_write(udc, AT91_UDP_IDR, 0xffffffff);
/* Clear all pending interrupts - UDP may be used by bootloader. */
at91_udp_write(udc, AT91_UDP_ICR, 0xffffffff);
- clk_disable_unprepare(udc->iclk);
+ clk_disable(udc->iclk);
/* request UDC and maybe VBUS irqs */
udc->udp_irq = platform_get_irq(pdev, 0);
@@ -1795,7 +1816,7 @@ static int at91udc_probe(struct platform_device *pdev)
0, driver_name, udc);
if (retval < 0) {
DBG("request irq %d failed\n", udc->udp_irq);
- goto fail1;
+ goto fail1c;
}
if (gpio_is_valid(udc->board.vbus_pin)) {
retval = gpio_request(udc->board.vbus_pin, "udc_vbus");
@@ -1848,6 +1869,13 @@ fail3:
gpio_free(udc->board.vbus_pin);
fail2:
free_irq(udc->udp_irq, udc);
+fail1c:
+ clk_unprepare(udc->iclk);
+fail1b:
+ clk_unprepare(udc->fclk);
+fail1a:
+ if (IS_ENABLED(CONFIG_COMMON_CLK))
+ clk_unprepare(udc->uclk);
fail1:
if (IS_ENABLED(CONFIG_COMMON_CLK) && !IS_ERR(udc->uclk))
clk_put(udc->uclk);
@@ -1896,6 +1924,11 @@ static int __exit at91udc_remove(struct platform_device *pdev)
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
release_mem_region(res->start, resource_size(res));
+ if (IS_ENABLED(CONFIG_COMMON_CLK))
+ clk_unprepare(udc->uclk);
+ clk_unprepare(udc->fclk);
+ clk_unprepare(udc->iclk);
+
clk_put(udc->iclk);
clk_put(udc->fclk);
if (IS_ENABLED(CONFIG_COMMON_CLK))
diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 1529926e20a0..b31747979c4a 100644
--- a/drivers/usb/gadget/udc/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -987,8 +987,8 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
static int atmel_usba_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
-static int atmel_usba_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver);
+static int atmel_usba_stop(struct usb_gadget *gadget);
+
static const struct usb_gadget_ops usba_udc_ops = {
.get_frame = usba_udc_get_frame,
.wakeup = usba_udc_wakeup,
@@ -1007,19 +1007,10 @@ static struct usb_endpoint_descriptor usba_ep0_desc = {
.bInterval = 1,
};
-static void nop_release(struct device *dev)
-{
-
-}
-
static struct usb_gadget usba_gadget_template = {
.ops = &usba_udc_ops,
.max_speed = USB_SPEED_HIGH,
.name = "atmel_usba_udc",
- .dev = {
- .init_name = "gadget",
- .release = nop_release,
- },
};
/*
@@ -1685,11 +1676,10 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
reset_all_endpoints(udc);
- if (udc->gadget.speed != USB_SPEED_UNKNOWN
- && udc->driver && udc->driver->disconnect) {
+ if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) {
udc->gadget.speed = USB_SPEED_UNKNOWN;
spin_unlock(&udc->lock);
- udc->driver->disconnect(&udc->gadget);
+ usb_gadget_udc_reset(&udc->gadget, udc->driver);
spin_lock(&udc->lock);
}
@@ -1791,8 +1781,6 @@ static int atmel_usba_start(struct usb_gadget *gadget,
return ret;
}
- DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name);
-
udc->vbus_prev = 0;
if (gpio_is_valid(udc->vbus_pin))
enable_irq(gpio_to_irq(udc->vbus_pin));
@@ -1809,8 +1797,7 @@ static int atmel_usba_start(struct usb_gadget *gadget,
return 0;
}
-static int atmel_usba_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int atmel_usba_stop(struct usb_gadget *gadget)
{
struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
unsigned long flags;
@@ -1830,8 +1817,6 @@ static int atmel_usba_stop(struct usb_gadget *gadget,
clk_disable_unprepare(udc->hclk);
clk_disable_unprepare(udc->pclk);
- DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name);
-
udc->driver = NULL;
return 0;
diff --git a/drivers/usb/gadget/udc/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index 2235b8808700..c790918b337b 100644
--- a/drivers/usb/gadget/udc/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
@@ -1836,8 +1836,7 @@ static int bcm63xx_udc_start(struct usb_gadget *gadget,
* @gadget: USB slave device.
* @driver: Driver for USB slave devices.
*/
-static int bcm63xx_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int bcm63xx_udc_stop(struct usb_gadget *gadget)
{
struct bcm63xx_udc *udc = gadget_to_udc(gadget);
unsigned long flags;
@@ -1963,7 +1962,7 @@ static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
{
struct bcm63xx_udc *udc = dev_id;
u32 stat;
- bool disconnected = false;
+ bool disconnected = false, bus_reset = false;
stat = usbd_readl(udc, USBD_EVENT_IRQ_STATUS_REG) &
usbd_readl(udc, USBD_EVENT_IRQ_MASK_REG);
@@ -1991,7 +1990,7 @@ static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
udc->ep0_req_reset = 1;
schedule_work(&udc->ep0_wq);
- disconnected = true;
+ bus_reset = true;
}
if (stat & BIT(USBD_EVENT_IRQ_SETUP)) {
if (bcm63xx_update_link_speed(udc)) {
@@ -2014,6 +2013,8 @@ static irqreturn_t bcm63xx_udc_ctrl_isr(int irq, void *dev_id)
if (disconnected && udc->driver)
udc->driver->disconnect(&udc->gadget);
+ else if (bus_reset && udc->driver)
+ usb_gadget_udc_reset(&udc->gadget, udc->driver);
return IRQ_HANDLED;
}
@@ -2324,10 +2325,8 @@ static int bcm63xx_udc_probe(struct platform_device *pdev)
int rc = -ENOMEM, i, irq;
udc = devm_kzalloc(dev, sizeof(*udc), GFP_KERNEL);
- if (!udc) {
- dev_err(dev, "cannot allocate memory\n");
+ if (!udc)
return -ENOMEM;
- }
platform_set_drvdata(pdev, udc);
udc->dev = dev;
diff --git a/drivers/usb/gadget/udc/bdc/Kconfig b/drivers/usb/gadget/udc/bdc/Kconfig
new file mode 100644
index 000000000000..0d7b8c9f72fd
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/Kconfig
@@ -0,0 +1,21 @@
+config USB_BDC_UDC
+ tristate "Broadcom USB3.0 device controller IP driver(BDC)"
+ depends on USB_GADGET && HAS_DMA
+
+ help
+ BDC is Broadcom's USB3.0 device controller IP. If your SOC has a BDC IP
+ then select this driver.
+
+ Say "y" here to link the driver statically, or "m" to build a dynamically
+ linked module called "bdc".
+
+if USB_BDC_UDC
+
+comment "Platform Support"
+config USB_BDC_PCI
+ tristate "BDC support for PCIe based platforms"
+ depends on PCI
+ default USB_BDC_UDC
+ help
+ Enable support for platforms which have BDC connected through PCIe, such as Lego3 FPGA platform.
+endif
diff --git a/drivers/usb/gadget/udc/bdc/Makefile b/drivers/usb/gadget/udc/bdc/Makefile
new file mode 100644
index 000000000000..5cf6a3bcdf0f
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/Makefile
@@ -0,0 +1,8 @@
+obj-$(CONFIG_USB_BDC_UDC) += bdc.o
+bdc-y := bdc_core.o bdc_cmd.o bdc_ep.o bdc_udc.o
+
+ifneq ($(CONFIG_USB_GADGET_VERBOSE),)
+ bdc-y += bdc_dbg.o
+endif
+
+obj-$(CONFIG_USB_BDC_PCI) += bdc_pci.o
diff --git a/drivers/usb/gadget/udc/bdc/bdc.h b/drivers/usb/gadget/udc/bdc/bdc.h
new file mode 100644
index 000000000000..dc18a20bf040
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc.h
@@ -0,0 +1,490 @@
+/*
+ * bdc.h - header for the BRCM BDC USB3.0 device controller
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * 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 __LINUX_BDC_H__
+#define __LINUX_BDC_H__
+
+#include <linux/kernel.h>
+#include <linux/usb.h>
+#include <linux/device.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/dma-mapping.h>
+#include <linux/mm.h>
+#include <linux/debugfs.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <asm/unaligned.h>
+
+#define BRCM_BDC_NAME "bdc_usb3"
+#define BRCM_BDC_DESC "BDC device controller driver"
+
+#define DMA_ADDR_INVALID (~(dma_addr_t)0)
+
+/* BDC command operation timeout in usec*/
+#define BDC_CMD_TIMEOUT 1000
+/* BDC controller operation timeout in usec*/
+#define BDC_COP_TIMEOUT 500
+
+/*
+ * Maximum size of ep0 response buffer for ch9 requests,
+ * the set_sel request uses 6 so far, the max.
+*/
+#define EP0_RESPONSE_BUFF 6
+/* Start with SS as default */
+#define EP0_MAX_PKT_SIZE 512
+
+/* 64 entries in a SRR */
+#define NUM_SR_ENTRIES 64
+
+/* Num of bds per table */
+#define NUM_BDS_PER_TABLE 32
+
+/* Num of tables in bd list for control,bulk and Int ep */
+#define NUM_TABLES 2
+
+/* Num of tables in bd list for Isoch ep */
+#define NUM_TABLES_ISOCH 6
+
+/* U1 Timeout default: 248usec */
+#define U1_TIMEOUT 0xf8
+
+/* Interrupt coalescence in usec */
+#define INT_CLS 500
+
+/* Register offsets */
+/* Configuration and Capability registers */
+#define BDC_BDCCFG0 0x00
+#define BDC_BDCCFG1 0x04
+#define BDC_BDCCAP0 0x08
+#define BDC_BDCCAP1 0x0c
+#define BDC_CMDPAR0 0x10
+#define BDC_CMDPAR1 0x14
+#define BDC_CMDPAR2 0x18
+#define BDC_CMDSC 0x1c
+#define BDC_USPC 0x20
+#define BDC_USPPMS 0x28
+#define BDC_USPPM2 0x2c
+#define BDC_SPBBAL 0x38
+#define BDC_SPBBAH 0x3c
+#define BDC_BDCSC 0x40
+#define BDC_XSFNTF 0x4c
+
+#define BDC_DVCSA 0x50
+#define BDC_DVCSB 0x54
+#define BDC_EPSTS0(n) (0x60 + (n * 0x10))
+#define BDC_EPSTS1(n) (0x64 + (n * 0x10))
+#define BDC_EPSTS2(n) (0x68 + (n * 0x10))
+#define BDC_EPSTS3(n) (0x6c + (n * 0x10))
+#define BDC_EPSTS4(n) (0x70 + (n * 0x10))
+#define BDC_EPSTS5(n) (0x74 + (n * 0x10))
+#define BDC_EPSTS6(n) (0x78 + (n * 0x10))
+#define BDC_EPSTS7(n) (0x7c + (n * 0x10))
+#define BDC_SRRBAL(n) (0x200 + (n * 0x10))
+#define BDC_SRRBAH(n) (0x204 + (n * 0x10))
+#define BDC_SRRINT(n) (0x208 + (n * 0x10))
+#define BDC_INTCTLS(n) (0x20c + (n * 0x10))
+
+/* Extended capability regs */
+#define BDC_FSCNOC 0xcd4
+#define BDC_FSCNIC 0xce4
+#define NUM_NCS(p) (p >> 28)
+
+/* Register bit fields and Masks */
+/* BDC Configuration 0 */
+#define BDC_PGS(p) (((p) & (0x7 << 8)) >> 8)
+#define BDC_SPB(p) (p & 0x7)
+
+/* BDC Capability1 */
+#define BDC_P64 (1 << 0)
+
+/* BDC Command register */
+#define BDC_CMD_FH 0xe
+#define BDC_CMD_DNC 0x6
+#define BDC_CMD_EPO 0x4
+#define BDC_CMD_BLA 0x3
+#define BDC_CMD_EPC 0x2
+#define BDC_CMD_DVC 0x1
+#define BDC_CMD_CWS (0x1 << 5)
+#define BDC_CMD_CST(p) (((p) & (0xf << 6))>>6)
+#define BDC_CMD_EPN(p) ((p & 0x1f) << 10)
+#define BDC_SUB_CMD_ADD (0x1 << 17)
+#define BDC_SUB_CMD_FWK (0x4 << 17)
+/* Reset sequence number */
+#define BDC_CMD_EPO_RST_SN (0x1 << 16)
+#define BDC_CMD_EP0_XSD (0x1 << 16)
+#define BDC_SUB_CMD_ADD_EP (0x1 << 17)
+#define BDC_SUB_CMD_DRP_EP (0x2 << 17)
+#define BDC_SUB_CMD_EP_STP (0x2 << 17)
+#define BDC_SUB_CMD_EP_STL (0x4 << 17)
+#define BDC_SUB_CMD_EP_RST (0x1 << 17)
+#define BDC_CMD_SRD (1 << 27)
+
+/* CMD completion status */
+#define BDC_CMDS_SUCC 0x1
+#define BDC_CMDS_PARA 0x3
+#define BDC_CMDS_STAT 0x4
+#define BDC_CMDS_FAIL 0x5
+#define BDC_CMDS_INTL 0x6
+#define BDC_CMDS_BUSY 0xf
+
+/* CMDSC Param 2 shifts */
+#define EPT_SHIFT 22
+#define MP_SHIFT 10
+#define MB_SHIFT 6
+#define EPM_SHIFT 4
+
+/* BDC USPSC */
+#define BDC_VBC (1 << 31)
+#define BDC_PRC (1 << 30)
+#define BDC_PCE (1 << 29)
+#define BDC_CFC (1 << 28)
+#define BDC_PCC (1 << 27)
+#define BDC_PSC (1 << 26)
+#define BDC_VBS (1 << 25)
+#define BDC_PRS (1 << 24)
+#define BDC_PCS (1 << 23)
+#define BDC_PSP(p) (((p) & (0x7 << 20))>>20)
+#define BDC_SCN (1 << 8)
+#define BDC_SDC (1 << 7)
+#define BDC_SWS (1 << 4)
+
+#define BDC_USPSC_RW (BDC_SCN|BDC_SDC|BDC_SWS|0xf)
+#define BDC_PSP(p) (((p) & (0x7 << 20))>>20)
+
+#define BDC_SPEED_FS 0x1
+#define BDC_SPEED_LS 0x2
+#define BDC_SPEED_HS 0x3
+#define BDC_SPEED_SS 0x4
+
+#define BDC_PST(p) (p & 0xf)
+#define BDC_PST_MASK 0xf
+
+/* USPPMS */
+#define BDC_U2E (0x1 << 31)
+#define BDC_U1E (0x1 << 30)
+#define BDC_U2A (0x1 << 29)
+#define BDC_PORT_W1S (0x1 << 17)
+#define BDC_U1T(p) ((p) & 0xff)
+#define BDC_U2T(p) (((p) & 0xff) << 8)
+#define BDC_U1T_MASK 0xff
+
+/* USBPM2 */
+/* Hardware LPM Enable */
+#define BDC_HLE (1 << 16)
+
+/* BDC Status and Control */
+#define BDC_COP_RST (1 << 29)
+#define BDC_COP_RUN (2 << 29)
+#define BDC_COP_STP (4 << 29)
+
+#define BDC_COP_MASK (BDC_COP_RST|BDC_COP_RUN|BDC_COP_STP)
+
+#define BDC_COS (1 << 28)
+#define BDC_CSTS(p) (((p) & (0x7 << 20)) >> 20)
+#define BDC_MASK_MCW (1 << 7)
+#define BDC_GIE (1 << 1)
+#define BDC_GIP (1 << 0)
+
+#define BDC_HLT 1
+#define BDC_NOR 2
+#define BDC_OIP 7
+
+/* Buffer descriptor and Status report bit fields and masks */
+#define BD_TYPE_BITMASK (0xf)
+#define BD_CHAIN 0xf
+
+#define BD_TFS_SHIFT 4
+#define BD_SOT (1 << 26)
+#define BD_EOT (1 << 27)
+#define BD_ISP (1 << 29)
+#define BD_IOC (1 << 30)
+#define BD_SBF (1 << 31)
+
+#define BD_INTR_TARGET(p) (((p) & 0x1f) << 27)
+
+#define BDC_SRR_RWS (1 << 4)
+#define BDC_SRR_RST (1 << 3)
+#define BDC_SRR_ISR (1 << 2)
+#define BDC_SRR_IE (1 << 1)
+#define BDC_SRR_IP (1 << 0)
+#define BDC_SRR_EPI(p) (((p) & (0xff << 24)) >> 24)
+#define BDC_SRR_DPI(p) (((p) & (0xff << 16)) >> 16)
+#define BDC_SRR_DPI_MASK 0x00ff0000
+
+#define MARK_CHAIN_BD (BD_CHAIN|BD_EOT|BD_SOT)
+
+/* Control transfer BD specific fields */
+#define BD_DIR_IN (1 << 25)
+
+#define BDC_PTC_MASK 0xf0000000
+
+/* status report defines */
+#define SR_XSF 0
+#define SR_USPC 4
+#define SR_BD_LEN(p) (p & 0xffffff)
+
+#define XSF_SUCC 0x1
+#define XSF_SHORT 0x3
+#define XSF_BABB 0x4
+#define XSF_SETUP_RECV 0x6
+#define XSF_DATA_START 0x7
+#define XSF_STATUS_START 0x8
+
+#define XSF_STS(p) (((p) >> 28) & 0xf)
+
+/* Transfer BD fields */
+#define BD_LEN(p) ((p) & 0x1ffff)
+#define BD_LTF (1 << 25)
+#define BD_TYPE_DS 0x1
+#define BD_TYPE_SS 0x2
+
+#define BDC_EP_ENABLED (1 << 0)
+#define BDC_EP_STALL (1 << 1)
+#define BDC_EP_STOP (1 << 2)
+
+/* One BD can transfer max 65536 bytes */
+#define BD_MAX_BUFF_SIZE (1 << 16)
+/* Maximum bytes in one XFR, Refer to BDC spec */
+#define MAX_XFR_LEN 16777215
+
+/* defines for Force Header command */
+#define DEV_NOTF_TYPE 6
+#define FWK_SUBTYPE 1
+#define TRA_PACKET 4
+
+#define to_bdc_ep(e) container_of(e, struct bdc_ep, usb_ep)
+#define to_bdc_req(r) container_of(r, struct bdc_req, usb_req)
+#define gadget_to_bdc(g) container_of(g, struct bdc, gadget)
+
+/* FUNCTION WAKE DEV NOTIFICATION interval, USB3 spec table 8.13 */
+#define BDC_TNOTIFY 2500 /*in ms*/
+/* Devstatus bitfields */
+#define REMOTE_WAKEUP_ISSUED (1 << 16)
+#define DEVICE_SUSPENDED (1 << 17)
+#define FUNC_WAKE_ISSUED (1 << 18)
+#define REMOTE_WAKE_ENABLE (1 << USB_DEVICE_REMOTE_WAKEUP)
+
+/* On disconnect, preserve these bits and clear rest */
+#define DEVSTATUS_CLEAR (1 << USB_DEVICE_SELF_POWERED)
+/* Hardware and software Data structures */
+
+/* Endpoint bd: buffer descriptor */
+struct bdc_bd {
+ __le32 offset[4];
+};
+
+/* Status report in Status report ring(srr) */
+struct bdc_sr {
+ __le32 offset[4];
+};
+
+/* bd_table: contigous bd's in a table */
+struct bd_table {
+ struct bdc_bd *start_bd;
+ /* dma address of start bd of table*/
+ dma_addr_t dma;
+};
+
+/*
+ * Each endpoint has a bdl(buffer descriptor list), bdl consists of 1 or more bd
+ * table's chained to each other through a chain bd, every table has equal
+ * number of bds. the software uses bdi(bd index) to refer to particular bd in
+ * the list.
+ */
+struct bd_list {
+ /* Array of bd table pointers*/
+ struct bd_table **bd_table_array;
+ /* How many tables chained to each other */
+ int num_tabs;
+ /* Max_bdi = num_tabs * num_bds_table - 1 */
+ int max_bdi;
+ /* current enq bdi from sw point of view */
+ int eqp_bdi;
+ /* current deq bdi from sw point of view */
+ int hwd_bdi;
+ /* numbers of bds per table */
+ int num_bds_table;
+};
+
+struct bdc_req;
+
+/* Representation of a transfer, one transfer can have multiple bd's */
+struct bd_transfer {
+ struct bdc_req *req;
+ /* start bd index */
+ int start_bdi;
+ /* this will be the next hw dqp when this transfer completes */
+ int next_hwd_bdi;
+ /* number of bds in this transfer */
+ int num_bds;
+};
+
+/*
+ * Representation of a gadget request, every gadget request is contained
+ * by 1 bd_transfer.
+ */
+struct bdc_req {
+ struct usb_request usb_req;
+ struct list_head queue;
+ struct bdc_ep *ep;
+ /* only one Transfer per request */
+ struct bd_transfer bd_xfr;
+ int epnum;
+};
+
+/* scratchpad buffer needed by bdc hardware */
+struct bdc_scratchpad {
+ dma_addr_t sp_dma;
+ void *buff;
+ u32 size;
+};
+
+/* endpoint representation */
+struct bdc_ep {
+ struct usb_ep usb_ep;
+ struct list_head queue;
+ struct bdc *bdc;
+ u8 ep_type;
+ u8 dir;
+ u8 ep_num;
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ const struct usb_endpoint_descriptor *desc;
+ unsigned int flags;
+ char name[20];
+ /* endpoint bd list*/
+ struct bd_list bd_list;
+ /*
+ * HW generates extra event for multi bd tranfers, this flag helps in
+ * ignoring the extra event
+ */
+ bool ignore_next_sr;
+};
+
+/* bdc cmmand parameter structure */
+struct bdc_cmd_params {
+ u32 param2;
+ u32 param1;
+ u32 param0;
+};
+
+/* status report ring(srr), currently one srr is supported for entire system */
+struct srr {
+ struct bdc_sr *sr_bds;
+ u16 eqp_index;
+ u16 dqp_index;
+ dma_addr_t dma_addr;
+};
+
+/* EP0 states */
+enum bdc_ep0_state {
+ WAIT_FOR_SETUP = 0,
+ WAIT_FOR_DATA_START,
+ WAIT_FOR_DATA_XMIT,
+ WAIT_FOR_STATUS_START,
+ WAIT_FOR_STATUS_XMIT,
+ STATUS_PENDING
+};
+
+/* Link states */
+enum bdc_link_state {
+ BDC_LINK_STATE_U0 = 0x00,
+ BDC_LINK_STATE_U3 = 0x03,
+ BDC_LINK_STATE_RX_DET = 0x05,
+ BDC_LINK_STATE_RESUME = 0x0f
+};
+
+/* representation of bdc */
+struct bdc {
+ struct usb_gadget gadget;
+ struct usb_gadget_driver *gadget_driver;
+ struct device *dev;
+ /* device lock */
+ spinlock_t lock;
+
+ /* num of endpoints for a particular instantiation of IP */
+ unsigned int num_eps;
+ /*
+ * Array of ep's, it uses the same index covention as bdc hw i.e.
+ * 1 for ep0, 2 for 1out,3 for 1in ....
+ */
+ struct bdc_ep **bdc_ep_array;
+ void __iomem *regs;
+ struct bdc_scratchpad scratchpad;
+ u32 sp_buff_size;
+ /* current driver supports 1 status ring */
+ struct srr srr;
+ /* Last received setup packet */
+ struct usb_ctrlrequest setup_pkt;
+ struct bdc_req ep0_req;
+ struct bdc_req status_req;
+ enum bdc_ep0_state ep0_state;
+ bool delayed_status;
+ bool zlp_needed;
+ bool reinit;
+ bool pullup;
+ /* Bits 0-15 are standard and 16-31 for proprietary information */
+ u32 devstatus;
+ int irq;
+ void *mem;
+ u32 dev_addr;
+ /* DMA pools */
+ struct dma_pool *bd_table_pool;
+ u8 test_mode;
+ /* array of callbacks for various status report handlers */
+ void (*sr_handler[2])(struct bdc *, struct bdc_sr *);
+ /* ep0 callback handlers */
+ void (*sr_xsf_ep0[3])(struct bdc *, struct bdc_sr *);
+ /* ep0 response buffer for ch9 requests like GET_STATUS and SET_SEL */
+ unsigned char ep0_response_buff[EP0_RESPONSE_BUFF];
+ /*
+ * Timer to check if host resumed transfer after bdc sent Func wake
+ * notification packet after a remote wakeup. if not, then resend the
+ * Func Wake packet every 2.5 secs. Refer to USB3 spec section 8.5.6.4
+ */
+ struct delayed_work func_wake_notify;
+};
+
+static inline u32 bdc_readl(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline void bdc_writel(void __iomem *base, u32 offset, u32 value)
+{
+ writel(value, base + offset);
+}
+
+/* Buffer descriptor list operations */
+void bdc_notify_xfr(struct bdc *, u32);
+void bdc_softconn(struct bdc *);
+void bdc_softdisconn(struct bdc *);
+int bdc_run(struct bdc *);
+int bdc_stop(struct bdc *);
+int bdc_reset(struct bdc *);
+int bdc_udc_init(struct bdc *);
+void bdc_udc_exit(struct bdc *);
+int bdc_reinit(struct bdc *);
+
+/* Status report handlers */
+/* Upstream port status change sr */
+void bdc_sr_uspc(struct bdc *, struct bdc_sr *);
+/* transfer sr */
+void bdc_sr_xsf(struct bdc *, struct bdc_sr *);
+/* EP0 XSF handlers */
+void bdc_xsf_ep0_setup_recv(struct bdc *, struct bdc_sr *);
+void bdc_xsf_ep0_data_start(struct bdc *, struct bdc_sr *);
+void bdc_xsf_ep0_status_start(struct bdc *, struct bdc_sr *);
+
+#endif /* __LINUX_BDC_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.c b/drivers/usb/gadget/udc/bdc/bdc_cmd.c
new file mode 100644
index 000000000000..6a4155c4bd86
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.c
@@ -0,0 +1,376 @@
+/*
+ * bdc_cmd.c - BRCM BDC USB3.0 device controller
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+
+#include "bdc.h"
+#include "bdc_cmd.h"
+#include "bdc_dbg.h"
+
+/* Issues a cmd to cmd processor and waits for cmd completion */
+static int bdc_issue_cmd(struct bdc *bdc, u32 cmd_sc, u32 param0,
+ u32 param1, u32 param2)
+{
+ u32 timeout = BDC_CMD_TIMEOUT;
+ u32 cmd_status;
+ u32 temp;
+
+ bdc_writel(bdc->regs, BDC_CMDPAR0, param0);
+ bdc_writel(bdc->regs, BDC_CMDPAR1, param1);
+ bdc_writel(bdc->regs, BDC_CMDPAR2, param2);
+
+ /* Issue the cmd */
+ /* Make sure the cmd params are written before asking HW to exec cmd */
+ wmb();
+ bdc_writel(bdc->regs, BDC_CMDSC, cmd_sc | BDC_CMD_CWS | BDC_CMD_SRD);
+ do {
+ temp = bdc_readl(bdc->regs, BDC_CMDSC);
+ dev_dbg_ratelimited(bdc->dev, "cmdsc=%x", temp);
+ cmd_status = BDC_CMD_CST(temp);
+ if (cmd_status != BDC_CMDS_BUSY) {
+ dev_dbg(bdc->dev,
+ "command completed cmd_sts:%x\n", cmd_status);
+ return cmd_status;
+ }
+ udelay(1);
+ } while (timeout--);
+
+ dev_err(bdc->dev,
+ "command operation timedout cmd_status=%d\n", cmd_status);
+
+ return cmd_status;
+}
+
+/* Submits cmd and analyze the return value of bdc_issue_cmd */
+static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc,
+ u32 param0, u32 param1, u32 param2)
+{
+ u32 temp, cmd_status;
+ int reset_bdc = 0;
+ int ret;
+
+ temp = bdc_readl(bdc->regs, BDC_CMDSC);
+ dev_dbg(bdc->dev,
+ "%s:CMDSC:%08x cmdsc:%08x param0=%08x param1=%08x param2=%08x\n",
+ __func__, temp, cmd_sc, param0, param1, param2);
+
+ cmd_status = BDC_CMD_CST(temp);
+ if (cmd_status == BDC_CMDS_BUSY) {
+ dev_err(bdc->dev, "command processor busy: %x\n", cmd_status);
+ return -EBUSY;
+ }
+ ret = bdc_issue_cmd(bdc, cmd_sc, param0, param1, param2);
+ switch (ret) {
+ case BDC_CMDS_SUCC:
+ dev_dbg(bdc->dev, "command completed successfully\n");
+ ret = 0;
+ break;
+
+ case BDC_CMDS_PARA:
+ dev_err(bdc->dev, "command parameter error\n");
+ ret = -EINVAL;
+ break;
+
+ case BDC_CMDS_STAT:
+ dev_err(bdc->dev, "Invalid device/ep state\n");
+ ret = -EINVAL;
+ break;
+
+ case BDC_CMDS_FAIL:
+ dev_err(bdc->dev, "Command failed?\n");
+ ret = -EAGAIN;
+ break;
+
+ case BDC_CMDS_INTL:
+ dev_err(bdc->dev, "BDC Internal error\n");
+ reset_bdc = 1;
+ ret = -ECONNRESET;
+ break;
+
+ case BDC_CMDS_BUSY:
+ dev_err(bdc->dev,
+ "command timedout waited for %dusec\n",
+ BDC_CMD_TIMEOUT);
+ reset_bdc = 1;
+ ret = -ECONNRESET;
+ break;
+ default:
+ dev_dbg(bdc->dev, "Unknown command completion code:%x\n", ret);
+ }
+
+ return ret;
+}
+
+/* Deconfigure the endpoint from HW */
+int bdc_dconfig_ep(struct bdc *bdc, struct bdc_ep *ep)
+{
+ u32 cmd_sc;
+
+ cmd_sc = BDC_SUB_CMD_DRP_EP|BDC_CMD_EPN(ep->ep_num)|BDC_CMD_EPC;
+ dev_dbg(bdc->dev, "%s ep->ep_num =%d cmd_sc=%x\n", __func__,
+ ep->ep_num, cmd_sc);
+
+ return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+}
+
+/* Reinitalize the bdlist after config ep command */
+static void ep_bd_list_reinit(struct bdc_ep *ep)
+{
+ struct bdc *bdc = ep->bdc;
+ struct bdc_bd *bd;
+
+ ep->bd_list.eqp_bdi = 0;
+ ep->bd_list.hwd_bdi = 0;
+ bd = ep->bd_list.bd_table_array[0]->start_bd;
+ dev_dbg(bdc->dev, "%s ep:%p bd:%p\n", __func__, ep, bd);
+ memset(bd, 0, sizeof(struct bdc_bd));
+ bd->offset[3] |= cpu_to_le32(BD_SBF);
+}
+
+/* Configure an endpoint */
+int bdc_config_ep(struct bdc *bdc, struct bdc_ep *ep)
+{
+ const struct usb_ss_ep_comp_descriptor *comp_desc;
+ const struct usb_endpoint_descriptor *desc;
+ u32 param0, param1, param2, cmd_sc;
+ u32 mps, mbs, mul, si;
+ int ret;
+
+ desc = ep->desc;
+ comp_desc = ep->comp_desc;
+ cmd_sc = mul = mbs = param2 = 0;
+ param0 = lower_32_bits(ep->bd_list.bd_table_array[0]->dma);
+ param1 = upper_32_bits(ep->bd_list.bd_table_array[0]->dma);
+ cpu_to_le32s(&param0);
+ cpu_to_le32s(&param1);
+
+ dev_dbg(bdc->dev, "%s: param0=%08x param1=%08x",
+ __func__, param0, param1);
+ si = desc->bInterval;
+ si = clamp_val(si, 1, 16) - 1;
+
+ mps = usb_endpoint_maxp(desc);
+ mps &= 0x7ff;
+ param2 |= mps << MP_SHIFT;
+ param2 |= usb_endpoint_type(desc) << EPT_SHIFT;
+
+ switch (bdc->gadget.speed) {
+ case USB_SPEED_SUPER:
+ if (usb_endpoint_xfer_int(desc) ||
+ usb_endpoint_xfer_isoc(desc)) {
+ param2 |= si;
+ if (usb_endpoint_xfer_isoc(desc) && comp_desc)
+ mul = comp_desc->bmAttributes;
+
+ }
+ param2 |= mul << EPM_SHIFT;
+ if (comp_desc)
+ mbs = comp_desc->bMaxBurst;
+ param2 |= mbs << MB_SHIFT;
+ break;
+
+ case USB_SPEED_HIGH:
+ if (usb_endpoint_xfer_isoc(desc) ||
+ usb_endpoint_xfer_int(desc)) {
+ param2 |= si;
+
+ mbs = (usb_endpoint_maxp(desc) & 0x1800) >> 11;
+ param2 |= mbs << MB_SHIFT;
+ }
+ break;
+
+ case USB_SPEED_FULL:
+ case USB_SPEED_LOW:
+ /* the hardware accepts SI in 125usec range */
+ if (usb_endpoint_xfer_isoc(desc))
+ si += 3;
+
+ /*
+ * FS Int endpoints can have si of 1-255ms but the controller
+ * accepts 2^bInterval*125usec, so convert ms to nearest power
+ * of 2
+ */
+ if (usb_endpoint_xfer_int(desc))
+ si = fls(desc->bInterval * 8) - 1;
+
+ param2 |= si;
+ break;
+ default:
+ dev_err(bdc->dev, "UNKNOWN speed ERR\n");
+ return -EINVAL;
+ }
+
+ cmd_sc |= BDC_CMD_EPC|BDC_CMD_EPN(ep->ep_num)|BDC_SUB_CMD_ADD_EP;
+
+ dev_dbg(bdc->dev, "cmd_sc=%x param2=%08x\n", cmd_sc, param2);
+ ret = bdc_submit_cmd(bdc, cmd_sc, param0, param1, param2);
+ if (ret) {
+ dev_err(bdc->dev, "command failed :%x\n", ret);
+ return ret;
+ }
+ ep_bd_list_reinit(ep);
+
+ return ret;
+}
+
+/*
+ * Change the HW deq pointer, if this command is successful, HW will start
+ * fetching the next bd from address dma_addr.
+ */
+int bdc_ep_bla(struct bdc *bdc, struct bdc_ep *ep, dma_addr_t dma_addr)
+{
+ u32 param0, param1;
+ u32 cmd_sc = 0;
+
+ dev_dbg(bdc->dev, "%s: add=%08llx\n", __func__,
+ (unsigned long long)(dma_addr));
+ param0 = lower_32_bits(dma_addr);
+ param1 = upper_32_bits(dma_addr);
+ cpu_to_le32s(&param0);
+ cpu_to_le32s(&param1);
+
+ cmd_sc |= BDC_CMD_EPN(ep->ep_num)|BDC_CMD_BLA;
+ dev_dbg(bdc->dev, "cmd_sc=%x\n", cmd_sc);
+
+ return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
+}
+
+/* Set the address sent bu Host in SET_ADD request */
+int bdc_address_device(struct bdc *bdc, u32 add)
+{
+ u32 cmd_sc = 0;
+ u32 param2;
+
+ dev_dbg(bdc->dev, "%s: add=%d\n", __func__, add);
+ cmd_sc |= BDC_SUB_CMD_ADD|BDC_CMD_DVC;
+ param2 = add & 0x7f;
+
+ return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
+}
+
+/* Send a Function Wake notification packet using FH command */
+int bdc_function_wake_fh(struct bdc *bdc, u8 intf)
+{
+ u32 param0, param1;
+ u32 cmd_sc = 0;
+
+ param0 = param1 = 0;
+ dev_dbg(bdc->dev, "%s intf=%d\n", __func__, intf);
+ cmd_sc |= BDC_CMD_FH;
+ param0 |= TRA_PACKET;
+ param0 |= (bdc->dev_addr << 25);
+ param1 |= DEV_NOTF_TYPE;
+ param1 |= (FWK_SUBTYPE<<4);
+ dev_dbg(bdc->dev, "param0=%08x param1=%08x\n", param0, param1);
+
+ return bdc_submit_cmd(bdc, cmd_sc, param0, param1, 0);
+}
+
+/* Send a Function Wake notification packet using DNC command */
+int bdc_function_wake(struct bdc *bdc, u8 intf)
+{
+ u32 cmd_sc = 0;
+ u32 param2 = 0;
+
+ dev_dbg(bdc->dev, "%s intf=%d", __func__, intf);
+ param2 |= intf;
+ cmd_sc |= BDC_SUB_CMD_FWK|BDC_CMD_DNC;
+
+ return bdc_submit_cmd(bdc, cmd_sc, 0, 0, param2);
+}
+
+/* Stall the endpoint */
+int bdc_ep_set_stall(struct bdc *bdc, int epnum)
+{
+ u32 cmd_sc = 0;
+
+ dev_dbg(bdc->dev, "%s epnum=%d\n", __func__, epnum);
+ /* issue a stall endpoint command */
+ cmd_sc |= BDC_SUB_CMD_EP_STL | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
+
+ return bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+}
+
+/* resets the endpoint, called when host sends CLEAR_FEATURE(HALT) */
+int bdc_ep_clear_stall(struct bdc *bdc, int epnum)
+{
+ struct bdc_ep *ep;
+ u32 cmd_sc = 0;
+ int ret;
+
+ dev_dbg(bdc->dev, "%s: epnum=%d\n", __func__, epnum);
+ ep = bdc->bdc_ep_array[epnum];
+ /*
+ * If we are not in stalled then stall Endpoint and issue clear stall,
+ * his will reset the seq number for non EP0.
+ */
+ if (epnum != 1) {
+ /* if the endpoint it not stallled */
+ if (!(ep->flags & BDC_EP_STALL)) {
+ ret = bdc_ep_set_stall(bdc, epnum);
+ if (ret)
+ return ret;
+ }
+ }
+ /* Preserve the seq number for ep0 only */
+ if (epnum != 1)
+ cmd_sc |= BDC_CMD_EPO_RST_SN;
+
+ /* issue a reset endpoint command */
+ cmd_sc |= BDC_SUB_CMD_EP_RST | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
+
+ ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+ if (ret) {
+ dev_err(bdc->dev, "command failed:%x\n", ret);
+ return ret;
+ }
+ bdc_notify_xfr(bdc, epnum);
+
+ return ret;
+}
+
+/* Stop the endpoint, called when software wants to dequeue some request */
+int bdc_stop_ep(struct bdc *bdc, int epnum)
+{
+ struct bdc_ep *ep;
+ u32 cmd_sc = 0;
+ int ret;
+
+ ep = bdc->bdc_ep_array[epnum];
+ dev_dbg(bdc->dev, "%s: ep:%s ep->flags:%08x\n", __func__,
+ ep->name, ep->flags);
+ /* Endpoint has to be in running state to execute stop ep command */
+ if (!(ep->flags & BDC_EP_ENABLED)) {
+ dev_err(bdc->dev, "stop endpoint called for disabled ep\n");
+ return -EINVAL;
+ }
+ if ((ep->flags & BDC_EP_STALL) || (ep->flags & BDC_EP_STOP))
+ return 0;
+
+ /* issue a stop endpoint command */
+ cmd_sc |= BDC_CMD_EP0_XSD | BDC_SUB_CMD_EP_STP
+ | BDC_CMD_EPN(epnum) | BDC_CMD_EPO;
+
+ ret = bdc_submit_cmd(bdc, cmd_sc, 0, 0, 0);
+ if (ret) {
+ dev_err(bdc->dev,
+ "stop endpoint command didn't complete:%d ep:%s\n",
+ ret, ep->name);
+ return ret;
+ }
+ ep->flags |= BDC_EP_STOP;
+ bdc_dump_epsts(bdc);
+
+ return ret;
+}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.h b/drivers/usb/gadget/udc/bdc/bdc_cmd.h
new file mode 100644
index 000000000000..61d0e3bf9853
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.h
@@ -0,0 +1,29 @@
+/*
+ * bdc_cmd.h - header for the BDC debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * 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 __LINUX_BDC_CMD_H__
+#define __LINUX_BDC_CMD_H__
+
+/* Command operations */
+int bdc_address_device(struct bdc *, u32);
+int bdc_config_ep(struct bdc *, struct bdc_ep *);
+int bdc_dconfig_ep(struct bdc *, struct bdc_ep *);
+int bdc_stop_ep(struct bdc *, int);
+int bdc_ep_set_stall(struct bdc *, int);
+int bdc_ep_clear_stall(struct bdc *, int);
+int bdc_ep_set_halt(struct bdc_ep *, u32 , int);
+int bdc_ep_bla(struct bdc *, struct bdc_ep *, dma_addr_t);
+int bdc_function_wake(struct bdc*, u8);
+int bdc_function_wake_fh(struct bdc*, u8);
+
+#endif /* __LINUX_BDC_CMD_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_core.c b/drivers/usb/gadget/udc/bdc/bdc_core.c
new file mode 100644
index 000000000000..c6dfef8c7bbc
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
@@ -0,0 +1,533 @@
+/*
+ * bdc_core.c - BRCM BDC USB3.0 device controller core operations
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/of.h>
+#include <linux/moduleparam.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include "bdc.h"
+#include "bdc_dbg.h"
+
+/* Poll till controller status is not OIP */
+static int poll_oip(struct bdc *bdc, int usec)
+{
+ u32 status;
+ /* Poll till STS!= OIP */
+ while (usec) {
+ status = bdc_readl(bdc->regs, BDC_BDCSC);
+ if (BDC_CSTS(status) != BDC_OIP) {
+ dev_dbg(bdc->dev,
+ "poll_oip complete status=%d",
+ BDC_CSTS(status));
+ return 0;
+ }
+ udelay(10);
+ usec -= 10;
+ }
+ dev_err(bdc->dev, "Err: operation timedout BDCSC: 0x%08x\n", status);
+
+ return -ETIMEDOUT;
+}
+
+/* Stop the BDC controller */
+int bdc_stop(struct bdc *bdc)
+{
+ int ret;
+ u32 temp;
+
+ dev_dbg(bdc->dev, "%s ()\n\n", __func__);
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ /* Check if BDC is already halted */
+ if (BDC_CSTS(temp) == BDC_HLT) {
+ dev_vdbg(bdc->dev, "BDC already halted\n");
+ return 0;
+ }
+ temp &= ~BDC_COP_MASK;
+ temp |= BDC_COS|BDC_COP_STP;
+ bdc_writel(bdc->regs, BDC_BDCSC, temp);
+
+ ret = poll_oip(bdc, BDC_COP_TIMEOUT);
+ if (ret)
+ dev_err(bdc->dev, "bdc stop operation failed");
+
+ return ret;
+}
+
+/* Issue a reset to BDC controller */
+int bdc_reset(struct bdc *bdc)
+{
+ u32 temp;
+ int ret;
+
+ dev_dbg(bdc->dev, "%s ()\n", __func__);
+ /* First halt the controller */
+ ret = bdc_stop(bdc);
+ if (ret)
+ return ret;
+
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ temp &= ~BDC_COP_MASK;
+ temp |= BDC_COS|BDC_COP_RST;
+ bdc_writel(bdc->regs, BDC_BDCSC, temp);
+ ret = poll_oip(bdc, BDC_COP_TIMEOUT);
+ if (ret)
+ dev_err(bdc->dev, "bdc reset operation failed");
+
+ return ret;
+}
+
+/* Run the BDC controller */
+int bdc_run(struct bdc *bdc)
+{
+ u32 temp;
+ int ret;
+
+ dev_dbg(bdc->dev, "%s ()\n", __func__);
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ /* if BDC is already in running state then do not do anything */
+ if (BDC_CSTS(temp) == BDC_NOR) {
+ dev_warn(bdc->dev, "bdc is already in running state\n");
+ return 0;
+ }
+ temp &= ~BDC_COP_MASK;
+ temp |= BDC_COP_RUN;
+ temp |= BDC_COS;
+ bdc_writel(bdc->regs, BDC_BDCSC, temp);
+ ret = poll_oip(bdc, BDC_COP_TIMEOUT);
+ if (ret) {
+ dev_err(bdc->dev, "bdc run operation failed:%d", ret);
+ return ret;
+ }
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ if (BDC_CSTS(temp) != BDC_NOR) {
+ dev_err(bdc->dev, "bdc not in normal mode after RUN op :%d\n",
+ BDC_CSTS(temp));
+ return -ESHUTDOWN;
+ }
+
+ return 0;
+}
+
+/*
+ * Present the termination to the host, typically called from upstream port
+ * event with Vbus present =1
+ */
+void bdc_softconn(struct bdc *bdc)
+{
+ u32 uspc;
+
+ uspc = bdc_readl(bdc->regs, BDC_USPC);
+ uspc &= ~BDC_PST_MASK;
+ uspc |= BDC_LINK_STATE_RX_DET;
+ uspc |= BDC_SWS;
+ dev_dbg(bdc->dev, "%s () uspc=%08x\n", __func__, uspc);
+ bdc_writel(bdc->regs, BDC_USPC, uspc);
+}
+
+/* Remove the termination */
+void bdc_softdisconn(struct bdc *bdc)
+{
+ u32 uspc;
+
+ uspc = bdc_readl(bdc->regs, BDC_USPC);
+ uspc |= BDC_SDC;
+ uspc &= ~BDC_SCN;
+ dev_dbg(bdc->dev, "%s () uspc=%x\n", __func__, uspc);
+ bdc_writel(bdc->regs, BDC_USPC, uspc);
+}
+
+/* Set up the scratchpad buffer array and scratchpad buffers, if needed. */
+static int scratchpad_setup(struct bdc *bdc)
+{
+ int sp_buff_size;
+ u32 low32;
+ u32 upp32;
+
+ sp_buff_size = BDC_SPB(bdc_readl(bdc->regs, BDC_BDCCFG0));
+ dev_dbg(bdc->dev, "%s() sp_buff_size=%d\n", __func__, sp_buff_size);
+ if (!sp_buff_size) {
+ dev_dbg(bdc->dev, "Scratchpad buffer not needed\n");
+ return 0;
+ }
+ /* Refer to BDC spec, Table 4 for description of SPB */
+ sp_buff_size = 1 << (sp_buff_size + 5);
+ dev_dbg(bdc->dev, "Allocating %d bytes for scratchpad\n", sp_buff_size);
+ bdc->scratchpad.buff = dma_zalloc_coherent(bdc->dev, sp_buff_size,
+ &bdc->scratchpad.sp_dma, GFP_KERNEL);
+
+ if (!bdc->scratchpad.buff)
+ goto fail;
+
+ bdc->sp_buff_size = sp_buff_size;
+ bdc->scratchpad.size = sp_buff_size;
+ low32 = lower_32_bits(bdc->scratchpad.sp_dma);
+ upp32 = upper_32_bits(bdc->scratchpad.sp_dma);
+ cpu_to_le32s(&low32);
+ cpu_to_le32s(&upp32);
+ bdc_writel(bdc->regs, BDC_SPBBAL, low32);
+ bdc_writel(bdc->regs, BDC_SPBBAH, upp32);
+ return 0;
+
+fail:
+ bdc->scratchpad.buff = NULL;
+
+ return -ENOMEM;
+}
+
+/* Allocate the status report ring */
+static int setup_srr(struct bdc *bdc, int interrupter)
+{
+ dev_dbg(bdc->dev, "%s() NUM_SR_ENTRIES:%d\n", __func__, NUM_SR_ENTRIES);
+ /* Reset the SRR */
+ bdc_writel(bdc->regs, BDC_SRRINT(0), BDC_SRR_RWS | BDC_SRR_RST);
+ bdc->srr.dqp_index = 0;
+ /* allocate the status report descriptors */
+ bdc->srr.sr_bds = dma_zalloc_coherent(
+ bdc->dev,
+ NUM_SR_ENTRIES * sizeof(struct bdc_bd),
+ &bdc->srr.dma_addr,
+ GFP_KERNEL);
+ if (!bdc->srr.sr_bds)
+ return -ENOMEM;
+
+ return 0;
+}
+
+/* Initialize the HW regs and internal data structures */
+static void bdc_mem_init(struct bdc *bdc, bool reinit)
+{
+ u8 size = 0;
+ u32 usb2_pm;
+ u32 low32;
+ u32 upp32;
+ u32 temp;
+
+ dev_dbg(bdc->dev, "%s ()\n", __func__);
+ bdc->ep0_state = WAIT_FOR_SETUP;
+ bdc->dev_addr = 0;
+ bdc->srr.eqp_index = 0;
+ bdc->srr.dqp_index = 0;
+ bdc->zlp_needed = false;
+ bdc->delayed_status = false;
+
+ bdc_writel(bdc->regs, BDC_SPBBAL, bdc->scratchpad.sp_dma);
+ /* Init the SRR */
+ temp = BDC_SRR_RWS | BDC_SRR_RST;
+ /* Reset the SRR */
+ bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
+ dev_dbg(bdc->dev, "bdc->srr.sr_bds =%p\n", bdc->srr.sr_bds);
+ temp = lower_32_bits(bdc->srr.dma_addr);
+ size = fls(NUM_SR_ENTRIES) - 2;
+ temp |= size;
+ dev_dbg(bdc->dev, "SRRBAL[0]=%08x NUM_SR_ENTRIES:%d size:%d\n",
+ temp, NUM_SR_ENTRIES, size);
+
+ low32 = lower_32_bits(temp);
+ upp32 = upper_32_bits(bdc->srr.dma_addr);
+ cpu_to_le32s(&low32);
+ cpu_to_le32s(&upp32);
+
+ /* Write the dma addresses into regs*/
+ bdc_writel(bdc->regs, BDC_SRRBAL(0), low32);
+ bdc_writel(bdc->regs, BDC_SRRBAH(0), upp32);
+
+ temp = bdc_readl(bdc->regs, BDC_SRRINT(0));
+ temp |= BDC_SRR_IE;
+ temp &= ~(BDC_SRR_RST | BDC_SRR_RWS);
+ bdc_writel(bdc->regs, BDC_SRRINT(0), temp);
+
+ /* Set the Interrupt Coalescence ~500 usec */
+ temp = bdc_readl(bdc->regs, BDC_INTCTLS(0));
+ temp &= ~0xffff;
+ temp |= INT_CLS;
+ bdc_writel(bdc->regs, BDC_INTCTLS(0), temp);
+
+ usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
+ dev_dbg(bdc->dev, "usb2_pm=%08x", usb2_pm);
+ /* Enable hardware LPM Enable */
+ usb2_pm |= BDC_HLE;
+ bdc_writel(bdc->regs, BDC_USPPM2, usb2_pm);
+
+ /* readback for debug */
+ usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
+ dev_dbg(bdc->dev, "usb2_pm=%08x\n", usb2_pm);
+
+ /* Disable any unwanted SR's on SRR */
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ /* We don't want Microframe counter wrap SR */
+ temp |= BDC_MASK_MCW;
+ bdc_writel(bdc->regs, BDC_BDCSC, temp);
+
+ /*
+ * In some error cases, driver has to reset the entire BDC controller
+ * in that case reinit is passed as 1
+ */
+ if (reinit) {
+ /* Enable interrupts */
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ temp |= BDC_GIE;
+ bdc_writel(bdc->regs, BDC_BDCSC, temp);
+ /* Init scratchpad to 0 */
+ memset(bdc->scratchpad.buff, 0, bdc->sp_buff_size);
+ /* Initialize SRR to 0 */
+ memset(bdc->srr.sr_bds, 0,
+ NUM_SR_ENTRIES * sizeof(struct bdc_bd));
+ } else {
+ /* One time initiaization only */
+ /* Enable status report function pointers */
+ bdc->sr_handler[0] = bdc_sr_xsf;
+ bdc->sr_handler[1] = bdc_sr_uspc;
+
+ /* EP0 status report function pointers */
+ bdc->sr_xsf_ep0[0] = bdc_xsf_ep0_setup_recv;
+ bdc->sr_xsf_ep0[1] = bdc_xsf_ep0_data_start;
+ bdc->sr_xsf_ep0[2] = bdc_xsf_ep0_status_start;
+ }
+}
+
+/* Free the dynamic memory */
+static void bdc_mem_free(struct bdc *bdc)
+{
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ /* Free SRR */
+ if (bdc->srr.sr_bds)
+ dma_free_coherent(bdc->dev,
+ NUM_SR_ENTRIES * sizeof(struct bdc_bd),
+ bdc->srr.sr_bds, bdc->srr.dma_addr);
+
+ /* Free scratchpad */
+ if (bdc->scratchpad.buff)
+ dma_free_coherent(bdc->dev, bdc->sp_buff_size,
+ bdc->scratchpad.buff, bdc->scratchpad.sp_dma);
+
+ /* Destroy the dma pools */
+ if (bdc->bd_table_pool)
+ dma_pool_destroy(bdc->bd_table_pool);
+
+ /* Free the bdc_ep array */
+ kfree(bdc->bdc_ep_array);
+
+ bdc->srr.sr_bds = NULL;
+ bdc->scratchpad.buff = NULL;
+ bdc->bd_table_pool = NULL;
+ bdc->bdc_ep_array = NULL;
+}
+
+/*
+ * bdc reinit gives a controller reset and reinitialize the registers,
+ * called from disconnect/bus reset scenario's, to ensure proper HW cleanup
+ */
+int bdc_reinit(struct bdc *bdc)
+{
+ int ret;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ ret = bdc_stop(bdc);
+ if (ret)
+ goto out;
+
+ ret = bdc_reset(bdc);
+ if (ret)
+ goto out;
+
+ /* the reinit flag is 1 */
+ bdc_mem_init(bdc, true);
+ ret = bdc_run(bdc);
+out:
+ bdc->reinit = false;
+
+ return ret;
+}
+
+/* Allocate all the dyanmic memory */
+static int bdc_mem_alloc(struct bdc *bdc)
+{
+ u32 page_size;
+ unsigned int num_ieps, num_oeps;
+
+ dev_dbg(bdc->dev,
+ "%s() NUM_BDS_PER_TABLE:%d\n", __func__,
+ NUM_BDS_PER_TABLE);
+ page_size = BDC_PGS(bdc_readl(bdc->regs, BDC_BDCCFG0));
+ /* page size is 2^pgs KB */
+ page_size = 1 << page_size;
+ /* KB */
+ page_size <<= 10;
+ dev_dbg(bdc->dev, "page_size=%d\n", page_size);
+
+ /* Create a pool of bd tables */
+ bdc->bd_table_pool =
+ dma_pool_create("BDC BD tables", bdc->dev, NUM_BDS_PER_TABLE * 16,
+ 16, page_size);
+
+ if (!bdc->bd_table_pool)
+ goto fail;
+
+ if (scratchpad_setup(bdc))
+ goto fail;
+
+ /* read from regs */
+ num_ieps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNIC));
+ num_oeps = NUM_NCS(bdc_readl(bdc->regs, BDC_FSCNOC));
+ /* +2: 1 for ep0 and the other is rsvd i.e. bdc_ep[0] is rsvd */
+ bdc->num_eps = num_ieps + num_oeps + 2;
+ dev_dbg(bdc->dev,
+ "ieps:%d eops:%d num_eps:%d\n",
+ num_ieps, num_oeps, bdc->num_eps);
+ /* allocate array of ep pointers */
+ bdc->bdc_ep_array = kcalloc(bdc->num_eps, sizeof(struct bdc_ep *),
+ GFP_KERNEL);
+ if (!bdc->bdc_ep_array)
+ goto fail;
+
+ dev_dbg(bdc->dev, "Allocating sr report0\n");
+ if (setup_srr(bdc, 0))
+ goto fail;
+
+ return 0;
+fail:
+ dev_warn(bdc->dev, "Couldn't initialize memory\n");
+ bdc_mem_free(bdc);
+
+ return -ENOMEM;
+}
+
+/* opposite to bdc_hw_init */
+static void bdc_hw_exit(struct bdc *bdc)
+{
+ dev_dbg(bdc->dev, "%s ()\n", __func__);
+ bdc_mem_free(bdc);
+}
+
+/* Initialize the bdc HW and memory */
+static int bdc_hw_init(struct bdc *bdc)
+{
+ int ret;
+
+ dev_dbg(bdc->dev, "%s ()\n", __func__);
+ ret = bdc_reset(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "err resetting bdc abort bdc init%d\n", ret);
+ return ret;
+ }
+ ret = bdc_mem_alloc(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "Mem alloc failed, aborting\n");
+ return -ENOMEM;
+ }
+ bdc_mem_init(bdc, 0);
+ bdc_dbg_regs(bdc);
+ dev_dbg(bdc->dev, "HW Init done\n");
+
+ return 0;
+}
+
+static int bdc_probe(struct platform_device *pdev)
+{
+ struct bdc *bdc;
+ struct resource *res;
+ int ret = -ENOMEM;
+ int irq;
+ u32 temp;
+ struct device *dev = &pdev->dev;
+
+ dev_dbg(dev, "%s()\n", __func__);
+ bdc = devm_kzalloc(dev, sizeof(*bdc), GFP_KERNEL);
+ if (!bdc)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ bdc->regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(bdc->regs)) {
+ dev_err(dev, "ioremap error\n");
+ return -ENOMEM;
+ }
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(dev, "platform_get_irq failed:%d\n", irq);
+ return irq;
+ }
+ spin_lock_init(&bdc->lock);
+ platform_set_drvdata(pdev, bdc);
+ bdc->irq = irq;
+ bdc->dev = dev;
+ dev_dbg(bdc->dev, "bdc->regs: %p irq=%d\n", bdc->regs, bdc->irq);
+
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ if ((temp & BDC_P64) &&
+ !dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64))) {
+ dev_dbg(bdc->dev, "Using 64-bit address\n");
+ } else {
+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(bdc->dev, "No suitable DMA config available, abort\n");
+ return -ENOTSUPP;
+ }
+ dev_dbg(bdc->dev, "Using 32-bit address\n");
+ }
+ ret = bdc_hw_init(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "BDC init failure:%d\n", ret);
+ return ret;
+ }
+ ret = bdc_udc_init(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "BDC Gadget init failure:%d\n", ret);
+ goto cleanup;
+ }
+ return 0;
+
+cleanup:
+ bdc_hw_exit(bdc);
+
+ return ret;
+}
+
+static int bdc_remove(struct platform_device *pdev)
+{
+ struct bdc *bdc;
+
+ bdc = platform_get_drvdata(pdev);
+ dev_dbg(bdc->dev, "%s ()\n", __func__);
+ bdc_udc_exit(bdc);
+ bdc_hw_exit(bdc);
+
+ return 0;
+}
+
+static struct platform_driver bdc_driver = {
+ .driver = {
+ .name = BRCM_BDC_NAME,
+ .owner = THIS_MODULE
+ },
+ .probe = bdc_probe,
+ .remove = bdc_remove,
+};
+
+module_platform_driver(bdc_driver);
+MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION(BRCM_BDC_DESC);
diff --git a/drivers/usb/gadget/udc/bdc/bdc_dbg.c b/drivers/usb/gadget/udc/bdc/bdc_dbg.c
new file mode 100644
index 000000000000..5945dbc47825
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_dbg.c
@@ -0,0 +1,123 @@
+/*
+ * bdc_dbg.c - BRCM BDC USB3.0 device controller debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include "bdc.h"
+#include "bdc_dbg.h"
+
+void bdc_dbg_regs(struct bdc *bdc)
+{
+ u32 temp;
+
+ dev_vdbg(bdc->dev, "bdc->regs:%p\n", bdc->regs);
+ temp = bdc_readl(bdc->regs, BDC_BDCCFG0);
+ dev_vdbg(bdc->dev, "bdccfg0:0x%08x\n", temp);
+ temp = bdc_readl(bdc->regs, BDC_BDCCFG1);
+ dev_vdbg(bdc->dev, "bdccfg1:0x%08x\n", temp);
+ temp = bdc_readl(bdc->regs, BDC_BDCCAP0);
+ dev_vdbg(bdc->dev, "bdccap0:0x%08x\n", temp);
+ temp = bdc_readl(bdc->regs, BDC_BDCCAP1);
+ dev_vdbg(bdc->dev, "bdccap1:0x%08x\n", temp);
+ temp = bdc_readl(bdc->regs, BDC_USPC);
+ dev_vdbg(bdc->dev, "uspc:0x%08x\n", temp);
+ temp = bdc_readl(bdc->regs, BDC_DVCSA);
+ dev_vdbg(bdc->dev, "dvcsa:0x%08x\n", temp);
+ temp = bdc_readl(bdc->regs, BDC_DVCSB);
+ dev_vdbg(bdc->dev, "dvcsb:0x%x08\n", temp);
+}
+
+void bdc_dump_epsts(struct bdc *bdc)
+{
+ u32 temp;
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS0(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS0:0x%08x\n", temp);
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS1(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS1:0x%x\n", temp);
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS2(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS2:0x%08x\n", temp);
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS3(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS3:0x%08x\n", temp);
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS4(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS4:0x%08x\n", temp);
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS5(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS5:0x%08x\n", temp);
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS6(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS6:0x%08x\n", temp);
+
+ temp = bdc_readl(bdc->regs, BDC_EPSTS7(0));
+ dev_vdbg(bdc->dev, "BDC_EPSTS7:0x%08x\n", temp);
+}
+
+void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
+{
+ struct bdc_sr *sr;
+ dma_addr_t addr;
+ int i;
+
+ sr = bdc->srr.sr_bds;
+ addr = bdc->srr.dma_addr;
+ dev_vdbg(bdc->dev, "bdc_dbg_srr sr:%p dqp_index:%d\n",
+ sr, bdc->srr.dqp_index);
+ for (i = 0; i < NUM_SR_ENTRIES; i++) {
+ sr = &bdc->srr.sr_bds[i];
+ dev_vdbg(bdc->dev, "%llx %08x %08x %08x %08x\n",
+ (unsigned long long)addr,
+ le32_to_cpu(sr->offset[0]),
+ le32_to_cpu(sr->offset[1]),
+ le32_to_cpu(sr->offset[2]),
+ le32_to_cpu(sr->offset[3]));
+ addr += sizeof(*sr);
+ }
+}
+
+void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
+{
+ struct bd_list *bd_list = &ep->bd_list;
+ struct bd_table *bd_table;
+ struct bdc_bd *bd;
+ int tbi, bdi, gbdi;
+ dma_addr_t dma;
+
+ gbdi = 0;
+ dev_vdbg(bdc->dev,
+ "Dump bd list for %s epnum:%d\n",
+ ep->name, ep->ep_num);
+
+ dev_vdbg(bdc->dev,
+ "tabs:%d max_bdi:%d eqp_bdi:%d hwd_bdi:%d num_bds_table:%d\n",
+ bd_list->num_tabs, bd_list->max_bdi, bd_list->eqp_bdi,
+ bd_list->hwd_bdi, bd_list->num_bds_table);
+
+ for (tbi = 0; tbi < bd_list->num_tabs; tbi++) {
+ bd_table = bd_list->bd_table_array[tbi];
+ for (bdi = 0; bdi < bd_list->num_bds_table; bdi++) {
+ bd = bd_table->start_bd + bdi;
+ dma = bd_table->dma + (sizeof(struct bdc_bd) * bdi);
+ dev_vdbg(bdc->dev,
+ "tbi:%2d bdi:%2d gbdi:%2d virt:%p phys:%llx %08x %08x %08x %08x\n",
+ tbi, bdi, gbdi++, bd, (unsigned long long)dma,
+ le32_to_cpu(bd->offset[0]),
+ le32_to_cpu(bd->offset[1]),
+ le32_to_cpu(bd->offset[2]),
+ le32_to_cpu(bd->offset[3]));
+ }
+ dev_vdbg(bdc->dev, "\n\n");
+ }
+}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_dbg.h b/drivers/usb/gadget/udc/bdc/bdc_dbg.h
new file mode 100644
index 000000000000..338a6c701315
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_dbg.h
@@ -0,0 +1,37 @@
+/*
+ * bdc_dbg.h - header for the BDC debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * 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 __LINUX_BDC_DBG_H__
+#define __LINUX_BDC_DBG_H__
+
+#include "bdc.h"
+
+#ifdef CONFIG_USB_GADGET_VERBOSE
+void bdc_dbg_bd_list(struct bdc *, struct bdc_ep*);
+void bdc_dbg_srr(struct bdc *, u32);
+void bdc_dbg_regs(struct bdc *);
+void bdc_dump_epsts(struct bdc *);
+#else
+static inline void bdc_dbg_regs(struct bdc *bdc)
+{ }
+
+static inline void bdc_dbg_srr(struct bdc *bdc, u32 srr_num)
+{ }
+
+static inline void bdc_dbg_bd_list(struct bdc *bdc, struct bdc_ep *ep)
+{ }
+
+static inline void bdc_dump_epsts(struct bdc *bdc)
+{ }
+#endif /* CONFIG_USB_GADGET_VERBOSE */
+#endif /* __LINUX_BDC_DBG_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c
new file mode 100644
index 000000000000..15da5b1e76c0
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c
@@ -0,0 +1,2023 @@
+/*
+ * bdc_ep.c - BRCM BDC USB3.0 device controller endpoint related functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * Based on drivers under drivers/usb/
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/dmapool.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/unaligned.h>
+#include <linux/platform_device.h>
+#include <linux/usb/composite.h>
+
+#include "bdc.h"
+#include "bdc_ep.h"
+#include "bdc_cmd.h"
+#include "bdc_dbg.h"
+
+static const char * const ep0_state_string[] = {
+ "WAIT_FOR_SETUP",
+ "WAIT_FOR_DATA_START",
+ "WAIT_FOR_DATA_XMIT",
+ "WAIT_FOR_STATUS_START",
+ "WAIT_FOR_STATUS_XMIT",
+ "STATUS_PENDING"
+};
+
+/* Free the bdl during ep disable */
+static void ep_bd_list_free(struct bdc_ep *ep, u32 num_tabs)
+{
+ struct bd_list *bd_list = &ep->bd_list;
+ struct bdc *bdc = ep->bdc;
+ struct bd_table *bd_table;
+ int index;
+
+ dev_dbg(bdc->dev, "%s ep:%s num_tabs:%d\n",
+ __func__, ep->name, num_tabs);
+
+ if (!bd_list->bd_table_array) {
+ dev_dbg(bdc->dev, "%s already freed\n", ep->name);
+ return;
+ }
+ for (index = 0; index < num_tabs; index++) {
+ /*
+ * check if the bd_table struct is allocated ?
+ * if yes, then check if bd memory has been allocated, then
+ * free the dma_pool and also the bd_table struct memory
+ */
+ bd_table = bd_list->bd_table_array[index];
+ dev_dbg(bdc->dev, "bd_table:%p index:%d\n", bd_table, index);
+ if (!bd_table) {
+ dev_dbg(bdc->dev, "bd_table not allocated\n");
+ continue;
+ }
+ if (!bd_table->start_bd) {
+ dev_dbg(bdc->dev, "bd dma pool not allocted\n");
+ continue;
+ }
+
+ dev_dbg(bdc->dev,
+ "Free dma pool start_bd:%p dma:%llx\n",
+ bd_table->start_bd,
+ (unsigned long long)bd_table->dma);
+
+ dma_pool_free(bdc->bd_table_pool,
+ bd_table->start_bd,
+ bd_table->dma);
+ /* Free the bd_table structure */
+ kfree(bd_table);
+ }
+ /* Free the bd table array */
+ kfree(ep->bd_list.bd_table_array);
+}
+
+/*
+ * chain the tables, by insteting a chain bd at the end of prev_table, pointing
+ * to next_table
+ */
+static inline void chain_table(struct bd_table *prev_table,
+ struct bd_table *next_table,
+ u32 bd_p_tab)
+{
+ /* Chain the prev table to next table */
+ prev_table->start_bd[bd_p_tab-1].offset[0] =
+ cpu_to_le32(lower_32_bits(next_table->dma));
+
+ prev_table->start_bd[bd_p_tab-1].offset[1] =
+ cpu_to_le32(upper_32_bits(next_table->dma));
+
+ prev_table->start_bd[bd_p_tab-1].offset[2] =
+ 0x0;
+
+ prev_table->start_bd[bd_p_tab-1].offset[3] =
+ cpu_to_le32(MARK_CHAIN_BD);
+}
+
+/* Allocate the bdl for ep, during config ep */
+static int ep_bd_list_alloc(struct bdc_ep *ep)
+{
+ struct bd_table *prev_table = NULL;
+ int index, num_tabs, bd_p_tab;
+ struct bdc *bdc = ep->bdc;
+ struct bd_table *bd_table;
+ dma_addr_t dma;
+
+ if (usb_endpoint_xfer_isoc(ep->desc))
+ num_tabs = NUM_TABLES_ISOCH;
+ else
+ num_tabs = NUM_TABLES;
+
+ bd_p_tab = NUM_BDS_PER_TABLE;
+ /* if there is only 1 table in bd list then loop chain to self */
+ dev_dbg(bdc->dev,
+ "%s ep:%p num_tabs:%d\n",
+ __func__, ep, num_tabs);
+
+ /* Allocate memory for table array */
+ ep->bd_list.bd_table_array = kzalloc(
+ num_tabs * sizeof(struct bd_table *),
+ GFP_ATOMIC);
+ if (!ep->bd_list.bd_table_array)
+ return -ENOMEM;
+
+ /* Allocate memory for each table */
+ for (index = 0; index < num_tabs; index++) {
+ /* Allocate memory for bd_table structure */
+ bd_table = kzalloc(sizeof(struct bd_table), GFP_ATOMIC);
+ if (!bd_table)
+ goto fail;
+
+ bd_table->start_bd = dma_pool_alloc(bdc->bd_table_pool,
+ GFP_ATOMIC,
+ &dma);
+ if (!bd_table->start_bd)
+ goto fail;
+
+ bd_table->dma = dma;
+
+ dev_dbg(bdc->dev,
+ "index:%d start_bd:%p dma=%08llx prev_table:%p\n",
+ index, bd_table->start_bd,
+ (unsigned long long)bd_table->dma, prev_table);
+
+ ep->bd_list.bd_table_array[index] = bd_table;
+ memset(bd_table->start_bd, 0, bd_p_tab * sizeof(struct bdc_bd));
+ if (prev_table)
+ chain_table(prev_table, bd_table, bd_p_tab);
+
+ prev_table = bd_table;
+ }
+ chain_table(prev_table, ep->bd_list.bd_table_array[0], bd_p_tab);
+ /* Memory allocation is successful, now init the internal fields */
+ ep->bd_list.num_tabs = num_tabs;
+ ep->bd_list.max_bdi = (num_tabs * bd_p_tab) - 1;
+ ep->bd_list.num_tabs = num_tabs;
+ ep->bd_list.num_bds_table = bd_p_tab;
+ ep->bd_list.eqp_bdi = 0;
+ ep->bd_list.hwd_bdi = 0;
+
+ return 0;
+fail:
+ /* Free the bd_table_array, bd_table struct, bd's */
+ ep_bd_list_free(ep, num_tabs);
+
+ return -ENOMEM;
+}
+
+/* returns how many bd's are need for this transfer */
+static inline int bd_needed_req(struct bdc_req *req)
+{
+ int bd_needed = 0;
+ int remaining;
+
+ /* 1 bd needed for 0 byte transfer */
+ if (req->usb_req.length == 0)
+ return 1;
+
+ /* remaining bytes after tranfering all max BD size BD's */
+ remaining = req->usb_req.length % BD_MAX_BUFF_SIZE;
+ if (remaining)
+ bd_needed++;
+
+ /* How many maximum BUFF size BD's ? */
+ remaining = req->usb_req.length / BD_MAX_BUFF_SIZE;
+ bd_needed += remaining;
+
+ return bd_needed;
+}
+
+/* returns the bd index(bdi) corresponding to bd dma address */
+static int bd_add_to_bdi(struct bdc_ep *ep, dma_addr_t bd_dma_addr)
+{
+ struct bd_list *bd_list = &ep->bd_list;
+ dma_addr_t dma_first_bd, dma_last_bd;
+ struct bdc *bdc = ep->bdc;
+ struct bd_table *bd_table;
+ bool found = false;
+ int tbi, bdi;
+
+ dma_first_bd = dma_last_bd = 0;
+ dev_dbg(bdc->dev, "%s %llx\n",
+ __func__, (unsigned long long)bd_dma_addr);
+ /*
+ * Find in which table this bd_dma_addr belongs?, go through the table
+ * array and compare addresses of first and last address of bd of each
+ * table
+ */
+ for (tbi = 0; tbi < bd_list->num_tabs; tbi++) {
+ bd_table = bd_list->bd_table_array[tbi];
+ dma_first_bd = bd_table->dma;
+ dma_last_bd = bd_table->dma +
+ (sizeof(struct bdc_bd) *
+ (bd_list->num_bds_table - 1));
+ dev_dbg(bdc->dev, "dma_first_bd:%llx dma_last_bd:%llx\n",
+ (unsigned long long)dma_first_bd,
+ (unsigned long long)dma_last_bd);
+ if (bd_dma_addr >= dma_first_bd && bd_dma_addr <= dma_last_bd) {
+ found = true;
+ break;
+ }
+ }
+ if (unlikely(!found)) {
+ dev_err(bdc->dev, "%s FATAL err, bd not found\n", __func__);
+ return -EINVAL;
+ }
+ /* Now we know the table, find the bdi */
+ bdi = (bd_dma_addr - dma_first_bd) / sizeof(struct bdc_bd);
+
+ /* return the global bdi, to compare with ep eqp_bdi */
+ return (bdi + (tbi * bd_list->num_bds_table));
+}
+
+/* returns the table index(tbi) of the given bdi */
+static int bdi_to_tbi(struct bdc_ep *ep, int bdi)
+{
+ int tbi;
+
+ tbi = bdi / ep->bd_list.num_bds_table;
+ dev_vdbg(ep->bdc->dev,
+ "bdi:%d num_bds_table:%d tbi:%d\n",
+ bdi, ep->bd_list.num_bds_table, tbi);
+
+ return tbi;
+}
+
+/* Find the bdi last bd in the transfer */
+static inline int find_end_bdi(struct bdc_ep *ep, int next_hwd_bdi)
+{
+ int end_bdi;
+
+ end_bdi = next_hwd_bdi - 1;
+ if (end_bdi < 0)
+ end_bdi = ep->bd_list.max_bdi - 1;
+ else if ((end_bdi % (ep->bd_list.num_bds_table-1)) == 0)
+ end_bdi--;
+
+ return end_bdi;
+}
+
+/*
+ * How many transfer bd's are available on this ep bdl, chain bds are not
+ * counted in available bds
+ */
+static int bd_available_ep(struct bdc_ep *ep)
+{
+ struct bd_list *bd_list = &ep->bd_list;
+ int available1, available2;
+ struct bdc *bdc = ep->bdc;
+ int chain_bd1, chain_bd2;
+ int available_bd = 0;
+
+ available1 = available2 = chain_bd1 = chain_bd2 = 0;
+ /* if empty then we have all bd's available - number of chain bd's */
+ if (bd_list->eqp_bdi == bd_list->hwd_bdi)
+ return bd_list->max_bdi - bd_list->num_tabs;
+
+ /*
+ * Depending upon where eqp and dqp pointers are, caculate number
+ * of avaialble bd's
+ */
+ if (bd_list->hwd_bdi < bd_list->eqp_bdi) {
+ /* available bd's are from eqp..max_bds + 0..dqp - chain_bds */
+ available1 = bd_list->max_bdi - bd_list->eqp_bdi;
+ available2 = bd_list->hwd_bdi;
+ chain_bd1 = available1 / bd_list->num_bds_table;
+ chain_bd2 = available2 / bd_list->num_bds_table;
+ dev_vdbg(bdc->dev, "chain_bd1:%d chain_bd2:%d\n",
+ chain_bd1, chain_bd2);
+ available_bd = available1 + available2 - chain_bd1 - chain_bd2;
+ } else {
+ /* available bd's are from eqp..dqp - number of chain bd's */
+ available1 = bd_list->hwd_bdi - bd_list->eqp_bdi;
+ /* if gap between eqp and dqp is less than NUM_BDS_PER_TABLE */
+ if ((bd_list->hwd_bdi - bd_list->eqp_bdi)
+ <= bd_list->num_bds_table) {
+ /* If there any chain bd in between */
+ if (!(bdi_to_tbi(ep, bd_list->hwd_bdi)
+ == bdi_to_tbi(ep, bd_list->eqp_bdi))) {
+ available_bd = available1 - 1;
+ }
+ } else {
+ chain_bd1 = available1 / bd_list->num_bds_table;
+ available_bd = available1 - chain_bd1;
+ }
+ }
+ /*
+ * we need to keep one extra bd to check if ring is full or empty so
+ * reduce by 1
+ */
+ available_bd--;
+ dev_vdbg(bdc->dev, "available_bd:%d\n", available_bd);
+
+ return available_bd;
+}
+
+/* Notify the hardware after queueing the bd to bdl */
+void bdc_notify_xfr(struct bdc *bdc, u32 epnum)
+{
+ struct bdc_ep *ep = bdc->bdc_ep_array[epnum];
+
+ dev_vdbg(bdc->dev, "%s epnum:%d\n", __func__, epnum);
+ /*
+ * We don't have anyway to check if ep state is running,
+ * except the software flags.
+ */
+ if (unlikely(ep->flags & BDC_EP_STOP))
+ ep->flags &= ~BDC_EP_STOP;
+
+ bdc_writel(bdc->regs, BDC_XSFNTF, epnum);
+}
+
+/* returns the bd corresponding to bdi */
+static struct bdc_bd *bdi_to_bd(struct bdc_ep *ep, int bdi)
+{
+ int tbi = bdi_to_tbi(ep, bdi);
+ int local_bdi = 0;
+
+ local_bdi = bdi - (tbi * ep->bd_list.num_bds_table);
+ dev_vdbg(ep->bdc->dev,
+ "%s bdi:%d local_bdi:%d\n",
+ __func__, bdi, local_bdi);
+
+ return (ep->bd_list.bd_table_array[tbi]->start_bd + local_bdi);
+}
+
+/* Advance the enqueue pointer */
+static void ep_bdlist_eqp_adv(struct bdc_ep *ep)
+{
+ ep->bd_list.eqp_bdi++;
+ /* if it's chain bd, then move to next */
+ if (((ep->bd_list.eqp_bdi + 1) % ep->bd_list.num_bds_table) == 0)
+ ep->bd_list.eqp_bdi++;
+
+ /* if the eqp is pointing to last + 1 then move back to 0 */
+ if (ep->bd_list.eqp_bdi == (ep->bd_list.max_bdi + 1))
+ ep->bd_list.eqp_bdi = 0;
+}
+
+/* Setup the first bd for ep0 transfer */
+static int setup_first_bd_ep0(struct bdc *bdc, struct bdc_req *req, u32 *dword3)
+{
+ u16 wValue;
+ u32 req_len;
+
+ req->ep->dir = 0;
+ req_len = req->usb_req.length;
+ switch (bdc->ep0_state) {
+ case WAIT_FOR_DATA_START:
+ *dword3 |= BD_TYPE_DS;
+ if (bdc->setup_pkt.bRequestType & USB_DIR_IN)
+ *dword3 |= BD_DIR_IN;
+
+ /* check if zlp will be needed */
+ wValue = le16_to_cpu(bdc->setup_pkt.wValue);
+ if ((wValue > req_len) &&
+ (req_len % bdc->gadget.ep0->maxpacket == 0)) {
+ dev_dbg(bdc->dev, "ZLP needed wVal:%d len:%d MaxP:%d\n",
+ wValue, req_len,
+ bdc->gadget.ep0->maxpacket);
+ bdc->zlp_needed = true;
+ }
+ break;
+
+ case WAIT_FOR_STATUS_START:
+ *dword3 |= BD_TYPE_SS;
+ if (!le16_to_cpu(bdc->setup_pkt.wLength) ||
+ !(bdc->setup_pkt.bRequestType & USB_DIR_IN))
+ *dword3 |= BD_DIR_IN;
+ break;
+ default:
+ dev_err(bdc->dev,
+ "Unknown ep0 state for queueing bd ep0_state:%s\n",
+ ep0_state_string[bdc->ep0_state]);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Setup the bd dma descriptor for a given request */
+static int setup_bd_list_xfr(struct bdc *bdc, struct bdc_req *req, int num_bds)
+{
+ dma_addr_t buf_add = req->usb_req.dma;
+ u32 maxp, tfs, dword2, dword3;
+ struct bd_transfer *bd_xfr;
+ struct bd_list *bd_list;
+ struct bdc_ep *ep;
+ struct bdc_bd *bd;
+ int ret, bdnum;
+ u32 req_len;
+
+ ep = req->ep;
+ bd_list = &ep->bd_list;
+ bd_xfr = &req->bd_xfr;
+ bd_xfr->req = req;
+ bd_xfr->start_bdi = bd_list->eqp_bdi;
+ bd = bdi_to_bd(ep, bd_list->eqp_bdi);
+ req_len = req->usb_req.length;
+ maxp = usb_endpoint_maxp(ep->desc) & 0x7ff;
+ tfs = roundup(req->usb_req.length, maxp);
+ tfs = tfs/maxp;
+ dev_vdbg(bdc->dev, "%s ep:%s num_bds:%d tfs:%d r_len:%d bd:%p\n",
+ __func__, ep->name, num_bds, tfs, req_len, bd);
+
+ for (bdnum = 0; bdnum < num_bds; bdnum++) {
+ dword2 = dword3 = 0;
+ /* First bd */
+ if (!bdnum) {
+ dword3 |= BD_SOT|BD_SBF|(tfs<<BD_TFS_SHIFT);
+ dword2 |= BD_LTF;
+ /* format of first bd for ep0 is different than other */
+ if (ep->ep_num == 1)
+ ret = setup_first_bd_ep0(bdc, req, &dword3);
+ if (ret)
+ return ret;
+ }
+ if (!req->ep->dir)
+ dword3 |= BD_ISP;
+
+ if (req_len > BD_MAX_BUFF_SIZE) {
+ dword2 |= BD_MAX_BUFF_SIZE;
+ req_len -= BD_MAX_BUFF_SIZE;
+ } else {
+ /* this should be the last bd */
+ dword2 |= req_len;
+ dword3 |= BD_IOC;
+ dword3 |= BD_EOT;
+ }
+ /* Currently only 1 INT target is supported */
+ dword2 |= BD_INTR_TARGET(0);
+ bd = bdi_to_bd(ep, ep->bd_list.eqp_bdi);
+ if (unlikely(!bd)) {
+ dev_err(bdc->dev, "Err bd pointing to wrong addr\n");
+ return -EINVAL;
+ }
+ /* write bd */
+ bd->offset[0] = cpu_to_le32(lower_32_bits(buf_add));
+ bd->offset[1] = cpu_to_le32(upper_32_bits(buf_add));
+ bd->offset[2] = cpu_to_le32(dword2);
+ bd->offset[3] = cpu_to_le32(dword3);
+ /* advance eqp pointer */
+ ep_bdlist_eqp_adv(ep);
+ /* advance the buff pointer */
+ buf_add += BD_MAX_BUFF_SIZE;
+ dev_vdbg(bdc->dev, "buf_add:%08llx req_len:%d bd:%p eqp:%d\n",
+ (unsigned long long)buf_add, req_len, bd,
+ ep->bd_list.eqp_bdi);
+ bd = bdi_to_bd(ep, ep->bd_list.eqp_bdi);
+ bd->offset[3] = cpu_to_le32(BD_SBF);
+ }
+ /* clear the STOP BD fetch bit from the first bd of this xfr */
+ bd = bdi_to_bd(ep, bd_xfr->start_bdi);
+ bd->offset[3] &= cpu_to_le32(~BD_SBF);
+ /* the new eqp will be next hw dqp */
+ bd_xfr->num_bds = num_bds;
+ bd_xfr->next_hwd_bdi = ep->bd_list.eqp_bdi;
+ /* everything is written correctly before notifying the HW */
+ wmb();
+
+ return 0;
+}
+
+/* Queue the xfr */
+static int bdc_queue_xfr(struct bdc *bdc, struct bdc_req *req)
+{
+ int num_bds, bd_available;
+ struct bdc_ep *ep;
+ int ret;
+
+ ep = req->ep;
+ dev_dbg(bdc->dev, "%s req:%p\n", __func__, req);
+ dev_dbg(bdc->dev, "eqp_bdi:%d hwd_bdi:%d\n",
+ ep->bd_list.eqp_bdi, ep->bd_list.hwd_bdi);
+
+ num_bds = bd_needed_req(req);
+ bd_available = bd_available_ep(ep);
+
+ /* how many bd's are avaialble on ep */
+ if (num_bds > bd_available)
+ return -ENOMEM;
+
+ ret = setup_bd_list_xfr(bdc, req, num_bds);
+ if (ret)
+ return ret;
+ list_add_tail(&req->queue, &ep->queue);
+ bdc_dbg_bd_list(bdc, ep);
+ bdc_notify_xfr(bdc, ep->ep_num);
+
+ return 0;
+}
+
+/* callback to gadget layer when xfr completes */
+static void bdc_req_complete(struct bdc_ep *ep, struct bdc_req *req,
+ int status)
+{
+ struct bdc *bdc = ep->bdc;
+
+ if (req == NULL || &req->queue == NULL || &req->usb_req == NULL)
+ return;
+
+ dev_dbg(bdc->dev, "%s ep:%s status:%d\n", __func__, ep->name, status);
+ list_del(&req->queue);
+ req->usb_req.status = status;
+ usb_gadget_unmap_request(&bdc->gadget, &req->usb_req, ep->dir);
+ if (req->usb_req.complete) {
+ spin_unlock(&bdc->lock);
+ usb_gadget_giveback_request(&ep->usb_ep, &req->usb_req);
+ spin_lock(&bdc->lock);
+ }
+}
+
+/* Disable the endpoint */
+int bdc_ep_disable(struct bdc_ep *ep)
+{
+ struct bdc_req *req;
+ struct bdc *bdc;
+ int ret;
+
+ ret = 0;
+ bdc = ep->bdc;
+ dev_dbg(bdc->dev, "%s() ep->ep_num=%d\n", __func__, ep->ep_num);
+ /* Stop the endpoint */
+ ret = bdc_stop_ep(bdc, ep->ep_num);
+
+ /*
+ * Intentionally don't check the ret value of stop, it can fail in
+ * disconnect scenarios, continue with dconfig
+ */
+ /* de-queue any pending requests */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct bdc_req,
+ queue);
+ bdc_req_complete(ep, req, -ESHUTDOWN);
+ }
+ /* deconfigure the endpoint */
+ ret = bdc_dconfig_ep(bdc, ep);
+ if (ret)
+ dev_warn(bdc->dev,
+ "dconfig fail but continue with memory free");
+
+ ep->flags = 0;
+ /* ep0 memory is not freed, but reused on next connect sr */
+ if (ep->ep_num == 1)
+ return 0;
+
+ /* Free the bdl memory */
+ ep_bd_list_free(ep, ep->bd_list.num_tabs);
+ ep->desc = NULL;
+ ep->comp_desc = NULL;
+ ep->usb_ep.desc = NULL;
+ ep->ep_type = 0;
+
+ return ret;
+}
+
+/* Enable the ep */
+int bdc_ep_enable(struct bdc_ep *ep)
+{
+ struct bdc *bdc;
+ int ret = 0;
+
+ bdc = ep->bdc;
+ dev_dbg(bdc->dev, "%s NUM_TABLES:%d %d\n",
+ __func__, NUM_TABLES, NUM_TABLES_ISOCH);
+
+ ret = ep_bd_list_alloc(ep);
+ if (ret) {
+ dev_err(bdc->dev, "ep bd list allocation failed:%d\n", ret);
+ return -ENOMEM;
+ }
+ bdc_dbg_bd_list(bdc, ep);
+ /* only for ep0: config ep is called for ep0 from connect event */
+ ep->flags |= BDC_EP_ENABLED;
+ if (ep->ep_num == 1)
+ return ret;
+
+ /* Issue a configure endpoint command */
+ ret = bdc_config_ep(bdc, ep);
+ if (ret)
+ return ret;
+
+ ep->usb_ep.maxpacket = usb_endpoint_maxp(ep->desc);
+ ep->usb_ep.desc = ep->desc;
+ ep->usb_ep.comp_desc = ep->comp_desc;
+ ep->ep_type = usb_endpoint_type(ep->desc);
+ ep->flags |= BDC_EP_ENABLED;
+
+ return 0;
+}
+
+/* EP0 related code */
+
+/* Queue a status stage BD */
+static int ep0_queue_status_stage(struct bdc *bdc)
+{
+ struct bdc_req *status_req;
+ struct bdc_ep *ep;
+
+ status_req = &bdc->status_req;
+ ep = bdc->bdc_ep_array[1];
+ status_req->ep = ep;
+ status_req->usb_req.length = 0;
+ status_req->usb_req.status = -EINPROGRESS;
+ status_req->usb_req.actual = 0;
+ status_req->usb_req.complete = NULL;
+ bdc_queue_xfr(bdc, status_req);
+
+ return 0;
+}
+
+/* Queue xfr on ep0 */
+static int ep0_queue(struct bdc_ep *ep, struct bdc_req *req)
+{
+ struct bdc *bdc;
+ int ret;
+
+ bdc = ep->bdc;
+ dev_dbg(bdc->dev, "%s()\n", __func__);
+ req->usb_req.actual = 0;
+ req->usb_req.status = -EINPROGRESS;
+ req->epnum = ep->ep_num;
+
+ if (bdc->delayed_status) {
+ bdc->delayed_status = false;
+ /* if status stage was delayed? */
+ if (bdc->ep0_state == WAIT_FOR_STATUS_START) {
+ /* Queue a status stage BD */
+ ep0_queue_status_stage(bdc);
+ bdc->ep0_state = WAIT_FOR_STATUS_XMIT;
+ return 0;
+ }
+ } else {
+ /*
+ * if delayed status is false and 0 length transfer is requested
+ * i.e. for status stage of some setup request, then just
+ * return from here the status stage is queued independently
+ */
+ if (req->usb_req.length == 0)
+ return 0;
+
+ }
+ ret = usb_gadget_map_request(&bdc->gadget, &req->usb_req, ep->dir);
+ if (ret) {
+ dev_err(bdc->dev, "dma mapping failed %s\n", ep->name);
+ return ret;
+ }
+
+ return bdc_queue_xfr(bdc, req);
+}
+
+/* Queue data stage */
+static int ep0_queue_data_stage(struct bdc *bdc)
+{
+ struct usb_request *ep0_usb_req;
+ struct bdc_ep *ep;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ ep0_usb_req = &bdc->ep0_req.usb_req;
+ ep = bdc->bdc_ep_array[1];
+ bdc->ep0_req.ep = ep;
+ bdc->ep0_req.usb_req.complete = NULL;
+
+ return ep0_queue(ep, &bdc->ep0_req);
+}
+
+/* Queue req on ep */
+static int ep_queue(struct bdc_ep *ep, struct bdc_req *req)
+{
+ struct bdc *bdc;
+ int ret = 0;
+
+ bdc = ep->bdc;
+ if (!req || !ep || !ep->usb_ep.desc)
+ return -EINVAL;
+
+ req->usb_req.actual = 0;
+ req->usb_req.status = -EINPROGRESS;
+ req->epnum = ep->ep_num;
+
+ ret = usb_gadget_map_request(&bdc->gadget, &req->usb_req, ep->dir);
+ if (ret) {
+ dev_err(bdc->dev, "dma mapping failed\n");
+ return ret;
+ }
+
+ return bdc_queue_xfr(bdc, req);
+}
+
+/* Dequeue a request from ep */
+static int ep_dequeue(struct bdc_ep *ep, struct bdc_req *req)
+{
+ int start_bdi, end_bdi, tbi, eqp_bdi, curr_hw_dqpi;
+ bool start_pending, end_pending;
+ bool first_remove = false;
+ struct bdc_req *first_req;
+ struct bdc_bd *bd_start;
+ struct bd_table *table;
+ dma_addr_t next_bd_dma;
+ u64 deq_ptr_64 = 0;
+ struct bdc *bdc;
+ u32 tmp_32;
+ int ret;
+
+ bdc = ep->bdc;
+ start_pending = end_pending = false;
+ eqp_bdi = ep->bd_list.eqp_bdi - 1;
+
+ if (eqp_bdi < 0)
+ eqp_bdi = ep->bd_list.max_bdi;
+
+ start_bdi = req->bd_xfr.start_bdi;
+ end_bdi = find_end_bdi(ep, req->bd_xfr.next_hwd_bdi);
+
+ dev_dbg(bdc->dev, "%s ep:%s start:%d end:%d\n",
+ __func__, ep->name, start_bdi, end_bdi);
+ dev_dbg(bdc->dev, "ep_dequeue ep=%p ep->desc=%p\n",
+ ep, (void *)ep->usb_ep.desc);
+ /* Stop the ep to see where the HW is ? */
+ ret = bdc_stop_ep(bdc, ep->ep_num);
+ /* if there is an issue with stopping ep, then no need to go further */
+ if (ret)
+ return 0;
+
+ /*
+ * After endpoint is stopped, there can be 3 cases, the request
+ * is processed, pending or in the middle of processing
+ */
+
+ /* The current hw dequeue pointer */
+ tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS0(0));
+ deq_ptr_64 = tmp_32;
+ tmp_32 = bdc_readl(bdc->regs, BDC_EPSTS0(1));
+ deq_ptr_64 |= ((u64)tmp_32 << 32);
+
+ /* we have the dma addr of next bd that will be fetched by hardware */
+ curr_hw_dqpi = bd_add_to_bdi(ep, deq_ptr_64);
+ if (curr_hw_dqpi < 0)
+ return curr_hw_dqpi;
+
+ /*
+ * curr_hw_dqpi points to actual dqp of HW and HW owns bd's from
+ * curr_hw_dqbdi..eqp_bdi.
+ */
+
+ /* Check if start_bdi and end_bdi are in range of HW owned BD's */
+ if (curr_hw_dqpi > eqp_bdi) {
+ /* there is a wrap from last to 0 */
+ if (start_bdi >= curr_hw_dqpi || start_bdi <= eqp_bdi) {
+ start_pending = true;
+ end_pending = true;
+ } else if (end_bdi >= curr_hw_dqpi || end_bdi <= eqp_bdi) {
+ end_pending = true;
+ }
+ } else {
+ if (start_bdi >= curr_hw_dqpi) {
+ start_pending = true;
+ end_pending = true;
+ } else if (end_bdi >= curr_hw_dqpi) {
+ end_pending = true;
+ }
+ }
+ dev_dbg(bdc->dev,
+ "start_pending:%d end_pending:%d speed:%d\n",
+ start_pending, end_pending, bdc->gadget.speed);
+
+ /* If both start till end are processes, we cannot deq req */
+ if (!start_pending && !end_pending)
+ return -EINVAL;
+
+ /*
+ * if ep_dequeue is called after disconnect then just return
+ * success from here
+ */
+ if (bdc->gadget.speed == USB_SPEED_UNKNOWN)
+ return 0;
+ tbi = bdi_to_tbi(ep, req->bd_xfr.next_hwd_bdi);
+ table = ep->bd_list.bd_table_array[tbi];
+ next_bd_dma = table->dma +
+ sizeof(struct bdc_bd)*(req->bd_xfr.next_hwd_bdi -
+ tbi * ep->bd_list.num_bds_table);
+
+ first_req = list_first_entry(&ep->queue, struct bdc_req,
+ queue);
+
+ if (req == first_req)
+ first_remove = true;
+
+ /*
+ * Due to HW limitation we need to bypadd chain bd's and issue ep_bla,
+ * incase if start is pending this is the first request in the list
+ * then issue ep_bla instead of marking as chain bd
+ */
+ if (start_pending && !first_remove) {
+ /*
+ * Mark the start bd as Chain bd, and point the chain
+ * bd to next_bd_dma
+ */
+ bd_start = bdi_to_bd(ep, start_bdi);
+ bd_start->offset[0] = cpu_to_le32(lower_32_bits(next_bd_dma));
+ bd_start->offset[1] = cpu_to_le32(upper_32_bits(next_bd_dma));
+ bd_start->offset[2] = 0x0;
+ bd_start->offset[3] = cpu_to_le32(MARK_CHAIN_BD);
+ bdc_dbg_bd_list(bdc, ep);
+ } else if (end_pending) {
+ /*
+ * The transfer is stopped in the middle, move the
+ * HW deq pointer to next_bd_dma
+ */
+ ret = bdc_ep_bla(bdc, ep, next_bd_dma);
+ if (ret) {
+ dev_err(bdc->dev, "error in ep_bla:%d\n", ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+/* Halt/Clear the ep based on value */
+static int ep_set_halt(struct bdc_ep *ep, u32 value)
+{
+ struct bdc *bdc;
+ int ret;
+
+ bdc = ep->bdc;
+ dev_dbg(bdc->dev, "%s ep:%s value=%d\n", __func__, ep->name, value);
+
+ if (value) {
+ dev_dbg(bdc->dev, "Halt\n");
+ if (ep->ep_num == 1)
+ bdc->ep0_state = WAIT_FOR_SETUP;
+
+ ret = bdc_ep_set_stall(bdc, ep->ep_num);
+ if (ret)
+ dev_err(bdc->dev, "failed to %s STALL on %s\n",
+ value ? "set" : "clear", ep->name);
+ else
+ ep->flags |= BDC_EP_STALL;
+ } else {
+ /* Clear */
+ dev_dbg(bdc->dev, "Before Clear\n");
+ ret = bdc_ep_clear_stall(bdc, ep->ep_num);
+ if (ret)
+ dev_err(bdc->dev, "failed to %s STALL on %s\n",
+ value ? "set" : "clear", ep->name);
+ else
+ ep->flags &= ~BDC_EP_STALL;
+ dev_dbg(bdc->dev, "After Clear\n");
+ }
+
+ return ret;
+}
+
+/* Free all the ep */
+void bdc_free_ep(struct bdc *bdc)
+{
+ struct bdc_ep *ep;
+ u8 epnum;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ for (epnum = 1; epnum < bdc->num_eps; epnum++) {
+ ep = bdc->bdc_ep_array[epnum];
+ if (!ep)
+ continue;
+
+ if (ep->flags & BDC_EP_ENABLED)
+ ep_bd_list_free(ep, ep->bd_list.num_tabs);
+
+ /* ep0 is not in this gadget list */
+ if (epnum != 1)
+ list_del(&ep->usb_ep.ep_list);
+
+ kfree(ep);
+ }
+}
+
+/* USB2 spec, section 7.1.20 */
+static int bdc_set_test_mode(struct bdc *bdc)
+{
+ u32 usb2_pm;
+
+ usb2_pm = bdc_readl(bdc->regs, BDC_USPPM2);
+ usb2_pm &= ~BDC_PTC_MASK;
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ switch (bdc->test_mode) {
+ case TEST_J:
+ case TEST_K:
+ case TEST_SE0_NAK:
+ case TEST_PACKET:
+ case TEST_FORCE_EN:
+ usb2_pm |= bdc->test_mode << 28;
+ break;
+ default:
+ return -EINVAL;
+ }
+ dev_dbg(bdc->dev, "usb2_pm=%08x", usb2_pm);
+ bdc_writel(bdc->regs, BDC_USPPM2, usb2_pm);
+
+ return 0;
+}
+
+/*
+ * Helper function to handle Transfer status report with status as either
+ * success or short
+ */
+static void handle_xsr_succ_status(struct bdc *bdc, struct bdc_ep *ep,
+ struct bdc_sr *sreport)
+{
+ int short_bdi, start_bdi, end_bdi, max_len_bds, chain_bds;
+ struct bd_list *bd_list = &ep->bd_list;
+ int actual_length, length_short;
+ struct bd_transfer *bd_xfr;
+ struct bdc_bd *short_bd;
+ struct bdc_req *req;
+ u64 deq_ptr_64 = 0;
+ int status = 0;
+ int sr_status;
+ u32 tmp_32;
+
+ dev_dbg(bdc->dev, "%s ep:%p\n", __func__, ep);
+ bdc_dbg_srr(bdc, 0);
+ /* do not process thie sr if ignore flag is set */
+ if (ep->ignore_next_sr) {
+ ep->ignore_next_sr = false;
+ return;
+ }
+
+ if (unlikely(list_empty(&ep->queue))) {
+ dev_warn(bdc->dev, "xfr srr with no BD's queued\n");
+ return;
+ }
+ req = list_entry(ep->queue.next, struct bdc_req,
+ queue);
+
+ bd_xfr = &req->bd_xfr;
+ sr_status = XSF_STS(le32_to_cpu(sreport->offset[3]));
+
+ /*
+ * sr_status is short and this transfer has more than 1 bd then it needs
+ * special handling, this is only applicable for bulk and ctrl
+ */
+ if (sr_status == XSF_SHORT && bd_xfr->num_bds > 1) {
+ /*
+ * This is multi bd xfr, lets see which bd
+ * caused short transfer and how many bytes have been
+ * transferred so far.
+ */
+ tmp_32 = le32_to_cpu(sreport->offset[0]);
+ deq_ptr_64 = tmp_32;
+ tmp_32 = le32_to_cpu(sreport->offset[1]);
+ deq_ptr_64 |= ((u64)tmp_32 << 32);
+ short_bdi = bd_add_to_bdi(ep, deq_ptr_64);
+ if (unlikely(short_bdi < 0))
+ dev_warn(bdc->dev, "bd doesn't exist?\n");
+
+ start_bdi = bd_xfr->start_bdi;
+ /*
+ * We know the start_bdi and short_bdi, how many xfr
+ * bds in between
+ */
+ if (start_bdi <= short_bdi) {
+ max_len_bds = short_bdi - start_bdi;
+ if (max_len_bds <= bd_list->num_bds_table) {
+ if (!(bdi_to_tbi(ep, start_bdi) ==
+ bdi_to_tbi(ep, short_bdi)))
+ max_len_bds--;
+ } else {
+ chain_bds = max_len_bds/bd_list->num_bds_table;
+ max_len_bds -= chain_bds;
+ }
+ } else {
+ /* there is a wrap in the ring within a xfr */
+ chain_bds = (bd_list->max_bdi - start_bdi)/
+ bd_list->num_bds_table;
+ chain_bds += short_bdi/bd_list->num_bds_table;
+ max_len_bds = bd_list->max_bdi - start_bdi;
+ max_len_bds += short_bdi;
+ max_len_bds -= chain_bds;
+ }
+ /* max_len_bds is the number of full length bds */
+ end_bdi = find_end_bdi(ep, bd_xfr->next_hwd_bdi);
+ if (!(end_bdi == short_bdi))
+ ep->ignore_next_sr = true;
+
+ actual_length = max_len_bds * BD_MAX_BUFF_SIZE;
+ short_bd = bdi_to_bd(ep, short_bdi);
+ /* length queued */
+ length_short = le32_to_cpu(short_bd->offset[2]) & 0x1FFFFF;
+ /* actual length trensfered */
+ length_short -= SR_BD_LEN(le32_to_cpu(sreport->offset[2]));
+ actual_length += length_short;
+ req->usb_req.actual = actual_length;
+ } else {
+ req->usb_req.actual = req->usb_req.length -
+ SR_BD_LEN(le32_to_cpu(sreport->offset[2]));
+ dev_dbg(bdc->dev,
+ "len=%d actual=%d bd_xfr->next_hwd_bdi:%d\n",
+ req->usb_req.length, req->usb_req.actual,
+ bd_xfr->next_hwd_bdi);
+ }
+
+ /* Update the dequeue pointer */
+ ep->bd_list.hwd_bdi = bd_xfr->next_hwd_bdi;
+ if (req->usb_req.actual < req->usb_req.length) {
+ dev_dbg(bdc->dev, "short xfr on %d\n", ep->ep_num);
+ if (req->usb_req.short_not_ok)
+ status = -EREMOTEIO;
+ }
+ bdc_req_complete(ep, bd_xfr->req, status);
+}
+
+/* EP0 setup related packet handlers */
+
+/*
+ * Setup packet received, just store the packet and process on next DS or SS
+ * started SR
+ */
+void bdc_xsf_ep0_setup_recv(struct bdc *bdc, struct bdc_sr *sreport)
+{
+ struct usb_ctrlrequest *setup_pkt;
+ u32 len;
+
+ dev_dbg(bdc->dev,
+ "%s ep0_state:%s\n",
+ __func__, ep0_state_string[bdc->ep0_state]);
+ /* Store received setup packet */
+ setup_pkt = &bdc->setup_pkt;
+ memcpy(setup_pkt, &sreport->offset[0], sizeof(*setup_pkt));
+ len = le16_to_cpu(setup_pkt->wLength);
+ if (!len)
+ bdc->ep0_state = WAIT_FOR_STATUS_START;
+ else
+ bdc->ep0_state = WAIT_FOR_DATA_START;
+
+
+ dev_dbg(bdc->dev,
+ "%s exit ep0_state:%s\n",
+ __func__, ep0_state_string[bdc->ep0_state]);
+}
+
+/* Stall ep0 */
+static void ep0_stall(struct bdc *bdc)
+{
+ struct bdc_ep *ep = bdc->bdc_ep_array[1];
+ struct bdc_req *req;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ bdc->delayed_status = false;
+ ep_set_halt(ep, 1);
+
+ /* de-queue any pendig requests */
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct bdc_req,
+ queue);
+ bdc_req_complete(ep, req, -ESHUTDOWN);
+ }
+}
+
+/* SET_ADD handlers */
+static int ep0_set_address(struct bdc *bdc, struct usb_ctrlrequest *ctrl)
+{
+ enum usb_device_state state = bdc->gadget.state;
+ int ret = 0;
+ u32 addr;
+
+ addr = le16_to_cpu(ctrl->wValue);
+ dev_dbg(bdc->dev,
+ "%s addr:%d dev state:%d\n",
+ __func__, addr, state);
+
+ if (addr > 127)
+ return -EINVAL;
+
+ switch (state) {
+ case USB_STATE_DEFAULT:
+ case USB_STATE_ADDRESS:
+ /* Issue Address device command */
+ ret = bdc_address_device(bdc, addr);
+ if (ret)
+ return ret;
+
+ if (addr)
+ usb_gadget_set_state(&bdc->gadget, USB_STATE_ADDRESS);
+ else
+ usb_gadget_set_state(&bdc->gadget, USB_STATE_DEFAULT);
+
+ bdc->dev_addr = addr;
+ break;
+ default:
+ dev_warn(bdc->dev,
+ "SET Address in wrong device state %d\n",
+ state);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+/* Handler for SET/CLEAR FEATURE requests for device */
+static int ep0_handle_feature_dev(struct bdc *bdc, u16 wValue,
+ u16 wIndex, bool set)
+{
+ enum usb_device_state state = bdc->gadget.state;
+ u32 usppms = 0;
+
+ dev_dbg(bdc->dev, "%s set:%d dev state:%d\n",
+ __func__, set, state);
+ switch (wValue) {
+ case USB_DEVICE_REMOTE_WAKEUP:
+ dev_dbg(bdc->dev, "USB_DEVICE_REMOTE_WAKEUP\n");
+ if (set)
+ bdc->devstatus |= REMOTE_WAKE_ENABLE;
+ else
+ bdc->devstatus &= ~REMOTE_WAKE_ENABLE;
+ break;
+
+ case USB_DEVICE_TEST_MODE:
+ dev_dbg(bdc->dev, "USB_DEVICE_TEST_MODE\n");
+ if ((wIndex & 0xFF) ||
+ (bdc->gadget.speed != USB_SPEED_HIGH) || !set)
+ return -EINVAL;
+
+ bdc->test_mode = wIndex >> 8;
+ break;
+
+ case USB_DEVICE_U1_ENABLE:
+ dev_dbg(bdc->dev, "USB_DEVICE_U1_ENABLE\n");
+
+ if (bdc->gadget.speed != USB_SPEED_SUPER ||
+ state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+
+ usppms = bdc_readl(bdc->regs, BDC_USPPMS);
+ if (set) {
+ /* clear previous u1t */
+ usppms &= ~BDC_U1T(BDC_U1T_MASK);
+ usppms |= BDC_U1T(U1_TIMEOUT);
+ usppms |= BDC_U1E | BDC_PORT_W1S;
+ bdc->devstatus |= (1 << USB_DEV_STAT_U1_ENABLED);
+ } else {
+ usppms &= ~BDC_U1E;
+ usppms |= BDC_PORT_W1S;
+ bdc->devstatus &= ~(1 << USB_DEV_STAT_U1_ENABLED);
+ }
+ bdc_writel(bdc->regs, BDC_USPPMS, usppms);
+ break;
+
+ case USB_DEVICE_U2_ENABLE:
+ dev_dbg(bdc->dev, "USB_DEVICE_U2_ENABLE\n");
+
+ if (bdc->gadget.speed != USB_SPEED_SUPER ||
+ state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+
+ usppms = bdc_readl(bdc->regs, BDC_USPPMS);
+ if (set) {
+ usppms |= BDC_U2E;
+ usppms |= BDC_U2A;
+ bdc->devstatus |= (1 << USB_DEV_STAT_U2_ENABLED);
+ } else {
+ usppms &= ~BDC_U2E;
+ usppms &= ~BDC_U2A;
+ bdc->devstatus &= ~(1 << USB_DEV_STAT_U2_ENABLED);
+ }
+ bdc_writel(bdc->regs, BDC_USPPMS, usppms);
+ break;
+
+ case USB_DEVICE_LTM_ENABLE:
+ dev_dbg(bdc->dev, "USB_DEVICE_LTM_ENABLE?\n");
+ if (bdc->gadget.speed != USB_SPEED_SUPER ||
+ state != USB_STATE_CONFIGURED)
+ return -EINVAL;
+ break;
+ default:
+ dev_err(bdc->dev, "Unknown wValue:%d\n", wValue);
+ return -EOPNOTSUPP;
+ } /* USB_RECIP_DEVICE end */
+
+ return 0;
+}
+
+/* SET/CLEAR FEATURE handler */
+static int ep0_handle_feature(struct bdc *bdc,
+ struct usb_ctrlrequest *setup_pkt, bool set)
+{
+ enum usb_device_state state = bdc->gadget.state;
+ struct bdc_ep *ep;
+ u16 wValue;
+ u16 wIndex;
+ int epnum;
+
+ wValue = le16_to_cpu(setup_pkt->wValue);
+ wIndex = le16_to_cpu(setup_pkt->wIndex);
+
+ dev_dbg(bdc->dev,
+ "%s wValue=%d wIndex=%d devstate=%08x speed=%d set=%d",
+ __func__, wValue, wIndex, state,
+ bdc->gadget.speed, set);
+
+ switch (setup_pkt->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ return ep0_handle_feature_dev(bdc, wValue, wIndex, set);
+ case USB_RECIP_INTERFACE:
+ dev_dbg(bdc->dev, "USB_RECIP_INTERFACE\n");
+ /* USB3 spec, sec 9.4.9 */
+ if (wValue != USB_INTRF_FUNC_SUSPEND)
+ return -EINVAL;
+ /* USB3 spec, Table 9-8 */
+ if (set) {
+ if (wIndex & USB_INTRF_FUNC_SUSPEND_RW) {
+ dev_dbg(bdc->dev, "SET REMOTE_WAKEUP\n");
+ bdc->devstatus |= REMOTE_WAKE_ENABLE;
+ } else {
+ dev_dbg(bdc->dev, "CLEAR REMOTE_WAKEUP\n");
+ bdc->devstatus &= ~REMOTE_WAKE_ENABLE;
+ }
+ }
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ dev_dbg(bdc->dev, "USB_RECIP_ENDPOINT\n");
+ if (wValue != USB_ENDPOINT_HALT)
+ return -EINVAL;
+
+ epnum = wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (epnum) {
+ if ((wIndex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ epnum = epnum * 2 + 1;
+ else
+ epnum *= 2;
+ } else {
+ epnum = 1; /*EP0*/
+ }
+ /*
+ * If CLEAR_FEATURE on ep0 then don't do anything as the stall
+ * condition on ep0 has already been cleared when SETUP packet
+ * was received.
+ */
+ if (epnum == 1 && !set) {
+ dev_dbg(bdc->dev, "ep0 stall already cleared\n");
+ return 0;
+ }
+ dev_dbg(bdc->dev, "epnum=%d\n", epnum);
+ ep = bdc->bdc_ep_array[epnum];
+ if (!ep)
+ return -EINVAL;
+
+ return ep_set_halt(ep, set);
+ default:
+ dev_err(bdc->dev, "Unknown recipient\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* GET_STATUS request handler */
+static int ep0_handle_status(struct bdc *bdc,
+ struct usb_ctrlrequest *setup_pkt)
+{
+ enum usb_device_state state = bdc->gadget.state;
+ struct bdc_ep *ep;
+ u16 usb_status = 0;
+ u32 epnum;
+ u16 wIndex;
+
+ /* USB2.0 spec sec 9.4.5 */
+ if (state == USB_STATE_DEFAULT)
+ return -EINVAL;
+ wIndex = le16_to_cpu(setup_pkt->wIndex);
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ usb_status = bdc->devstatus;
+ switch (setup_pkt->bRequestType & USB_RECIP_MASK) {
+ case USB_RECIP_DEVICE:
+ dev_dbg(bdc->dev,
+ "USB_RECIP_DEVICE devstatus:%08x\n",
+ bdc->devstatus);
+ /* USB3 spec, sec 9.4.5 */
+ if (bdc->gadget.speed == USB_SPEED_SUPER)
+ usb_status &= ~REMOTE_WAKE_ENABLE;
+ break;
+
+ case USB_RECIP_INTERFACE:
+ dev_dbg(bdc->dev, "USB_RECIP_INTERFACE\n");
+ if (bdc->gadget.speed == USB_SPEED_SUPER) {
+ /*
+ * This should come from func for Func remote wkup
+ * usb_status |=1;
+ */
+ if (bdc->devstatus & REMOTE_WAKE_ENABLE)
+ usb_status |= REMOTE_WAKE_ENABLE;
+ } else {
+ usb_status = 0;
+ }
+
+ break;
+
+ case USB_RECIP_ENDPOINT:
+ dev_dbg(bdc->dev, "USB_RECIP_ENDPOINT\n");
+ epnum = wIndex & USB_ENDPOINT_NUMBER_MASK;
+ if (epnum) {
+ if ((wIndex & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN)
+ epnum = epnum*2 + 1;
+ else
+ epnum *= 2;
+ } else {
+ epnum = 1; /* EP0 */
+ }
+
+ ep = bdc->bdc_ep_array[epnum];
+ if (!ep) {
+ dev_err(bdc->dev, "ISSUE, GET_STATUS for invalid EP ?");
+ return -EINVAL;
+ }
+ if (ep->flags & BDC_EP_STALL)
+ usb_status |= 1 << USB_ENDPOINT_HALT;
+
+ break;
+ default:
+ dev_err(bdc->dev, "Unknown recipient for get_status\n");
+ return -EINVAL;
+ }
+ /* prepare a data stage for GET_STATUS */
+ dev_dbg(bdc->dev, "usb_status=%08x\n", usb_status);
+ *(__le16 *)bdc->ep0_response_buff = cpu_to_le16(usb_status);
+ bdc->ep0_req.usb_req.length = 2;
+ bdc->ep0_req.usb_req.buf = &bdc->ep0_response_buff;
+ ep0_queue_data_stage(bdc);
+
+ return 0;
+}
+
+static void ep0_set_sel_cmpl(struct usb_ep *_ep, struct usb_request *_req)
+{
+ /* ep0_set_sel_cmpl */
+}
+
+/* Queue data stage to handle 6 byte SET_SEL request */
+static int ep0_set_sel(struct bdc *bdc,
+ struct usb_ctrlrequest *setup_pkt)
+{
+ struct bdc_ep *ep;
+ u16 wLength;
+ u16 wValue;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ wValue = le16_to_cpu(setup_pkt->wValue);
+ wLength = le16_to_cpu(setup_pkt->wLength);
+ if (unlikely(wLength != 6)) {
+ dev_err(bdc->dev, "%s Wrong wLength:%d\n", __func__, wLength);
+ return -EINVAL;
+ }
+ ep = bdc->bdc_ep_array[1];
+ bdc->ep0_req.ep = ep;
+ bdc->ep0_req.usb_req.length = 6;
+ bdc->ep0_req.usb_req.buf = bdc->ep0_response_buff;
+ bdc->ep0_req.usb_req.complete = ep0_set_sel_cmpl;
+ ep0_queue_data_stage(bdc);
+
+ return 0;
+}
+
+/*
+ * Queue a 0 byte bd only if wLength is more than the length and and length is
+ * a multiple of MaxPacket then queue 0 byte BD
+ */
+static int ep0_queue_zlp(struct bdc *bdc)
+{
+ int ret;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ bdc->ep0_req.ep = bdc->bdc_ep_array[1];
+ bdc->ep0_req.usb_req.length = 0;
+ bdc->ep0_req.usb_req.complete = NULL;
+ bdc->ep0_state = WAIT_FOR_DATA_START;
+ ret = bdc_queue_xfr(bdc, &bdc->ep0_req);
+ if (ret) {
+ dev_err(bdc->dev, "err queueing zlp :%d\n", ret);
+ return ret;
+ }
+ bdc->ep0_state = WAIT_FOR_DATA_XMIT;
+
+ return 0;
+}
+
+/* Control request handler */
+static int handle_control_request(struct bdc *bdc)
+{
+ enum usb_device_state state = bdc->gadget.state;
+ struct usb_ctrlrequest *setup_pkt;
+ int delegate_setup = 0;
+ int ret = 0;
+ int config = 0;
+
+ setup_pkt = &bdc->setup_pkt;
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ if ((setup_pkt->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+ switch (setup_pkt->bRequest) {
+ case USB_REQ_SET_ADDRESS:
+ dev_dbg(bdc->dev, "USB_REQ_SET_ADDRESS\n");
+ ret = ep0_set_address(bdc, setup_pkt);
+ bdc->devstatus &= DEVSTATUS_CLEAR;
+ break;
+
+ case USB_REQ_SET_CONFIGURATION:
+ dev_dbg(bdc->dev, "USB_REQ_SET_CONFIGURATION\n");
+ if (state == USB_STATE_ADDRESS) {
+ usb_gadget_set_state(&bdc->gadget,
+ USB_STATE_CONFIGURED);
+ } else if (state == USB_STATE_CONFIGURED) {
+ /*
+ * USB2 spec sec 9.4.7, if wValue is 0 then dev
+ * is moved to addressed state
+ */
+ config = le16_to_cpu(setup_pkt->wValue);
+ if (!config)
+ usb_gadget_set_state(
+ &bdc->gadget,
+ USB_STATE_ADDRESS);
+ }
+ delegate_setup = 1;
+ break;
+
+ case USB_REQ_SET_FEATURE:
+ dev_dbg(bdc->dev, "USB_REQ_SET_FEATURE\n");
+ ret = ep0_handle_feature(bdc, setup_pkt, 1);
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ dev_dbg(bdc->dev, "USB_REQ_CLEAR_FEATURE\n");
+ ret = ep0_handle_feature(bdc, setup_pkt, 0);
+ break;
+
+ case USB_REQ_GET_STATUS:
+ dev_dbg(bdc->dev, "USB_REQ_GET_STATUS\n");
+ ret = ep0_handle_status(bdc, setup_pkt);
+ break;
+
+ case USB_REQ_SET_SEL:
+ dev_dbg(bdc->dev, "USB_REQ_SET_SEL\n");
+ ret = ep0_set_sel(bdc, setup_pkt);
+ break;
+
+ case USB_REQ_SET_ISOCH_DELAY:
+ dev_warn(bdc->dev,
+ "USB_REQ_SET_ISOCH_DELAY not handled\n");
+ ret = 0;
+ break;
+ default:
+ delegate_setup = 1;
+ }
+ } else {
+ delegate_setup = 1;
+ }
+
+ if (delegate_setup) {
+ spin_unlock(&bdc->lock);
+ ret = bdc->gadget_driver->setup(&bdc->gadget, setup_pkt);
+ spin_lock(&bdc->lock);
+ }
+
+ return ret;
+}
+
+/* EP0: Data stage started */
+void bdc_xsf_ep0_data_start(struct bdc *bdc, struct bdc_sr *sreport)
+{
+ struct bdc_ep *ep;
+ int ret = 0;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ ep = bdc->bdc_ep_array[1];
+ /* If ep0 was stalled, the clear it first */
+ if (ep->flags & BDC_EP_STALL) {
+ ret = ep_set_halt(ep, 0);
+ if (ret)
+ goto err;
+ }
+ if (bdc->ep0_state != WAIT_FOR_DATA_START)
+ dev_warn(bdc->dev,
+ "Data stage not expected ep0_state:%s\n",
+ ep0_state_string[bdc->ep0_state]);
+
+ ret = handle_control_request(bdc);
+ if (ret == USB_GADGET_DELAYED_STATUS) {
+ /*
+ * The ep0 state will remain WAIT_FOR_DATA_START till
+ * we received ep_queue on ep0
+ */
+ bdc->delayed_status = true;
+ return;
+ }
+ if (!ret) {
+ bdc->ep0_state = WAIT_FOR_DATA_XMIT;
+ dev_dbg(bdc->dev,
+ "ep0_state:%s", ep0_state_string[bdc->ep0_state]);
+ return;
+ }
+err:
+ ep0_stall(bdc);
+}
+
+/* EP0: status stage started */
+void bdc_xsf_ep0_status_start(struct bdc *bdc, struct bdc_sr *sreport)
+{
+ struct usb_ctrlrequest *setup_pkt;
+ struct bdc_ep *ep;
+ int ret = 0;
+
+ dev_dbg(bdc->dev,
+ "%s ep0_state:%s",
+ __func__, ep0_state_string[bdc->ep0_state]);
+ ep = bdc->bdc_ep_array[1];
+
+ /* check if ZLP was queued? */
+ if (bdc->zlp_needed)
+ bdc->zlp_needed = false;
+
+ if (ep->flags & BDC_EP_STALL) {
+ ret = ep_set_halt(ep, 0);
+ if (ret)
+ goto err;
+ }
+
+ if ((bdc->ep0_state != WAIT_FOR_STATUS_START) &&
+ (bdc->ep0_state != WAIT_FOR_DATA_XMIT))
+ dev_err(bdc->dev,
+ "Status stage recv but ep0_state:%s\n",
+ ep0_state_string[bdc->ep0_state]);
+
+ /* check if data stage is in progress ? */
+ if (bdc->ep0_state == WAIT_FOR_DATA_XMIT) {
+ bdc->ep0_state = STATUS_PENDING;
+ /* Status stage will be queued upon Data stage transmit event */
+ dev_dbg(bdc->dev,
+ "status started but data not transmitted yet\n");
+ return;
+ }
+ setup_pkt = &bdc->setup_pkt;
+
+ /*
+ * 2 stage setup then only process the setup, for 3 stage setup the date
+ * stage is already handled
+ */
+ if (!le16_to_cpu(setup_pkt->wLength)) {
+ ret = handle_control_request(bdc);
+ if (ret == USB_GADGET_DELAYED_STATUS) {
+ bdc->delayed_status = true;
+ /* ep0_state will remain WAIT_FOR_STATUS_START */
+ return;
+ }
+ }
+ if (!ret) {
+ /* Queue a status stage BD */
+ ep0_queue_status_stage(bdc);
+ bdc->ep0_state = WAIT_FOR_STATUS_XMIT;
+ dev_dbg(bdc->dev,
+ "ep0_state:%s", ep0_state_string[bdc->ep0_state]);
+ return;
+ }
+err:
+ ep0_stall(bdc);
+}
+
+/* Helper function to update ep0 upon SR with xsf_succ or xsf_short */
+static void ep0_xsf_complete(struct bdc *bdc, struct bdc_sr *sreport)
+{
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ switch (bdc->ep0_state) {
+ case WAIT_FOR_DATA_XMIT:
+ bdc->ep0_state = WAIT_FOR_STATUS_START;
+ break;
+ case WAIT_FOR_STATUS_XMIT:
+ bdc->ep0_state = WAIT_FOR_SETUP;
+ if (bdc->test_mode) {
+ int ret;
+
+ dev_dbg(bdc->dev, "test_mode:%d\n", bdc->test_mode);
+ ret = bdc_set_test_mode(bdc);
+ if (ret < 0) {
+ dev_err(bdc->dev, "Err in setting Test mode\n");
+ return;
+ }
+ bdc->test_mode = 0;
+ }
+ break;
+ case STATUS_PENDING:
+ bdc_xsf_ep0_status_start(bdc, sreport);
+ break;
+
+ default:
+ dev_err(bdc->dev,
+ "Unknown ep0_state:%s\n",
+ ep0_state_string[bdc->ep0_state]);
+
+ }
+}
+
+/* xfr completion status report handler */
+void bdc_sr_xsf(struct bdc *bdc, struct bdc_sr *sreport)
+{
+ struct bdc_ep *ep;
+ u32 sr_status;
+ u8 ep_num;
+
+ ep_num = (le32_to_cpu(sreport->offset[3])>>4) & 0x1f;
+ ep = bdc->bdc_ep_array[ep_num];
+ if (!ep || !(ep->flags & BDC_EP_ENABLED)) {
+ dev_err(bdc->dev, "xsf for ep not enabled\n");
+ return;
+ }
+ /*
+ * check if this transfer is after link went from U3->U0 due
+ * to remote wakeup
+ */
+ if (bdc->devstatus & FUNC_WAKE_ISSUED) {
+ bdc->devstatus &= ~(FUNC_WAKE_ISSUED);
+ dev_dbg(bdc->dev, "%s clearing FUNC_WAKE_ISSUED flag\n",
+ __func__);
+ }
+ sr_status = XSF_STS(le32_to_cpu(sreport->offset[3]));
+ dev_dbg_ratelimited(bdc->dev, "%s sr_status=%d ep:%s\n",
+ __func__, sr_status, ep->name);
+
+ switch (sr_status) {
+ case XSF_SUCC:
+ case XSF_SHORT:
+ handle_xsr_succ_status(bdc, ep, sreport);
+ if (ep_num == 1)
+ ep0_xsf_complete(bdc, sreport);
+ break;
+
+ case XSF_SETUP_RECV:
+ case XSF_DATA_START:
+ case XSF_STATUS_START:
+ if (ep_num != 1) {
+ dev_err(bdc->dev,
+ "ep0 related packets on non ep0 endpoint");
+ return;
+ }
+ bdc->sr_xsf_ep0[sr_status - XSF_SETUP_RECV](bdc, sreport);
+ break;
+
+ case XSF_BABB:
+ if (ep_num == 1) {
+ dev_dbg(bdc->dev, "Babble on ep0 zlp_need:%d\n",
+ bdc->zlp_needed);
+ /*
+ * If the last completed transfer had wLength >Data Len,
+ * and Len is multiple of MaxPacket,then queue ZLP
+ */
+ if (bdc->zlp_needed) {
+ /* queue 0 length bd */
+ ep0_queue_zlp(bdc);
+ return;
+ }
+ }
+ dev_warn(bdc->dev, "Babble on ep not handled\n");
+ break;
+ default:
+ dev_warn(bdc->dev, "sr status not handled:%x\n", sr_status);
+ break;
+ }
+}
+
+static int bdc_gadget_ep_queue(struct usb_ep *_ep,
+ struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct bdc_req *req;
+ unsigned long flags;
+ struct bdc_ep *ep;
+ struct bdc *bdc;
+ int ret;
+
+ if (!_ep || !_ep->desc)
+ return -ESHUTDOWN;
+
+ if (!_req || !_req->complete || !_req->buf)
+ return -EINVAL;
+
+ ep = to_bdc_ep(_ep);
+ req = to_bdc_req(_req);
+ bdc = ep->bdc;
+ dev_dbg(bdc->dev, "%s ep:%p req:%p\n", __func__, ep, req);
+ dev_dbg(bdc->dev, "queuing request %p to %s length %d zero:%d\n",
+ _req, ep->name, _req->length, _req->zero);
+
+ if (!ep->usb_ep.desc) {
+ dev_warn(bdc->dev,
+ "trying to queue req %p to disabled %s\n",
+ _req, ep->name);
+ return -ESHUTDOWN;
+ }
+
+ if (_req->length > MAX_XFR_LEN) {
+ dev_warn(bdc->dev,
+ "req length > supported MAX:%d requested:%d\n",
+ MAX_XFR_LEN, _req->length);
+ return -EOPNOTSUPP;
+ }
+ spin_lock_irqsave(&bdc->lock, flags);
+ if (ep == bdc->bdc_ep_array[1])
+ ret = ep0_queue(ep, req);
+ else
+ ret = ep_queue(ep, req);
+
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return ret;
+}
+
+static int bdc_gadget_ep_dequeue(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct bdc_req *req;
+ unsigned long flags;
+ struct bdc_ep *ep;
+ struct bdc *bdc;
+ int ret;
+
+ if (!_ep || !_req)
+ return -EINVAL;
+
+ ep = to_bdc_ep(_ep);
+ req = to_bdc_req(_req);
+ bdc = ep->bdc;
+ dev_dbg(bdc->dev, "%s ep:%s req:%p\n", __func__, ep->name, req);
+ bdc_dbg_bd_list(bdc, ep);
+ spin_lock_irqsave(&bdc->lock, flags);
+ /* make sure it's still queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->usb_req == _req)
+ break;
+ }
+ if (&req->usb_req != _req) {
+ spin_unlock_irqrestore(&bdc->lock, flags);
+ dev_err(bdc->dev, "usb_req !=req n");
+ return -EINVAL;
+ }
+ ret = ep_dequeue(ep, req);
+ if (ret) {
+ ret = -EOPNOTSUPP;
+ goto err;
+ }
+ bdc_req_complete(ep, req, -ECONNRESET);
+
+err:
+ bdc_dbg_bd_list(bdc, ep);
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return ret;
+}
+
+static int bdc_gadget_ep_set_halt(struct usb_ep *_ep, int value)
+{
+ unsigned long flags;
+ struct bdc_ep *ep;
+ struct bdc *bdc;
+ int ret;
+
+ ep = to_bdc_ep(_ep);
+ bdc = ep->bdc;
+ dev_dbg(bdc->dev, "%s ep:%s value=%d\n", __func__, ep->name, value);
+ spin_lock_irqsave(&bdc->lock, flags);
+ if (usb_endpoint_xfer_isoc(ep->usb_ep.desc))
+ ret = -EINVAL;
+ else if (!list_empty(&ep->queue))
+ ret = -EAGAIN;
+ else
+ ret = ep_set_halt(ep, value);
+
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return ret;
+}
+
+static struct usb_request *bdc_gadget_alloc_request(struct usb_ep *_ep,
+ gfp_t gfp_flags)
+{
+ struct bdc_req *req;
+ struct bdc_ep *ep;
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+
+ ep = to_bdc_ep(_ep);
+ req->ep = ep;
+ req->epnum = ep->ep_num;
+ req->usb_req.dma = DMA_ADDR_INVALID;
+ dev_dbg(ep->bdc->dev, "%s ep:%s req:%p\n", __func__, ep->name, req);
+
+ return &req->usb_req;
+}
+
+static void bdc_gadget_free_request(struct usb_ep *_ep,
+ struct usb_request *_req)
+{
+ struct bdc_req *req;
+
+ req = to_bdc_req(_req);
+ kfree(req);
+}
+
+/* endpoint operations */
+
+/* configure endpoint and also allocate resources */
+static int bdc_gadget_ep_enable(struct usb_ep *_ep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ unsigned long flags;
+ struct bdc_ep *ep;
+ struct bdc *bdc;
+ int ret;
+
+ if (!_ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ pr_debug("bdc_gadget_ep_enable invalid parameters\n");
+ return -EINVAL;
+ }
+
+ if (!desc->wMaxPacketSize) {
+ pr_debug("bdc_gadget_ep_enable missing wMaxPacketSize\n");
+ return -EINVAL;
+ }
+
+ ep = to_bdc_ep(_ep);
+ bdc = ep->bdc;
+
+ /* Sanity check, upper layer will not send enable for ep0 */
+ if (ep == bdc->bdc_ep_array[1])
+ return -EINVAL;
+
+ if (!bdc->gadget_driver
+ || bdc->gadget.speed == USB_SPEED_UNKNOWN) {
+ return -ESHUTDOWN;
+ }
+
+ dev_dbg(bdc->dev, "%s Enabling %s\n", __func__, ep->name);
+ spin_lock_irqsave(&bdc->lock, flags);
+ ep->desc = desc;
+ ep->comp_desc = _ep->comp_desc;
+ ret = bdc_ep_enable(ep);
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return ret;
+}
+
+static int bdc_gadget_ep_disable(struct usb_ep *_ep)
+{
+ unsigned long flags;
+ struct bdc_ep *ep;
+ struct bdc *bdc;
+ int ret;
+
+ if (!_ep) {
+ pr_debug("bdc: invalid parameters\n");
+ return -EINVAL;
+ }
+ ep = to_bdc_ep(_ep);
+ bdc = ep->bdc;
+
+ /* Upper layer will not call this for ep0, but do a sanity check */
+ if (ep == bdc->bdc_ep_array[1]) {
+ dev_warn(bdc->dev, "%s called for ep0\n", __func__);
+ return -EINVAL;
+ }
+ dev_dbg(bdc->dev,
+ "%s() ep:%s ep->flags:%08x\n",
+ __func__, ep->name, ep->flags);
+
+ if (!(ep->flags & BDC_EP_ENABLED)) {
+ dev_warn(bdc->dev, "%s is already disabled\n", ep->name);
+ return 0;
+ }
+ spin_lock_irqsave(&bdc->lock, flags);
+ ret = bdc_ep_disable(ep);
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return ret;
+}
+
+static const struct usb_ep_ops bdc_gadget_ep_ops = {
+ .enable = bdc_gadget_ep_enable,
+ .disable = bdc_gadget_ep_disable,
+ .alloc_request = bdc_gadget_alloc_request,
+ .free_request = bdc_gadget_free_request,
+ .queue = bdc_gadget_ep_queue,
+ .dequeue = bdc_gadget_ep_dequeue,
+ .set_halt = bdc_gadget_ep_set_halt
+};
+
+/* dir = 1 is IN */
+static int init_ep(struct bdc *bdc, u32 epnum, u32 dir)
+{
+ struct bdc_ep *ep;
+
+ dev_dbg(bdc->dev, "%s epnum=%d dir=%d\n", __func__, epnum, dir);
+ ep = kzalloc(sizeof(*ep), GFP_KERNEL);
+ if (!ep)
+ return -ENOMEM;
+
+ ep->bdc = bdc;
+ ep->dir = dir;
+
+ /* ep->ep_num is the index inside bdc_ep */
+ if (epnum == 1) {
+ ep->ep_num = 1;
+ bdc->bdc_ep_array[ep->ep_num] = ep;
+ snprintf(ep->name, sizeof(ep->name), "ep%d", epnum - 1);
+ usb_ep_set_maxpacket_limit(&ep->usb_ep, EP0_MAX_PKT_SIZE);
+ ep->comp_desc = NULL;
+ bdc->gadget.ep0 = &ep->usb_ep;
+ } else {
+ if (dir)
+ ep->ep_num = epnum * 2 - 1;
+ else
+ ep->ep_num = epnum * 2 - 2;
+
+ bdc->bdc_ep_array[ep->ep_num] = ep;
+ snprintf(ep->name, sizeof(ep->name), "ep%d%s", epnum - 1,
+ dir & 1 ? "in" : "out");
+
+ usb_ep_set_maxpacket_limit(&ep->usb_ep, 1024);
+ ep->usb_ep.max_streams = 0;
+ list_add_tail(&ep->usb_ep.ep_list, &bdc->gadget.ep_list);
+ }
+ ep->usb_ep.ops = &bdc_gadget_ep_ops;
+ ep->usb_ep.name = ep->name;
+ ep->flags = 0;
+ ep->ignore_next_sr = false;
+ dev_dbg(bdc->dev, "ep=%p ep->usb_ep.name=%s epnum=%d ep->epnum=%d\n",
+ ep, ep->usb_ep.name, epnum, ep->ep_num);
+
+ INIT_LIST_HEAD(&ep->queue);
+
+ return 0;
+}
+
+/* Init all ep */
+int bdc_init_ep(struct bdc *bdc)
+{
+ u8 epnum;
+ int ret;
+
+ dev_dbg(bdc->dev, "%s()\n", __func__);
+ INIT_LIST_HEAD(&bdc->gadget.ep_list);
+ /* init ep0 */
+ ret = init_ep(bdc, 1, 0);
+ if (ret) {
+ dev_err(bdc->dev, "init ep ep0 fail %d\n", ret);
+ return ret;
+ }
+
+ for (epnum = 2; epnum <= bdc->num_eps / 2; epnum++) {
+ /* OUT */
+ ret = init_ep(bdc, epnum, 0);
+ if (ret) {
+ dev_err(bdc->dev,
+ "init ep failed for:%d error: %d\n",
+ epnum, ret);
+ return ret;
+ }
+
+ /* IN */
+ ret = init_ep(bdc, epnum, 1);
+ if (ret) {
+ dev_err(bdc->dev,
+ "init ep failed for:%d error: %d\n",
+ epnum, ret);
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.h b/drivers/usb/gadget/udc/bdc/bdc_ep.h
new file mode 100644
index 000000000000..8a6b36cbf2ea
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_ep.h
@@ -0,0 +1,22 @@
+/*
+ * bdc_ep.h - header for the BDC debug functions
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * 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 __LINUX_BDC_EP_H__
+#define __LINUX_BDC_EP_H__
+
+int bdc_init_ep(struct bdc *);
+int bdc_ep_disable(struct bdc_ep *);
+int bdc_ep_enable(struct bdc_ep *);
+void bdc_free_ep(struct bdc *);
+
+#endif /* __LINUX_BDC_EP_H__ */
diff --git a/drivers/usb/gadget/udc/bdc/bdc_pci.c b/drivers/usb/gadget/udc/bdc/bdc_pci.c
new file mode 100644
index 000000000000..02968842b359
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_pci.c
@@ -0,0 +1,132 @@
+/*
+ * bdc_pci.c - BRCM BDC USB3.0 device controller PCI interface file.
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * Based on drivers under drivers/usb/
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/platform_device.h>
+
+#include "bdc.h"
+
+#define BDC_PCI_PID 0x1570
+
+struct bdc_pci {
+ struct device *dev;
+ struct platform_device *bdc;
+};
+
+static int bdc_setup_msi(struct pci_dev *pci)
+{
+ int ret;
+
+ ret = pci_enable_msi(pci);
+ if (ret) {
+ pr_err("failed to allocate MSI entry\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static int bdc_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
+{
+ struct resource res[2];
+ struct platform_device *bdc;
+ struct bdc_pci *glue;
+ int ret = -ENOMEM;
+
+ glue = devm_kzalloc(&pci->dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ return -ENOMEM;
+
+ glue->dev = &pci->dev;
+ ret = pci_enable_device(pci);
+ if (ret) {
+ dev_err(&pci->dev, "failed to enable pci device\n");
+ return -ENODEV;
+ }
+ pci_set_master(pci);
+
+ bdc = platform_device_alloc(BRCM_BDC_NAME, PLATFORM_DEVID_AUTO);
+ if (!bdc)
+ return -ENOMEM;
+
+ memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
+ bdc_setup_msi(pci);
+
+ res[0].start = pci_resource_start(pci, 0);
+ res[0].end = pci_resource_end(pci, 0);
+ res[0].name = BRCM_BDC_NAME;
+ res[0].flags = IORESOURCE_MEM;
+
+ res[1].start = pci->irq;
+ res[1].name = BRCM_BDC_NAME;
+ res[1].flags = IORESOURCE_IRQ;
+
+ ret = platform_device_add_resources(bdc, res, ARRAY_SIZE(res));
+ if (ret) {
+ dev_err(&pci->dev,
+ "couldn't add resources to bdc device\n");
+ return ret;
+ }
+
+ pci_set_drvdata(pci, glue);
+
+ dma_set_coherent_mask(&bdc->dev, pci->dev.coherent_dma_mask);
+
+ bdc->dev.dma_mask = pci->dev.dma_mask;
+ bdc->dev.dma_parms = pci->dev.dma_parms;
+ bdc->dev.parent = &pci->dev;
+ glue->bdc = bdc;
+
+ ret = platform_device_add(bdc);
+ if (ret) {
+ dev_err(&pci->dev, "failed to register bdc device\n");
+ platform_device_put(bdc);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void bdc_pci_remove(struct pci_dev *pci)
+{
+ struct bdc_pci *glue = pci_get_drvdata(pci);
+
+ platform_device_unregister(glue->bdc);
+ pci_disable_msi(pci);
+}
+
+static struct pci_device_id bdc_pci_id_table[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_BROADCOM, BDC_PCI_PID), },
+ {} /* Terminating Entry */
+};
+
+MODULE_DEVICE_TABLE(pci, bdc_pci_id_table);
+
+static struct pci_driver bdc_pci_driver = {
+ .name = "bdc-pci",
+ .id_table = bdc_pci_id_table,
+ .probe = bdc_pci_probe,
+ .remove = bdc_pci_remove,
+};
+
+MODULE_AUTHOR("Ashwini Pahuja <ashwini.linux@gmail.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("BRCM BDC USB3 PCI Glue layer");
+module_pci_driver(bdc_pci_driver);
diff --git a/drivers/usb/gadget/udc/bdc/bdc_udc.c b/drivers/usb/gadget/udc/bdc/bdc_udc.c
new file mode 100644
index 000000000000..3700ce70b0be
--- /dev/null
+++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
@@ -0,0 +1,587 @@
+/*
+ * bdc_udc.c - BRCM BDC USB3.0 device controller gagdet ops
+ *
+ * Copyright (C) 2014 Broadcom Corporation
+ *
+ * Author: Ashwini Pahuja
+ *
+ * Based on drivers under drivers/usb/gadget/udc/
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/pm.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <asm/unaligned.h>
+#include <linux/platform_device.h>
+
+#include "bdc.h"
+#include "bdc_ep.h"
+#include "bdc_cmd.h"
+#include "bdc_dbg.h"
+
+static const struct usb_gadget_ops bdc_gadget_ops;
+
+static const char * const conn_speed_str[] = {
+ "Not connected",
+ "Full Speed",
+ "Low Speed",
+ "High Speed",
+ "Super Speed",
+};
+
+/* EP0 initial descripror */
+static struct usb_endpoint_descriptor bdc_gadget_ep0_desc = {
+ .bLength = USB_DT_ENDPOINT_SIZE,
+ .bDescriptorType = USB_DT_ENDPOINT,
+ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
+ .bEndpointAddress = 0,
+ .wMaxPacketSize = cpu_to_le16(EP0_MAX_PKT_SIZE),
+};
+
+/* Advance the srr dqp maintained by SW */
+static void srr_dqp_index_advc(struct bdc *bdc, u32 srr_num)
+{
+ struct srr *srr;
+
+ srr = &bdc->srr;
+ dev_dbg_ratelimited(bdc->dev, "srr->dqp_index:%d\n", srr->dqp_index);
+ srr->dqp_index++;
+ /* rollback to 0 if we are past the last */
+ if (srr->dqp_index == NUM_SR_ENTRIES)
+ srr->dqp_index = 0;
+}
+
+/* connect sr */
+static void bdc_uspc_connected(struct bdc *bdc)
+{
+ u32 speed, temp;
+ u32 usppms;
+ int ret;
+
+ temp = bdc_readl(bdc->regs, BDC_USPC);
+ speed = BDC_PSP(temp);
+ dev_dbg(bdc->dev, "%s speed=%x\n", __func__, speed);
+ switch (speed) {
+ case BDC_SPEED_SS:
+ bdc_gadget_ep0_desc.wMaxPacketSize =
+ cpu_to_le16(EP0_MAX_PKT_SIZE);
+ bdc->gadget.ep0->maxpacket = EP0_MAX_PKT_SIZE;
+ bdc->gadget.speed = USB_SPEED_SUPER;
+ /* Enable U1T in SS mode */
+ usppms = bdc_readl(bdc->regs, BDC_USPPMS);
+ usppms &= ~BDC_U1T(0xff);
+ usppms |= BDC_U1T(U1_TIMEOUT);
+ usppms |= BDC_PORT_W1S;
+ bdc_writel(bdc->regs, BDC_USPPMS, usppms);
+ break;
+
+ case BDC_SPEED_HS:
+ bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ bdc->gadget.ep0->maxpacket = 64;
+ bdc->gadget.speed = USB_SPEED_HIGH;
+ break;
+
+ case BDC_SPEED_FS:
+ bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64);
+ bdc->gadget.ep0->maxpacket = 64;
+ bdc->gadget.speed = USB_SPEED_FULL;
+ break;
+
+ case BDC_SPEED_LS:
+ bdc_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8);
+ bdc->gadget.ep0->maxpacket = 8;
+ bdc->gadget.speed = USB_SPEED_LOW;
+ break;
+ default:
+ dev_err(bdc->dev, "UNDEFINED SPEED\n");
+ return;
+ }
+ dev_dbg(bdc->dev, "connected at %s\n", conn_speed_str[speed]);
+ /* Now we know the speed, configure ep0 */
+ bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
+ ret = bdc_config_ep(bdc, bdc->bdc_ep_array[1]);
+ if (ret)
+ dev_err(bdc->dev, "EP0 config failed\n");
+ bdc->bdc_ep_array[1]->usb_ep.desc = &bdc_gadget_ep0_desc;
+ bdc->bdc_ep_array[1]->flags |= BDC_EP_ENABLED;
+ usb_gadget_set_state(&bdc->gadget, USB_STATE_DEFAULT);
+}
+
+/* device got disconnected */
+static void bdc_uspc_disconnected(struct bdc *bdc, bool reinit)
+{
+ struct bdc_ep *ep;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ /*
+ * Only stop ep0 from here, rest of the endpoints will be disabled
+ * from gadget_disconnect
+ */
+ ep = bdc->bdc_ep_array[1];
+ if (ep && (ep->flags & BDC_EP_ENABLED))
+ /* if enabled then stop and remove requests */
+ bdc_ep_disable(ep);
+
+ if (bdc->gadget_driver && bdc->gadget_driver->disconnect) {
+ spin_unlock(&bdc->lock);
+ bdc->gadget_driver->disconnect(&bdc->gadget);
+ spin_lock(&bdc->lock);
+ }
+ /* Set Unknown speed */
+ bdc->gadget.speed = USB_SPEED_UNKNOWN;
+ bdc->devstatus &= DEVSTATUS_CLEAR;
+ bdc->delayed_status = false;
+ bdc->reinit = reinit;
+ bdc->test_mode = false;
+}
+
+/* TNotify wkaeup timer */
+static void bdc_func_wake_timer(struct work_struct *work)
+{
+ struct bdc *bdc = container_of(work, struct bdc, func_wake_notify.work);
+ unsigned long flags;
+
+ dev_dbg(bdc->dev, "%s\n", __func__);
+ spin_lock_irqsave(&bdc->lock, flags);
+ /*
+ * Check if host has started transferring on endpoints
+ * FUNC_WAKE_ISSUED is cleared when transfer has started after resume
+ */
+ if (bdc->devstatus & FUNC_WAKE_ISSUED) {
+ dev_dbg(bdc->dev, "FUNC_WAKE_ISSUED FLAG IS STILL SET\n");
+ /* flag is still set, so again send func wake */
+ bdc_function_wake_fh(bdc, 0);
+ schedule_delayed_work(&bdc->func_wake_notify,
+ msecs_to_jiffies(BDC_TNOTIFY));
+ }
+ spin_unlock_irqrestore(&bdc->lock, flags);
+}
+
+/* handler for Link state change condition */
+static void handle_link_state_change(struct bdc *bdc, u32 uspc)
+{
+ u32 link_state;
+
+ dev_dbg(bdc->dev, "Link state change");
+ link_state = BDC_PST(uspc);
+ switch (link_state) {
+ case BDC_LINK_STATE_U3:
+ if ((bdc->gadget.speed != USB_SPEED_UNKNOWN) &&
+ bdc->gadget_driver->suspend) {
+ dev_dbg(bdc->dev, "Entered Suspend mode\n");
+ spin_unlock(&bdc->lock);
+ bdc->devstatus |= DEVICE_SUSPENDED;
+ bdc->gadget_driver->suspend(&bdc->gadget);
+ spin_lock(&bdc->lock);
+ }
+ break;
+ case BDC_LINK_STATE_U0:
+ if (bdc->devstatus & REMOTE_WAKEUP_ISSUED) {
+ bdc->devstatus &= ~REMOTE_WAKEUP_ISSUED;
+ if (bdc->gadget.speed == USB_SPEED_SUPER) {
+ bdc_function_wake_fh(bdc, 0);
+ bdc->devstatus |= FUNC_WAKE_ISSUED;
+ /*
+ * Start a Notification timer and check if the
+ * Host transferred anything on any of the EPs,
+ * if not then send function wake again every
+ * TNotification secs until host initiates
+ * transfer to BDC, USB3 spec Table 8.13
+ */
+ schedule_delayed_work(
+ &bdc->func_wake_notify,
+ msecs_to_jiffies(BDC_TNOTIFY));
+ dev_dbg(bdc->dev, "sched func_wake_notify\n");
+ }
+ }
+ break;
+
+ case BDC_LINK_STATE_RESUME:
+ dev_dbg(bdc->dev, "Resumed from Suspend\n");
+ if (bdc->devstatus & DEVICE_SUSPENDED) {
+ bdc->gadget_driver->resume(&bdc->gadget);
+ bdc->devstatus &= ~DEVICE_SUSPENDED;
+ }
+ break;
+ default:
+ dev_dbg(bdc->dev, "link state:%d\n", link_state);
+ }
+}
+
+/* something changes on upstream port, handle it here */
+void bdc_sr_uspc(struct bdc *bdc, struct bdc_sr *sreport)
+{
+ u32 clear_flags = 0;
+ u32 uspc;
+ bool connected = false;
+ bool disconn = false;
+
+ uspc = bdc_readl(bdc->regs, BDC_USPC);
+ dev_dbg(bdc->dev, "%s uspc=0x%08x\n", __func__, uspc);
+
+ /* Port connect changed */
+ if (uspc & BDC_PCC) {
+ /* Vbus not present, and not connected to Downstream port */
+ if ((uspc & BDC_VBC) && !(uspc & BDC_VBS) && !(uspc & BDC_PCS))
+ disconn = true;
+ else if ((uspc & BDC_PCS) && !BDC_PST(uspc))
+ connected = true;
+ }
+
+ /* Change in VBus and VBus is present */
+ if ((uspc & BDC_VBC) && (uspc & BDC_VBS)) {
+ if (bdc->pullup) {
+ dev_dbg(bdc->dev, "Do a softconnect\n");
+ /* Attached state, do a softconnect */
+ bdc_softconn(bdc);
+ usb_gadget_set_state(&bdc->gadget, USB_STATE_POWERED);
+ }
+ clear_flags = BDC_VBC;
+ } else if ((uspc & BDC_PRS) || (uspc & BDC_PRC) || disconn) {
+ /* Hot reset, warm reset, 2.0 bus reset or disconn */
+ dev_dbg(bdc->dev, "Port reset or disconn\n");
+ bdc_uspc_disconnected(bdc, disconn);
+ clear_flags = BDC_PCC|BDC_PCS|BDC_PRS|BDC_PRC;
+ } else if ((uspc & BDC_PSC) && (uspc & BDC_PCS)) {
+ /* Change in Link state */
+ handle_link_state_change(bdc, uspc);
+ clear_flags = BDC_PSC|BDC_PCS;
+ }
+
+ /*
+ * In SS we might not have PRC bit set before connection, but in 2.0
+ * the PRC bit is set before connection, so moving this condition out
+ * of bus reset to handle both SS/2.0 speeds.
+ */
+ if (connected) {
+ /* This is the connect event for U0/L0 */
+ dev_dbg(bdc->dev, "Connected\n");
+ bdc_uspc_connected(bdc);
+ bdc->devstatus &= ~(DEVICE_SUSPENDED);
+ }
+ uspc = bdc_readl(bdc->regs, BDC_USPC);
+ uspc &= (~BDC_USPSC_RW);
+ dev_dbg(bdc->dev, "uspc=%x\n", uspc);
+ bdc_writel(bdc->regs, BDC_USPC, clear_flags);
+}
+
+/* Main interrupt handler for bdc */
+static irqreturn_t bdc_udc_interrupt(int irq, void *_bdc)
+{
+ u32 eqp_index, dqp_index, sr_type, srr_int;
+ struct bdc_sr *sreport;
+ struct bdc *bdc = _bdc;
+ u32 status;
+ int ret;
+
+ spin_lock(&bdc->lock);
+ status = bdc_readl(bdc->regs, BDC_BDCSC);
+ if (!(status & BDC_GIP)) {
+ spin_unlock(&bdc->lock);
+ return IRQ_NONE;
+ }
+ srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
+ /* Check if the SRR IP bit it set? */
+ if (!(srr_int & BDC_SRR_IP)) {
+ dev_warn(bdc->dev, "Global irq pending but SRR IP is 0\n");
+ spin_unlock(&bdc->lock);
+ return IRQ_NONE;
+ }
+ eqp_index = BDC_SRR_EPI(srr_int);
+ dqp_index = BDC_SRR_DPI(srr_int);
+ dev_dbg(bdc->dev,
+ "%s eqp_index=%d dqp_index=%d srr.dqp_index=%d\n\n",
+ __func__, eqp_index, dqp_index, bdc->srr.dqp_index);
+
+ /* check for ring empty condition */
+ if (eqp_index == dqp_index) {
+ dev_dbg(bdc->dev, "SRR empty?\n");
+ spin_unlock(&bdc->lock);
+ return IRQ_HANDLED;
+ }
+
+ while (bdc->srr.dqp_index != eqp_index) {
+ sreport = &bdc->srr.sr_bds[bdc->srr.dqp_index];
+ /* sreport is read before using it */
+ rmb();
+ sr_type = le32_to_cpu(sreport->offset[3]) & BD_TYPE_BITMASK;
+ dev_dbg_ratelimited(bdc->dev, "sr_type=%d\n", sr_type);
+ switch (sr_type) {
+ case SR_XSF:
+ bdc->sr_handler[0](bdc, sreport);
+ break;
+
+ case SR_USPC:
+ bdc->sr_handler[1](bdc, sreport);
+ break;
+ default:
+ dev_warn(bdc->dev, "SR:%d not handled\n", sr_type);
+ }
+ /* Advance the srr dqp index */
+ srr_dqp_index_advc(bdc, 0);
+ }
+ /* update the hw dequeue pointer */
+ srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
+ srr_int &= ~BDC_SRR_DPI_MASK;
+ srr_int &= ~(BDC_SRR_RWS|BDC_SRR_RST|BDC_SRR_ISR);
+ srr_int |= ((bdc->srr.dqp_index) << 16);
+ srr_int |= BDC_SRR_IP;
+ bdc_writel(bdc->regs, BDC_SRRINT(0), srr_int);
+ srr_int = bdc_readl(bdc->regs, BDC_SRRINT(0));
+ if (bdc->reinit) {
+ ret = bdc_reinit(bdc);
+ if (ret)
+ dev_err(bdc->dev, "err in bdc reinit\n");
+ }
+
+ spin_unlock(&bdc->lock);
+
+ return IRQ_HANDLED;
+}
+
+/* Gadget ops */
+static int bdc_udc_start(struct usb_gadget *gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct bdc *bdc = gadget_to_bdc(gadget);
+ unsigned long flags;
+ int ret = 0;
+
+ dev_dbg(bdc->dev, "%s()\n", __func__);
+ spin_lock_irqsave(&bdc->lock, flags);
+ if (bdc->gadget_driver) {
+ dev_err(bdc->dev, "%s is already bound to %s\n",
+ bdc->gadget.name,
+ bdc->gadget_driver->driver.name);
+ ret = -EBUSY;
+ goto err;
+ }
+ /*
+ * Run the controller from here and when BDC is connected to
+ * Host then driver will receive a USPC SR with VBUS present
+ * and then driver will do a softconnect.
+ */
+ ret = bdc_run(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "%s bdc run fail\n", __func__);
+ goto err;
+ }
+ bdc->gadget_driver = driver;
+ bdc->gadget.dev.driver = &driver->driver;
+err:
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return ret;
+}
+
+static int bdc_udc_stop(struct usb_gadget *gadget)
+{
+ struct bdc *bdc = gadget_to_bdc(gadget);
+ unsigned long flags;
+
+ dev_dbg(bdc->dev, "%s()\n", __func__);
+ spin_lock_irqsave(&bdc->lock, flags);
+ bdc_stop(bdc);
+ bdc->gadget_driver = NULL;
+ bdc->gadget.dev.driver = NULL;
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return 0;
+}
+
+static int bdc_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+ struct bdc *bdc = gadget_to_bdc(gadget);
+ unsigned long flags;
+ u32 uspc;
+
+ dev_dbg(bdc->dev, "%s() is_on:%d\n", __func__, is_on);
+ if (!gadget)
+ return -EINVAL;
+
+ spin_lock_irqsave(&bdc->lock, flags);
+ if (!is_on) {
+ bdc_softdisconn(bdc);
+ bdc->pullup = false;
+ } else {
+ /*
+ * For a self powered device, we need to wait till we receive
+ * a VBUS change and Vbus present event, then if pullup flag
+ * is set, then only we present the Termintation.
+ */
+ bdc->pullup = true;
+ /*
+ * Check if BDC is already connected to Host i.e Vbus=1,
+ * if yes, then present TERM now, this is typical for bus
+ * powered devices.
+ */
+ uspc = bdc_readl(bdc->regs, BDC_USPC);
+ if (uspc & BDC_VBS)
+ bdc_softconn(bdc);
+ }
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return 0;
+}
+
+static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
+ int is_self)
+{
+ struct bdc *bdc = gadget_to_bdc(gadget);
+ unsigned long flags;
+
+ dev_dbg(bdc->dev, "%s()\n", __func__);
+ spin_lock_irqsave(&bdc->lock, flags);
+ if (!is_self)
+ bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+ else
+ bdc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return 0;
+}
+
+static int bdc_udc_wakeup(struct usb_gadget *gadget)
+{
+ struct bdc *bdc = gadget_to_bdc(gadget);
+ unsigned long flags;
+ u8 link_state;
+ u32 uspc;
+ int ret = 0;
+
+ dev_dbg(bdc->dev,
+ "%s() bdc->devstatus=%08x\n",
+ __func__, bdc->devstatus);
+
+ if (!(bdc->devstatus & REMOTE_WAKE_ENABLE))
+ return -EOPNOTSUPP;
+
+ spin_lock_irqsave(&bdc->lock, flags);
+ uspc = bdc_readl(bdc->regs, BDC_USPC);
+ link_state = BDC_PST(uspc);
+ dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
+ if (link_state != BDC_LINK_STATE_U3) {
+ dev_warn(bdc->dev,
+ "can't wakeup from link state %d\n",
+ link_state);
+ ret = -EINVAL;
+ goto out;
+ }
+ if (bdc->gadget.speed == USB_SPEED_SUPER)
+ bdc->devstatus |= REMOTE_WAKEUP_ISSUED;
+
+ uspc &= ~BDC_PST_MASK;
+ uspc &= (~BDC_USPSC_RW);
+ uspc |= BDC_PST(BDC_LINK_STATE_U0);
+ uspc |= BDC_SWS;
+ bdc_writel(bdc->regs, BDC_USPC, uspc);
+ uspc = bdc_readl(bdc->regs, BDC_USPC);
+ link_state = BDC_PST(uspc);
+ dev_dbg(bdc->dev, "link_state =%d portsc=%x", link_state, uspc);
+out:
+ spin_unlock_irqrestore(&bdc->lock, flags);
+
+ return ret;
+}
+
+static const struct usb_gadget_ops bdc_gadget_ops = {
+ .wakeup = bdc_udc_wakeup,
+ .set_selfpowered = bdc_udc_set_selfpowered,
+ .pullup = bdc_udc_pullup,
+ .udc_start = bdc_udc_start,
+ .udc_stop = bdc_udc_stop,
+};
+
+/* Init the gadget interface and register the udc */
+int bdc_udc_init(struct bdc *bdc)
+{
+ u32 temp;
+ int ret;
+
+ dev_dbg(bdc->dev, "%s()\n", __func__);
+ bdc->gadget.ops = &bdc_gadget_ops;
+ bdc->gadget.max_speed = USB_SPEED_SUPER;
+ bdc->gadget.speed = USB_SPEED_UNKNOWN;
+ bdc->gadget.dev.parent = bdc->dev;
+
+ bdc->gadget.sg_supported = false;
+
+
+ bdc->gadget.name = BRCM_BDC_NAME;
+ ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt,
+ IRQF_SHARED , BRCM_BDC_NAME, bdc);
+ if (ret) {
+ dev_err(bdc->dev,
+ "failed to request irq #%d %d\n",
+ bdc->irq, ret);
+ return ret;
+ }
+
+ ret = bdc_init_ep(bdc);
+ if (ret) {
+ dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
+ return ret;
+ }
+
+ ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
+ if (ret) {
+ dev_err(bdc->dev, "failed to register udc\n");
+ goto err0;
+ }
+ usb_gadget_set_state(&bdc->gadget, USB_STATE_NOTATTACHED);
+ bdc->bdc_ep_array[1]->desc = &bdc_gadget_ep0_desc;
+ /*
+ * Allocate bd list for ep0 only, ep0 will be enabled on connect
+ * status report when the speed is known
+ */
+ ret = bdc_ep_enable(bdc->bdc_ep_array[1]);
+ if (ret) {
+ dev_err(bdc->dev, "fail to enable %s\n",
+ bdc->bdc_ep_array[1]->name);
+ goto err1;
+ }
+ INIT_DELAYED_WORK(&bdc->func_wake_notify, bdc_func_wake_timer);
+ /* Enable Interrupts */
+ temp = bdc_readl(bdc->regs, BDC_BDCSC);
+ temp |= BDC_GIE;
+ bdc_writel(bdc->regs, BDC_BDCSC, temp);
+ return 0;
+err1:
+ usb_del_gadget_udc(&bdc->gadget);
+err0:
+ bdc_free_ep(bdc);
+
+ return ret;
+}
+
+void bdc_udc_exit(struct bdc *bdc)
+{
+ dev_dbg(bdc->dev, "%s()\n", __func__);
+ bdc_ep_disable(bdc->bdc_ep_array[1]);
+ usb_del_gadget_udc(&bdc->gadget);
+ bdc_free_ep(bdc);
+}
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 81dc5959e36b..1c69c760408e 100644
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -367,19 +367,22 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
dum_hcd->active)
dum_hcd->resuming = 0;
- /* if !connected or reset */
+ /* Currently !connected or in reset */
if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 ||
(dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
- /*
- * We're connected and not reset (reset occurred now),
- * and driver attached - disconnect!
- */
- if ((dum_hcd->old_status & USB_PORT_STAT_CONNECTION) != 0 &&
- (dum_hcd->old_status & USB_PORT_STAT_RESET) == 0 &&
- dum->driver) {
+ unsigned disconnect = USB_PORT_STAT_CONNECTION &
+ dum_hcd->old_status & (~dum_hcd->port_status);
+ unsigned reset = USB_PORT_STAT_RESET &
+ (~dum_hcd->old_status) & dum_hcd->port_status;
+
+ /* Report reset and disconnect events to the driver */
+ if (dum->driver && (disconnect || reset)) {
stop_activity(dum);
spin_unlock(&dum->lock);
- dum->driver->disconnect(&dum->gadget);
+ if (reset)
+ usb_gadget_udc_reset(&dum->gadget, dum->driver);
+ else
+ dum->driver->disconnect(&dum->gadget);
spin_lock(&dum->lock);
}
} else if (dum_hcd->active != dum_hcd->old_active) {
@@ -851,8 +854,7 @@ static int dummy_pullup(struct usb_gadget *_gadget, int value)
static int dummy_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int dummy_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int dummy_udc_stop(struct usb_gadget *g);
static const struct usb_gadget_ops dummy_ops = {
.get_frame = dummy_g_get_frame,
@@ -908,23 +910,16 @@ static int dummy_udc_start(struct usb_gadget *g,
*/
dum->devstatus = 0;
-
dum->driver = driver;
- dev_dbg(udc_dev(dum), "binding gadget driver '%s'\n",
- driver->driver.name);
+
return 0;
}
-static int dummy_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int dummy_udc_stop(struct usb_gadget *g)
{
struct dummy_hcd *dum_hcd = gadget_to_dummy_hcd(g);
struct dummy *dum = dum_hcd->dum;
- if (driver)
- dev_dbg(udc_dev(dum), "unregister gadget driver '%s'\n",
- driver->driver.name);
-
dum->driver = NULL;
return 0;
@@ -2370,7 +2365,6 @@ static void dummy_stop(struct usb_hcd *hcd)
dum = hcd_to_dummy_hcd(hcd)->dum;
device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs);
- usb_gadget_unregister_driver(dum->driver);
dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n");
}
diff --git a/drivers/usb/gadget/udc/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index 1d315921bf34..1ca52e11eb98 100644
--- a/drivers/usb/gadget/udc/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
@@ -1053,8 +1053,7 @@ static void fotg210_init(struct fotg210_udc *fotg210)
iowrite32(value, fotg210->reg + FOTG210_DMISGR0);
}
-static int fotg210_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int fotg210_udc_stop(struct usb_gadget *g)
{
struct fotg210_udc *fotg210 = gadget_to_fotg210(g);
unsigned long flags;
diff --git a/drivers/usb/gadget/udc/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index dd18ea38e391..d201f9a89ce3 100644
--- a/drivers/usb/gadget/udc/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -1887,8 +1887,7 @@ static int qe_get_frame(struct usb_gadget *gadget)
static int fsl_qe_start(struct usb_gadget *gadget,
struct usb_gadget_driver *driver);
-static int fsl_qe_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver);
+static int fsl_qe_stop(struct usb_gadget *gadget);
/* defined in usb_gadget.h */
static const struct usb_gadget_ops qe_gadget_ops = {
@@ -1918,7 +1917,7 @@ static int reset_queues(struct qe_udc *udc)
/* report disconnect; the driver is already quiesced */
spin_unlock(&udc->lock);
- udc->driver->disconnect(&udc->gadget);
+ usb_gadget_udc_reset(&udc->gadget, udc->driver);
spin_lock(&udc->lock);
return 0;
@@ -2305,13 +2304,10 @@ static int fsl_qe_start(struct usb_gadget *gadget,
udc->ep0_dir = USB_DIR_OUT;
spin_unlock_irqrestore(&udc->lock, flags);
- dev_info(udc->dev, "%s bind to driver %s\n", udc->gadget.name,
- driver->driver.name);
return 0;
}
-static int fsl_qe_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int fsl_qe_stop(struct usb_gadget *gadget)
{
struct qe_udc *udc;
struct qe_ep *loop_ep;
@@ -2336,8 +2332,6 @@ static int fsl_qe_stop(struct usb_gadget *gadget,
udc->driver = NULL;
- dev_info(udc->dev, "unregistered gadget driver '%s'\r\n",
- driver->driver.name);
return 0;
}
@@ -2538,7 +2532,6 @@ static int qe_udc_probe(struct platform_device *ofdev)
/* create a buf for ZLP send, need to remain zeroed */
udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL);
if (udc->nullbuf == NULL) {
- dev_err(udc->dev, "cannot alloc nullbuf\n");
ret = -ENOMEM;
goto err3;
}
diff --git a/drivers/usb/gadget/udc/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index c3620791a315..f340181da23c 100644
--- a/drivers/usb/gadget/udc/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -1236,9 +1236,8 @@ static int fsl_pullup(struct usb_gadget *gadget, int is_on)
static int fsl_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int fsl_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
-/* defined in gadget.h */
+static int fsl_udc_stop(struct usb_gadget *g);
+
static const struct usb_gadget_ops fsl_gadget_ops = {
.get_frame = fsl_get_frame,
.wakeup = fsl_wakeup,
@@ -1772,7 +1771,7 @@ static void bus_resume(struct fsl_udc *udc)
}
/* Clear up all ep queues */
-static int reset_queues(struct fsl_udc *udc)
+static int reset_queues(struct fsl_udc *udc, bool bus_reset)
{
u8 pipe;
@@ -1781,7 +1780,10 @@ static int reset_queues(struct fsl_udc *udc)
/* report disconnect; the driver is already quiesced */
spin_unlock(&udc->lock);
- udc->driver->disconnect(&udc->gadget);
+ if (bus_reset)
+ usb_gadget_udc_reset(&udc->gadget, udc->driver);
+ else
+ udc->driver->disconnect(&udc->gadget);
spin_lock(&udc->lock);
return 0;
@@ -1835,7 +1837,7 @@ static void reset_irq(struct fsl_udc *udc)
udc->bus_reset = 1;
/* Reset all the queues, include XD, dTD, EP queue
* head and TR Queue */
- reset_queues(udc);
+ reset_queues(udc, true);
udc->usb_state = USB_STATE_DEFAULT;
} else {
VDBG("Controller reset");
@@ -1844,7 +1846,7 @@ static void reset_irq(struct fsl_udc *udc)
dr_controller_setup(udc);
/* Reset all internal used Queues */
- reset_queues(udc);
+ reset_queues(udc, false);
ep0_setup(udc);
@@ -1975,8 +1977,7 @@ static int fsl_udc_start(struct usb_gadget *g,
}
/* Disconnect from gadget driver */
-static int fsl_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int fsl_udc_stop(struct usb_gadget *g)
{
struct fsl_ep *loop_ep;
unsigned long flags;
diff --git a/drivers/usb/gadget/udc/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index 8286df72add4..a1b33f534b52 100644
--- a/drivers/usb/gadget/udc/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -1320,8 +1320,7 @@ static int fusb300_udc_start(struct usb_gadget *g,
return 0;
}
-static int fusb300_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int fusb300_udc_stop(struct usb_gadget *g)
{
struct fusb300 *fusb300 = to_fusb300(g);
diff --git a/drivers/usb/gadget/udc/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index bf9c5ef8b56b..5b9176e7202a 100644
--- a/drivers/usb/gadget/udc/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
@@ -992,8 +992,7 @@ static int goku_get_frame(struct usb_gadget *_gadget)
static int goku_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int goku_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int goku_udc_stop(struct usb_gadget *g);
static const struct usb_gadget_ops goku_ops = {
.get_frame = goku_get_frame,
@@ -1364,8 +1363,7 @@ static void stop_activity(struct goku_udc *dev)
udc_enable(dev);
}
-static int goku_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int goku_udc_stop(struct usb_gadget *g)
{
struct goku_udc *dev = to_goku_udc(g);
unsigned long flags;
diff --git a/drivers/usb/gadget/udc/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index 1b3048a6a2a3..320df9a250ff 100644
--- a/drivers/usb/gadget/udc/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -1932,14 +1932,10 @@ static int gr_udc_start(struct usb_gadget *gadget,
spin_unlock(&dev->lock);
- dev_info(dev->dev, "Started with gadget driver '%s'\n",
- driver->driver.name);
-
return 0;
}
-static int gr_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int gr_udc_stop(struct usb_gadget *gadget)
{
struct gr_udc *dev = to_gr_udc(gadget);
unsigned long flags;
@@ -1951,8 +1947,6 @@ static int gr_udc_stop(struct usb_gadget *gadget,
spin_unlock_irqrestore(&dev->lock, flags);
- dev_info(dev->dev, "Stopped\n");
-
return 0;
}
diff --git a/drivers/usb/gadget/udc/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index feab0bac8fdc..976529631c19 100644
--- a/drivers/usb/gadget/udc/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -582,8 +582,7 @@ static void create_debug_file(struct lpc32xx_udc *udc)
static void remove_debug_file(struct lpc32xx_udc *udc)
{
- if (udc->pde)
- debugfs_remove(udc->pde);
+ debugfs_remove(udc->pde);
}
#else
@@ -2559,7 +2558,7 @@ static int lpc32xx_pullup(struct usb_gadget *gadget, int is_on)
}
static int lpc32xx_start(struct usb_gadget *, struct usb_gadget_driver *);
-static int lpc32xx_stop(struct usb_gadget *, struct usb_gadget_driver *);
+static int lpc32xx_stop(struct usb_gadget *);
static const struct usb_gadget_ops lpc32xx_udc_ops = {
.get_frame = lpc32xx_get_frame,
@@ -2961,15 +2960,11 @@ static int lpc32xx_start(struct usb_gadget *gadget,
return 0;
}
-static int lpc32xx_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int lpc32xx_stop(struct usb_gadget *gadget)
{
int i;
struct lpc32xx_udc *udc = to_udc(gadget);
- if (!driver || driver != udc->driver)
- return -EINVAL;
-
for (i = IRQ_USB_LP; i <= IRQ_USB_ATX; i++)
disable_irq(udc->udp_irq[i]);
diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 898565687a8c..ef3f73dd9099 100644
--- a/drivers/usb/gadget/udc/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1142,7 +1142,7 @@ static void irq_device_state(struct m66592 *m66592)
m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0);
if (dvsq == M66592_DS_DFLT) { /* bus reset */
- m66592->driver->disconnect(&m66592->gadget);
+ usb_gadget_udc_reset(&m66592->gadget, m66592->driver);
m66592_update_usb_speed(m66592);
}
if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG)
@@ -1485,8 +1485,7 @@ static int m66592_udc_start(struct usb_gadget *g,
return 0;
}
-static int m66592_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int m66592_udc_stop(struct usb_gadget *g)
{
struct m66592 *m66592 = to_m66592(g);
diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 046a1f808b0d..ea422ac79990 100644
--- a/drivers/usb/gadget/udc/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
@@ -1266,8 +1266,7 @@ static int mv_u3d_start(struct usb_gadget *g,
return 0;
}
-static int mv_u3d_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int mv_u3d_stop(struct usb_gadget *g)
{
struct mv_u3d *u3d = container_of(g, struct mv_u3d, gadget);
struct mv_usb_platform_data *pdata = dev_get_platdata(u3d->dev);
@@ -1284,7 +1283,7 @@ static int mv_u3d_stop(struct usb_gadget *g,
mv_u3d_controller_stop(u3d);
/* stop all usb activities */
u3d->gadget.speed = USB_SPEED_UNKNOWN;
- mv_u3d_stop_activity(u3d, driver);
+ mv_u3d_stop_activity(u3d, NULL);
mv_u3d_disable(u3d);
if (pdata->phy_deinit)
diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 3c5db80ae325..d4edd763e963 100644
--- a/drivers/usb/gadget/udc/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
@@ -1223,7 +1223,7 @@ static int mv_udc_pullup(struct usb_gadget *gadget, int is_on)
}
static int mv_udc_start(struct usb_gadget *, struct usb_gadget_driver *);
-static int mv_udc_stop(struct usb_gadget *, struct usb_gadget_driver *);
+static int mv_udc_stop(struct usb_gadget *);
/* device controller usb_gadget_ops structure */
static const struct usb_gadget_ops mv_ops = {
@@ -1307,6 +1307,23 @@ static void nuke(struct mv_ep *ep, int status)
}
}
+static void gadget_reset(struct mv_udc *udc, struct usb_gadget_driver *driver)
+{
+ struct mv_ep *ep;
+
+ nuke(&udc->eps[0], -ESHUTDOWN);
+
+ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
+ nuke(ep, -ESHUTDOWN);
+ }
+
+ /* report reset; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&udc->lock);
+ usb_gadget_udc_reset(&udc->gadget, driver);
+ spin_lock(&udc->lock);
+ }
+}
/* stop all USB activities */
static void stop_activity(struct mv_udc *udc, struct usb_gadget_driver *driver)
{
@@ -1371,8 +1388,7 @@ static int mv_udc_start(struct usb_gadget *gadget,
return 0;
}
-static int mv_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int mv_udc_stop(struct usb_gadget *gadget)
{
struct mv_udc *udc;
unsigned long flags;
@@ -1386,7 +1402,7 @@ static int mv_udc_stop(struct usb_gadget *gadget,
/* stop all usb activities */
udc->gadget.speed = USB_SPEED_UNKNOWN;
- stop_activity(udc, driver);
+ stop_activity(udc, NULL);
mv_udc_disable(udc);
spin_unlock_irqrestore(&udc->lock, flags);
@@ -1882,7 +1898,7 @@ static void irq_process_reset(struct mv_udc *udc)
dev_info(&udc->dev->dev, "usb bus reset\n");
udc->usb_state = USB_STATE_DEFAULT;
/* reset all the queues, stop all USB activities */
- stop_activity(udc, udc->driver);
+ gadget_reset(udc, udc->driver);
} else {
dev_info(&udc->dev->dev, "USB reset portsc 0x%x\n",
readl(&udc->op_regs->portsc));
@@ -2107,10 +2123,8 @@ static int mv_udc_probe(struct platform_device *pdev)
}
udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
- if (udc == NULL) {
- dev_err(&pdev->dev, "failed to allocate memory for udc\n");
+ if (udc == NULL)
return -ENOMEM;
- }
udc->done = &release_done;
udc->pdata = dev_get_platdata(&pdev->dev);
@@ -2207,7 +2221,6 @@ static int mv_udc_probe(struct platform_device *pdev)
size = udc->max_eps * sizeof(struct mv_ep) *2;
udc->eps = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
if (udc->eps == NULL) {
- dev_err(&pdev->dev, "allocate ep memory failed\n");
retval = -ENOMEM;
goto err_destroy_dma;
}
@@ -2216,7 +2229,6 @@ static int mv_udc_probe(struct platform_device *pdev)
udc->status_req = devm_kzalloc(&pdev->dev, sizeof(struct mv_req),
GFP_KERNEL);
if (!udc->status_req) {
- dev_err(&pdev->dev, "allocate status_req memory failed\n");
retval = -ENOMEM;
goto err_destroy_dma;
}
diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c
index 84d7162a8022..3a90856625f2 100644
--- a/drivers/usb/gadget/udc/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -1169,8 +1169,7 @@ net2272_pullup(struct usb_gadget *_gadget, int is_on)
static int net2272_start(struct usb_gadget *_gadget,
struct usb_gadget_driver *driver);
-static int net2272_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver);
+static int net2272_stop(struct usb_gadget *_gadget);
static const struct usb_gadget_ops net2272_ops = {
.get_frame = net2272_get_frame,
@@ -1471,8 +1470,6 @@ static int net2272_start(struct usb_gadget *_gadget,
*/
net2272_ep0_start(dev);
- dev_dbg(dev->dev, "%s ready\n", driver->driver.name);
-
return 0;
}
@@ -1502,8 +1499,7 @@ stop_activity(struct net2272 *dev, struct usb_gadget_driver *driver)
net2272_usb_reinit(dev);
}
-static int net2272_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver)
+static int net2272_stop(struct usb_gadget *_gadget)
{
struct net2272 *dev;
unsigned long flags;
@@ -1511,12 +1507,11 @@ static int net2272_stop(struct usb_gadget *_gadget,
dev = container_of(_gadget, struct net2272, gadget);
spin_lock_irqsave(&dev->lock, flags);
- stop_activity(dev, driver);
+ stop_activity(dev, NULL);
spin_unlock_irqrestore(&dev->lock, flags);
dev->driver = NULL;
- dev_dbg(dev->dev, "unregistered driver '%s'\n", driver->driver.name);
return 0;
}
@@ -1987,17 +1982,42 @@ net2272_handle_stat1_irqs(struct net2272 *dev, u8 stat)
mask = (1 << USB_HIGH_SPEED) | (1 << USB_FULL_SPEED);
if (stat & tmp) {
+ bool reset = false;
+ bool disconnect = false;
+
+ /*
+ * Ignore disconnects and resets if the speed hasn't been set.
+ * VBUS can bounce and there's always an initial reset.
+ */
net2272_write(dev, IRQSTAT1, tmp);
- if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
- ((net2272_read(dev, USBCTL1) & mask) == 0))
- || ((net2272_read(dev, USBCTL1) & (1 << VBUS_PIN))
- == 0))
- && (dev->gadget.speed != USB_SPEED_UNKNOWN)) {
- dev_dbg(dev->dev, "disconnect %s\n",
- dev->driver->driver.name);
- stop_activity(dev, dev->driver);
- net2272_ep0_start(dev);
- return;
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
+ if ((stat & (1 << VBUS_INTERRUPT)) &&
+ (net2272_read(dev, USBCTL1) &
+ (1 << VBUS_PIN)) == 0) {
+ disconnect = true;
+ dev_dbg(dev->dev, "disconnect %s\n",
+ dev->driver->driver.name);
+ } else if ((stat & (1 << ROOT_PORT_RESET_INTERRUPT)) &&
+ (net2272_read(dev, USBCTL1) & mask)
+ == 0) {
+ reset = true;
+ dev_dbg(dev->dev, "reset %s\n",
+ dev->driver->driver.name);
+ }
+
+ if (disconnect || reset) {
+ stop_activity(dev, dev->driver);
+ net2272_ep0_start(dev);
+ spin_unlock(&dev->lock);
+ if (reset)
+ usb_gadget_udc_reset
+ (&dev->gadget, dev->driver);
+ else
+ (dev->driver->disconnect)
+ (&dev->gadget);
+ spin_lock(&dev->lock);
+ return;
+ }
}
stat &= ~tmp;
@@ -2200,18 +2220,8 @@ static void
net2272_remove(struct net2272 *dev)
{
usb_del_gadget_udc(&dev->gadget);
-
- /* start with the driver above us */
- if (dev->driver) {
- /* should have been done already by driver model core */
- dev_warn(dev->dev, "pci remove, driver '%s' is still registered\n",
- dev->driver->driver.name);
- usb_gadget_unregister_driver(dev->driver);
- }
-
free_irq(dev->irq, dev);
iounmap(dev->base_addr);
-
device_remove_file(dev->dev, &dev_attr_registers);
dev_info(dev->dev, "unbind\n");
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
index 8d13337e2dde..d6411e0a8e03 100644
--- a/drivers/usb/gadget/udc/net2280.c
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -1118,10 +1118,10 @@ static void scan_dma_completions(struct net2280_ep *ep)
break;
} else if (!ep->is_in &&
(req->req.length % ep->ep.maxpacket) != 0) {
- tmp = readl(&ep->regs->ep_stat);
if (ep->dev->quirks & PLX_SUPERSPEED)
return dma_done(ep, req, tmp, 0);
+ tmp = readl(&ep->regs->ep_stat);
/* AVOID TROUBLE HERE by not issuing short reads from
* your gadget driver. That helps avoids errata 0121,
* 0122, and 0124; not all cases trigger the warning.
@@ -1548,8 +1548,7 @@ static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
static int net2280_start(struct usb_gadget *_gadget,
struct usb_gadget_driver *driver);
-static int net2280_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver);
+static int net2280_stop(struct usb_gadget *_gadget);
static const struct usb_gadget_ops net2280_ops = {
.get_frame = net2280_get_frame,
@@ -2397,11 +2396,6 @@ static int net2280_start(struct usb_gadget *_gadget,
ep0_start(dev);
- ep_dbg(dev, "%s ready, usbctl %08x stdrsp %08x\n",
- driver->driver.name,
- readl(&dev->usb->usbctl),
- readl(&dev->usb->stdrsp));
-
/* pci writes may still be posted */
return 0;
@@ -2437,8 +2431,7 @@ static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
usb_reinit(dev);
}
-static int net2280_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver)
+static int net2280_stop(struct usb_gadget *_gadget)
{
struct net2280 *dev;
unsigned long flags;
@@ -2446,11 +2439,9 @@ static int net2280_stop(struct usb_gadget *_gadget,
dev = container_of(_gadget, struct net2280, gadget);
spin_lock_irqsave(&dev->lock, flags);
- stop_activity(dev, driver);
+ stop_activity(dev, NULL);
spin_unlock_irqrestore(&dev->lock, flags);
- dev->driver = NULL;
-
net2280_led_active(dev, 0);
/* Disable full-speed test mode */
@@ -2460,8 +2451,7 @@ static int net2280_stop(struct usb_gadget *_gadget,
device_remove_file(&dev->pdev->dev, &dev_attr_function);
device_remove_file(&dev->pdev->dev, &dev_attr_queues);
- ep_dbg(dev, "unregistered driver '%s'\n",
- driver ? driver->driver.name : "");
+ dev->driver = NULL;
return 0;
}
@@ -3318,17 +3308,42 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
* only indicates a change in the reset state).
*/
if (stat & tmp) {
+ bool reset = false;
+ bool disconnect = false;
+
+ /*
+ * Ignore disconnects and resets if the speed hasn't been set.
+ * VBUS can bounce and there's always an initial reset.
+ */
writel(tmp, &dev->regs->irqstat1);
- if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
- ((readl(&dev->usb->usbstat) & mask) == 0)) ||
- ((readl(&dev->usb->usbctl) &
- BIT(VBUS_PIN)) == 0)) &&
- (dev->gadget.speed != USB_SPEED_UNKNOWN)) {
- ep_dbg(dev, "disconnect %s\n",
- dev->driver->driver.name);
- stop_activity(dev, dev->driver);
- ep0_start(dev);
- return;
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN) {
+ if ((stat & BIT(VBUS_INTERRUPT)) &&
+ (readl(&dev->usb->usbctl) &
+ BIT(VBUS_PIN)) == 0) {
+ disconnect = true;
+ ep_dbg(dev, "disconnect %s\n",
+ dev->driver->driver.name);
+ } else if ((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
+ (readl(&dev->usb->usbstat) & mask)
+ == 0) {
+ reset = true;
+ ep_dbg(dev, "reset %s\n",
+ dev->driver->driver.name);
+ }
+
+ if (disconnect || reset) {
+ stop_activity(dev, dev->driver);
+ ep0_start(dev);
+ spin_unlock(&dev->lock);
+ if (reset)
+ usb_gadget_udc_reset
+ (&dev->gadget, dev->driver);
+ else
+ (dev->driver->disconnect)
+ (&dev->gadget);
+ spin_lock(&dev->lock);
+ return;
+ }
}
stat &= ~tmp;
diff --git a/drivers/usb/gadget/udc/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index dcdfea46003b..534b85c07feb 100644
--- a/drivers/usb/gadget/udc/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -1311,8 +1311,7 @@ static int omap_pullup(struct usb_gadget *gadget, int is_on)
static int omap_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int omap_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int omap_udc_stop(struct usb_gadget *g);
static const struct usb_gadget_ops omap_gadget_ops = {
.get_frame = omap_get_frame,
@@ -2102,8 +2101,7 @@ done:
return status;
}
-static int omap_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int omap_udc_stop(struct usb_gadget *g)
{
unsigned long flags;
int status = -ENODEV;
diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index ccbe3d4a2a50..1c7379ac2379 100644
--- a/drivers/usb/gadget/udc/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
@@ -1240,8 +1240,8 @@ static int pch_udc_pcd_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
static int pch_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int pch_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int pch_udc_stop(struct usb_gadget *g);
+
static const struct usb_gadget_ops pch_udc_ops = {
.get_frame = pch_udc_pcd_get_frame,
.wakeup = pch_udc_pcd_wakeup,
@@ -2592,9 +2592,9 @@ static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev)
/* Complete request queue */
empty_req_queue(ep);
}
- if (dev->driver && dev->driver->disconnect) {
+ if (dev->driver) {
spin_unlock(&dev->lock);
- dev->driver->disconnect(&dev->gadget);
+ usb_gadget_udc_reset(&dev->gadget, dev->driver);
spin_lock(&dev->lock);
}
}
@@ -3008,8 +3008,7 @@ static int pch_udc_start(struct usb_gadget *g,
return 0;
}
-static int pch_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int pch_udc_stop(struct usb_gadget *g)
{
struct pch_udc_dev *dev = to_pch_udc(g);
diff --git a/drivers/usb/gadget/udc/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 42f7eeb8ff6f..347a05b5afc1 100644
--- a/drivers/usb/gadget/udc/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -998,8 +998,7 @@ static int pxa25x_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
static int pxa25x_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int pxa25x_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int pxa25x_udc_stop(struct usb_gadget *g);
static const struct usb_gadget_ops pxa25x_udc_ops = {
.get_frame = pxa25x_udc_get_frame,
@@ -1135,11 +1134,7 @@ static const struct file_operations debug_fops = {
dev->debugfs_udc = debugfs_create_file(dev->gadget.name, \
S_IRUGO, NULL, dev, &debug_fops); \
} while (0)
-#define remove_debug_files(dev) \
- do { \
- if (dev->debugfs_udc) \
- debugfs_remove(dev->debugfs_udc); \
- } while (0)
+#define remove_debug_files(dev) debugfs_remove(dev->debugfs_udc)
#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
@@ -1285,6 +1280,33 @@ bind_fail:
}
static void
+reset_gadget(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect drivers more than once */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* prevent new request submissions, kill any outstanding requests */
+ for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) {
+ struct pxa25x_ep *ep = &dev->ep[i];
+
+ ep->stopped = 1;
+ nuke(ep, -ESHUTDOWN);
+ }
+ del_timer_sync(&dev->timer);
+
+ /* report reset; the driver is already quiesced */
+ if (driver)
+ usb_gadget_udc_reset(&dev->gadget, driver);
+
+ /* re-init driver-visible data structures */
+ udc_reinit(dev);
+}
+
+static void
stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
{
int i;
@@ -1311,15 +1333,14 @@ stop_activity(struct pxa25x_udc *dev, struct usb_gadget_driver *driver)
udc_reinit(dev);
}
-static int pxa25x_udc_stop(struct usb_gadget*g,
- struct usb_gadget_driver *driver)
+static int pxa25x_udc_stop(struct usb_gadget*g)
{
struct pxa25x_udc *dev = to_pxa25x(g);
local_irq_disable();
dev->pullup = 0;
pullup(dev);
- stop_activity(dev, driver);
+ stop_activity(dev, NULL);
local_irq_enable();
if (!IS_ERR_OR_NULL(dev->transceiver))
@@ -1723,7 +1744,7 @@ pxa25x_udc_irq(int irq, void *_dev)
/* reset driver and endpoints,
* in case that's not yet done
*/
- stop_activity (dev, dev->driver);
+ reset_gadget(dev, dev->driver);
} else {
DBG(DBG_VERBOSE, "USB reset end\n");
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index 4868369eeec6..9b03fab13707 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -22,10 +22,13 @@
#include <linux/clk.h>
#include <linux/irq.h>
#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/slab.h>
#include <linux/prefetch.h>
#include <linux/byteorder/generic.h>
#include <linux/platform_data/pxa2xx_udc.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
#include <linux/usb.h>
#include <linux/usb/ch9.h>
@@ -1507,18 +1510,13 @@ static struct usb_ep_ops pxa_ep_ops = {
*/
static void dplus_pullup(struct pxa_udc *udc, int on)
{
- if (on) {
- if (gpio_is_valid(udc->mach->gpio_pullup))
- gpio_set_value(udc->mach->gpio_pullup,
- !udc->mach->gpio_pullup_inverted);
- if (udc->mach->udc_command)
- udc->mach->udc_command(PXA2XX_UDC_CMD_CONNECT);
- } else {
- if (gpio_is_valid(udc->mach->gpio_pullup))
- gpio_set_value(udc->mach->gpio_pullup,
- udc->mach->gpio_pullup_inverted);
- if (udc->mach->udc_command)
- udc->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
+ if (udc->gpiod) {
+ gpiod_set_value(udc->gpiod, on);
+ } else if (udc->udc_command) {
+ if (on)
+ udc->udc_command(PXA2XX_UDC_CMD_CONNECT);
+ else
+ udc->udc_command(PXA2XX_UDC_CMD_DISCONNECT);
}
udc->pullup_on = on;
}
@@ -1609,7 +1607,7 @@ static int pxa_udc_pullup(struct usb_gadget *_gadget, int is_active)
{
struct pxa_udc *udc = to_gadget_udc(_gadget);
- if (!gpio_is_valid(udc->mach->gpio_pullup) && !udc->mach->udc_command)
+ if (!udc->gpiod && !udc->udc_command)
return -EOPNOTSUPP;
dplus_pullup(udc, is_active);
@@ -1671,8 +1669,7 @@ static int pxa_udc_vbus_draw(struct usb_gadget *_gadget, unsigned mA)
static int pxa27x_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int pxa27x_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int pxa27x_udc_stop(struct usb_gadget *g);
static const struct usb_gadget_ops pxa_udc_ops = {
.get_frame = pxa_udc_get_frame,
@@ -1701,10 +1698,10 @@ static void udc_disable(struct pxa_udc *udc)
udc_writel(udc, UDCICR1, 0);
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
- clk_disable(udc->clk);
ep0_idle(udc);
udc->gadget.speed = USB_SPEED_UNKNOWN;
+ clk_disable(udc->clk);
udc->enabled = 0;
}
@@ -1757,16 +1754,16 @@ static void udc_enable(struct pxa_udc *udc)
if (udc->enabled)
return;
+ clk_enable(udc->clk);
udc_writel(udc, UDCICR0, 0);
udc_writel(udc, UDCICR1, 0);
udc_clear_mask_UDCCR(udc, UDCCR_UDE);
- clk_enable(udc->clk);
-
ep0_idle(udc);
udc->gadget.speed = USB_SPEED_FULL;
memset(&udc->stats, 0, sizeof(udc->stats));
+ pxa_eps_setup(udc);
udc_set_mask_UDCCR(udc, UDCCR_UDE);
ep_write_UDCCSR(&udc->pxa_ep[0], UDCCSR0_ACM);
udelay(2);
@@ -1859,12 +1856,11 @@ static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver)
*
* Returns 0 if no error, -ENODEV, -EINVAL otherwise
*/
-static int pxa27x_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int pxa27x_udc_stop(struct usb_gadget *g)
{
struct pxa_udc *udc = to_pxa(g);
- stop_activity(udc, driver);
+ stop_activity(udc, NULL);
udc_disable(udc);
dplus_pullup(udc, 0);
@@ -2404,6 +2400,14 @@ static struct pxa_udc memory = {
}
};
+#if defined(CONFIG_OF)
+static struct of_device_id udc_pxa_dt_ids[] = {
+ { .compatible = "marvell,pxa270-udc" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, udc_pxa_dt_ids);
+#endif
+
/**
* pxa_udc_probe - probes the udc device
* @_dev: platform device
@@ -2416,81 +2420,77 @@ static int pxa_udc_probe(struct platform_device *pdev)
struct resource *regs;
struct pxa_udc *udc = &memory;
int retval = 0, gpio;
+ struct pxa2xx_udc_mach_info *mach = dev_get_platdata(&pdev->dev);
+ unsigned long gpio_flags;
+
+ if (mach) {
+ gpio_flags = mach->gpio_pullup_inverted ? GPIOF_ACTIVE_LOW : 0;
+ gpio = mach->gpio_pullup;
+ if (gpio_is_valid(gpio)) {
+ retval = devm_gpio_request_one(&pdev->dev, gpio,
+ gpio_flags,
+ "USB D+ pullup");
+ if (retval)
+ return retval;
+ udc->gpiod = gpio_to_desc(mach->gpio_pullup);
+ }
+ udc->udc_command = mach->udc_command;
+ } else {
+ udc->gpiod = devm_gpiod_get(&pdev->dev, NULL);
+ }
regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!regs)
- return -ENXIO;
+ udc->regs = devm_ioremap_resource(&pdev->dev, regs);
+ if (IS_ERR(udc->regs))
+ return PTR_ERR(udc->regs);
udc->irq = platform_get_irq(pdev, 0);
if (udc->irq < 0)
return udc->irq;
udc->dev = &pdev->dev;
- udc->mach = dev_get_platdata(&pdev->dev);
udc->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
- gpio = udc->mach->gpio_pullup;
- if (gpio_is_valid(gpio)) {
- retval = gpio_request(gpio, "USB D+ pullup");
- if (retval == 0)
- gpio_direction_output(gpio,
- udc->mach->gpio_pullup_inverted);
- }
- if (retval) {
- dev_err(&pdev->dev, "Couldn't request gpio %d : %d\n",
- gpio, retval);
- return retval;
+ if (IS_ERR(udc->gpiod)) {
+ dev_err(&pdev->dev, "Couldn't find or request D+ gpio : %ld\n",
+ PTR_ERR(udc->gpiod));
+ return PTR_ERR(udc->gpiod);
}
+ if (udc->gpiod)
+ gpiod_direction_output(udc->gpiod, 0);
+
+ udc->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(udc->clk))
+ return PTR_ERR(udc->clk);
- udc->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(udc->clk)) {
- retval = PTR_ERR(udc->clk);
- goto err_clk;
- }
retval = clk_prepare(udc->clk);
if (retval)
- goto err_clk_prepare;
-
- retval = -ENOMEM;
- udc->regs = ioremap(regs->start, resource_size(regs));
- if (!udc->regs) {
- dev_err(&pdev->dev, "Unable to map UDC I/O memory\n");
- goto err_map;
- }
+ return retval;
udc->vbus_sensed = 0;
the_controller = udc;
platform_set_drvdata(pdev, udc);
udc_init_data(udc);
- pxa_eps_setup(udc);
/* irq setup after old hardware state is cleaned up */
- retval = request_irq(udc->irq, pxa_udc_irq,
- IRQF_SHARED, driver_name, udc);
+ retval = devm_request_irq(&pdev->dev, udc->irq, pxa_udc_irq,
+ IRQF_SHARED, driver_name, udc);
if (retval != 0) {
dev_err(udc->dev, "%s: can't get irq %i, err %d\n",
driver_name, udc->irq, retval);
- goto err_irq;
+ goto err;
}
retval = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (retval)
- goto err_add_udc;
+ goto err;
pxa_init_debugfs(udc);
-
+ if (should_enable_udc(udc))
+ udc_enable(udc);
return 0;
-
-err_add_udc:
- free_irq(udc->irq, udc);
-err_irq:
- iounmap(udc->regs);
-err_map:
+err:
clk_unprepare(udc->clk);
-err_clk_prepare:
- clk_put(udc->clk);
- udc->clk = NULL;
-err_clk:
return retval;
}
@@ -2501,22 +2501,15 @@ err_clk:
static int pxa_udc_remove(struct platform_device *_dev)
{
struct pxa_udc *udc = platform_get_drvdata(_dev);
- int gpio = udc->mach->gpio_pullup;
usb_del_gadget_udc(&udc->gadget);
- usb_gadget_unregister_driver(udc->driver);
- free_irq(udc->irq, udc);
pxa_cleanup_debugfs(udc);
- if (gpio_is_valid(gpio))
- gpio_free(gpio);
usb_put_phy(udc->transceiver);
udc->transceiver = NULL;
the_controller = NULL;
clk_unprepare(udc->clk);
- clk_put(udc->clk);
- iounmap(udc->regs);
return 0;
}
@@ -2546,19 +2539,11 @@ extern void pxa27x_clear_otgph(void);
*/
static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state)
{
- int i;
struct pxa_udc *udc = platform_get_drvdata(_dev);
struct pxa_ep *ep;
ep = &udc->pxa_ep[0];
udc->udccsr0 = udc_ep_readl(ep, UDCCSR);
- for (i = 1; i < NR_PXA_ENDPOINTS; i++) {
- ep = &udc->pxa_ep[i];
- ep->udccsr_value = udc_ep_readl(ep, UDCCSR);
- ep->udccr_value = udc_ep_readl(ep, UDCCR);
- ep_dbg(ep, "udccsr:0x%03x, udccr:0x%x\n",
- ep->udccsr_value, ep->udccr_value);
- }
udc_disable(udc);
udc->pullup_resume = udc->pullup_on;
@@ -2576,19 +2561,11 @@ static int pxa_udc_suspend(struct platform_device *_dev, pm_message_t state)
*/
static int pxa_udc_resume(struct platform_device *_dev)
{
- int i;
struct pxa_udc *udc = platform_get_drvdata(_dev);
struct pxa_ep *ep;
ep = &udc->pxa_ep[0];
udc_ep_writel(ep, UDCCSR, udc->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME));
- for (i = 1; i < NR_PXA_ENDPOINTS; i++) {
- ep = &udc->pxa_ep[i];
- udc_ep_writel(ep, UDCCSR, ep->udccsr_value);
- udc_ep_writel(ep, UDCCR, ep->udccr_value);
- ep_dbg(ep, "udccsr:0x%03x, udccr:0x%x\n",
- ep->udccsr_value, ep->udccr_value);
- }
dplus_pullup(udc, udc->pullup_resume);
if (should_enable_udc(udc))
@@ -2615,6 +2592,7 @@ static struct platform_driver udc_driver = {
.driver = {
.name = "pxa27x-udc",
.owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(udc_pxa_dt_ids),
},
.probe = pxa_udc_probe,
.remove = pxa_udc_remove,
diff --git a/drivers/usb/gadget/udc/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
index 28f2b53530f5..11e14232794b 100644
--- a/drivers/usb/gadget/udc/pxa27x_udc.h
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
@@ -420,7 +420,8 @@ struct udc_stats {
* @usb_gadget: udc gadget structure
* @driver: bound gadget (zero, g_ether, g_mass_storage, ...)
* @dev: device
- * @mach: machine info, used to activate specific GPIO
+ * @udc_command: machine specific function to activate D+ pullup
+ * @gpiod: gpio descriptor of gpio for D+ pullup (or NULL if none)
* @transceiver: external transceiver to handle vbus sense and D+ pullup
* @ep0state: control endpoint state machine state
* @stats: statistics on udc usage
@@ -446,7 +447,8 @@ struct pxa_udc {
struct usb_gadget gadget;
struct usb_gadget_driver *driver;
struct device *dev;
- struct pxa2xx_udc_mach_info *mach;
+ void (*udc_command)(int);
+ struct gpio_desc *gpiod;
struct usb_phy *transceiver;
enum ep0_state ep0state;
diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index f8186613b53e..06870da0b988 100644
--- a/drivers/usb/gadget/udc/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1345,7 +1345,7 @@ static void irq_device_state(struct r8a66597 *r8a66597)
if (dvsq == DS_DFLT) {
/* bus reset */
spin_unlock(&r8a66597->lock);
- r8a66597->driver->disconnect(&r8a66597->gadget);
+ usb_gadget_udc_reset(&r8a66597->gadget, r8a66597->driver);
spin_lock(&r8a66597->lock);
r8a66597_update_usb_speed(r8a66597);
}
@@ -1763,8 +1763,7 @@ static int r8a66597_start(struct usb_gadget *gadget,
return 0;
}
-static int r8a66597_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int r8a66597_stop(struct usb_gadget *gadget)
{
struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
unsigned long flags;
@@ -1846,10 +1845,7 @@ 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))
- return PTR_ERR(r8a66597->sudmac_reg);
-
- return 0;
+ return PTR_ERR_OR_ZERO(r8a66597->sudmac_reg);
}
static int r8a66597_probe(struct platform_device *pdev)
diff --git a/drivers/usb/gadget/udc/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index dfbf55797360..97d3a9144381 100644
--- a/drivers/usb/gadget/udc/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
@@ -1172,8 +1172,6 @@ static int s3c_hsudc_start(struct usb_gadget *gadget,
}
enable_irq(hsudc->irq);
- dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
-
s3c_hsudc_reconfig(hsudc);
pm_runtime_get_sync(hsudc->dev);
@@ -1190,8 +1188,7 @@ err_supplies:
return ret;
}
-static int s3c_hsudc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int s3c_hsudc_stop(struct usb_gadget *gadget)
{
struct s3c_hsudc *hsudc = to_hsudc(gadget);
unsigned long flags;
@@ -1199,11 +1196,7 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
if (!hsudc)
return -ENODEV;
- if (!driver || driver != hsudc->driver)
- return -EINVAL;
-
spin_lock_irqsave(&hsudc->lock, flags);
- hsudc->driver = NULL;
hsudc->gadget.speed = USB_SPEED_UNKNOWN;
s3c_hsudc_uninit_phy();
@@ -1220,9 +1213,8 @@ static int s3c_hsudc_stop(struct usb_gadget *gadget,
disable_irq(hsudc->irq);
regulator_bulk_disable(ARRAY_SIZE(hsudc->supplies), hsudc->supplies);
+ hsudc->driver = NULL;
- dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
- driver->driver.name);
return 0;
}
@@ -1267,10 +1259,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
hsudc = devm_kzalloc(&pdev->dev, sizeof(struct s3c_hsudc) +
sizeof(struct s3c_hsudc_ep) * pd->epnum,
GFP_KERNEL);
- if (!hsudc) {
- dev_err(dev, "cannot allocate memory\n");
+ if (!hsudc)
return -ENOMEM;
- }
platform_set_drvdata(pdev, dev);
hsudc->dev = dev;
diff --git a/drivers/usb/gadget/udc/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index ff423d15beff..2a8e36d31488 100644
--- a/drivers/usb/gadget/udc/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -1541,8 +1541,7 @@ static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
static int s3c2410_udc_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int s3c2410_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int s3c2410_udc_stop(struct usb_gadget *g);
static const struct usb_gadget_ops s3c2410_ops = {
.get_frame = s3c2410_udc_get_frame,
@@ -1683,8 +1682,7 @@ static int s3c2410_udc_start(struct usb_gadget *g,
return 0;
}
-static int s3c2410_udc_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int s3c2410_udc_stop(struct usb_gadget *g)
{
struct s3c2410_udc *udc = to_s3c2410(g);
diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index f2054659f25b..e31d574d8860 100644
--- a/drivers/usb/gadget/udc/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
@@ -174,8 +174,7 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
/**
* usb_gadget_udc_start - tells usb device controller to start up
- * @gadget: The gadget we want to get started
- * @driver: The driver we want to bind to @gadget
+ * @udc: The UDC to be started
*
* This call is issued by the UDC Class driver when it's about
* to register a gadget driver to the device controller, before
@@ -186,10 +185,9 @@ EXPORT_SYMBOL_GPL(usb_gadget_udc_reset);
*
* Returns zero on success, else negative errno.
*/
-static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static inline int usb_gadget_udc_start(struct usb_udc *udc)
{
- return gadget->ops->udc_start(gadget, driver);
+ return udc->gadget->ops->udc_start(udc->gadget, udc->driver);
}
/**
@@ -204,10 +202,9 @@ static inline int usb_gadget_udc_start(struct usb_gadget *gadget,
* far as powering off UDC completely and disable its data
* line pullups.
*/
-static inline void usb_gadget_udc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static inline void usb_gadget_udc_stop(struct usb_udc *udc)
{
- gadget->ops->udc_stop(gadget, driver);
+ udc->gadget->ops->udc_stop(udc->gadget);
}
/**
@@ -328,14 +325,14 @@ EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
static void usb_gadget_remove_driver(struct usb_udc *udc)
{
dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
- udc->gadget->name);
+ udc->driver->function);
kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
usb_gadget_disconnect(udc->gadget);
udc->driver->disconnect(udc->gadget);
udc->driver->unbind(udc->gadget);
- usb_gadget_udc_stop(udc->gadget, NULL);
+ usb_gadget_udc_stop(udc);
udc->driver = NULL;
udc->dev.driver = NULL;
@@ -395,7 +392,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
ret = driver->bind(udc->gadget, driver);
if (ret)
goto err1;
- ret = usb_gadget_udc_start(udc->gadget, driver);
+ ret = usb_gadget_udc_start(udc);
if (ret) {
driver->unbind(udc->gadget);
goto err1;
@@ -414,7 +411,7 @@ err1:
return ret;
}
-int udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
+int usb_udc_attach_driver(const char *name, struct usb_gadget_driver *driver)
{
struct usb_udc *udc = NULL;
int ret = -ENODEV;
@@ -438,7 +435,7 @@ out:
mutex_unlock(&udc_lock);
return ret;
}
-EXPORT_SYMBOL_GPL(udc_attach_driver);
+EXPORT_SYMBOL_GPL(usb_udc_attach_driver);
int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
{
@@ -513,11 +510,12 @@ static ssize_t usb_udc_softconn_store(struct device *dev,
}
if (sysfs_streq(buf, "connect")) {
- usb_gadget_udc_start(udc->gadget, udc->driver);
+ usb_gadget_udc_start(udc);
usb_gadget_connect(udc->gadget);
} else if (sysfs_streq(buf, "disconnect")) {
usb_gadget_disconnect(udc->gadget);
- usb_gadget_udc_stop(udc->gadget, udc->driver);
+ udc->driver->disconnect(udc->gadget);
+ usb_gadget_udc_stop(udc);
} else {
dev_err(dev, "unsupported command '%s'\n", buf);
return -EINVAL;
diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c
index ed27e1687a4e..1eac56fc384d 100644
--- a/drivers/usb/gadget/udc/udc-xilinx.c
+++ b/drivers/usb/gadget/udc/udc-xilinx.c
@@ -1403,8 +1403,7 @@ err:
*
* Return: zero always
*/
-static int xudc_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int xudc_stop(struct usb_gadget *gadget)
{
struct xusb_udc *udc = to_udc(gadget);
unsigned long flags;
diff --git a/drivers/usb/host/ohci-omap.c b/drivers/usb/host/ohci-omap.c
index 0231606d47c2..3e5df5af3740 100644
--- a/drivers/usb/host/ohci-omap.c
+++ b/drivers/usb/host/ohci-omap.c
@@ -183,7 +183,7 @@ static void start_hnp(struct ohci_hcd *ohci)
otg_start_hnp(hcd->usb_phy->otg);
local_irq_save(flags);
- hcd->usb_phy->state = OTG_STATE_A_SUSPEND;
+ hcd->usb_phy->otg->state = OTG_STATE_A_SUSPEND;
writel (RH_PS_PSS, &ohci->regs->roothub.portstatus [port]);
l = omap_readl(OTG_CTRL);
l &= ~OTG_A_BUSREQ;
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 06cc5d6ea681..9d68372dd9aa 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -58,8 +58,7 @@ config USB_MUSB_DUAL_ROLE
endchoice
-choice
- prompt "Platform Glue Layer"
+comment "Platform Glue Layer"
config USB_MUSB_DAVINCI
tristate "DaVinci"
@@ -101,8 +100,6 @@ config USB_MUSB_JZ4740
depends on USB_MUSB_GADGET
depends on USB_OTG_BLACKLIST_HUB
-endchoice
-
config USB_MUSB_AM335X_CHILD
tristate
diff --git a/drivers/usb/musb/am35x.c b/drivers/usb/musb/am35x.c
index a2735df24cc6..220fd4d3b41c 100644
--- a/drivers/usb/musb/am35x.c
+++ b/drivers/usb/musb/am35x.c
@@ -149,25 +149,25 @@ static void otg_timer(unsigned long _musb)
*/
devctl = musb_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_BCON:
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
case OTG_STATE_A_WAIT_VFALL:
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
musb_writel(musb->ctrl_base, CORE_INTR_SRC_SET_REG,
MUSB_INTR_VBUSERROR << AM35X_INTR_USB_SHIFT);
break;
@@ -176,7 +176,7 @@ static void otg_timer(unsigned long _musb)
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
else
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
break;
default:
break;
@@ -193,9 +193,9 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout)
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || (musb->a_wait_bcon == 0 &&
- musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
+ musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
del_timer(&otg_workaround);
last_timer = jiffies;
return;
@@ -208,7 +208,7 @@ static void am35x_musb_try_idle(struct musb *musb, unsigned long timeout)
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
jiffies_to_msecs(timeout - jiffies));
mod_timer(&otg_workaround, timeout);
}
@@ -278,27 +278,27 @@ static irqreturn_t am35x_musb_interrupt(int irq, void *hci)
* devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
- musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&otg_workaround);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
/* NOTE: this must complete power-on within 100 ms. */
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);
ret = IRQ_HANDLED;
@@ -324,7 +324,7 @@ eoi:
}
/* Poll for ID change */
- if (musb->xceiv->state == OTG_STATE_B_IDLE)
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -408,7 +408,7 @@ static int am35x_musb_exit(struct musb *musb)
}
/* AM35x supports only 32bit read operation */
-void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
+static void am35x_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
{
void __iomem *fifo = hw_ep->fifo;
u32 val;
@@ -438,9 +438,11 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
}
static const struct musb_platform_ops am35x_ops = {
+ .quirks = MUSB_INDEXED_EP,
.init = am35x_musb_init,
.exit = am35x_musb_exit,
+ .read_fifo = am35x_read_fifo,
.enable = am35x_musb_enable,
.disable = am35x_musb_disable,
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index ac4422b33dcd..a441a2de8619 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -33,10 +33,45 @@ struct bfin_glue {
};
#define glue_to_musb(g) platform_get_drvdata(g->musb)
+static u32 bfin_fifo_offset(u8 epnum)
+{
+ return USB_OFFSET(USB_EP0_FIFO) + (epnum * 8);
+}
+
+static u8 bfin_readb(const void __iomem *addr, unsigned offset)
+{
+ return (u8)(bfin_read16(addr + offset));
+}
+
+static u16 bfin_readw(const void __iomem *addr, unsigned offset)
+{
+ return bfin_read16(addr + offset);
+}
+
+static u32 bfin_readl(const void __iomem *addr, unsigned offset)
+{
+ return (u32)(bfin_read16(addr + offset));
+}
+
+static void bfin_writeb(void __iomem *addr, unsigned offset, u8 data)
+{
+ bfin_write16(addr + offset, (u16)data);
+}
+
+static void bfin_writew(void __iomem *addr, unsigned offset, u16 data)
+{
+ bfin_write16(addr + offset, data);
+}
+
+static void binf_writel(void __iomem *addr, unsigned offset, u32 data)
+{
+ bfin_write16(addr + offset, (u16)data);
+}
+
/*
* Load an endpoint's FIFO
*/
-void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
+static void bfin_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
{
struct musb *musb = hw_ep->musb;
void __iomem *fifo = hw_ep->fifo;
@@ -100,7 +135,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
/*
* Unload an endpoint's FIFO
*/
-void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
+static void bfin_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
{
struct musb *musb = hw_ep->musb;
void __iomem *fifo = hw_ep->fifo;
@@ -185,8 +220,8 @@ static irqreturn_t blackfin_interrupt(int irq, void *__hci)
}
/* Start sampling ID pin, when plug is removed from MUSB */
- if ((musb->xceiv->state == OTG_STATE_B_IDLE
- || musb->xceiv->state == OTG_STATE_A_WAIT_BCON) ||
+ if ((musb->xceiv->otg->state == OTG_STATE_B_IDLE
+ || musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON) ||
(musb->int_usb & MUSB_INTR_DISCONNECT && is_host_active(musb))) {
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
musb->a_wait_bcon = TIMER_DELAY;
@@ -205,7 +240,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
static u8 toggle;
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_IDLE:
case OTG_STATE_A_WAIT_BCON:
/* Start a new session */
@@ -219,7 +254,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
if (!(val & MUSB_DEVCTL_BDEVICE)) {
gpio_set_value(musb->config->gpio_vrsel, 1);
- musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
} else {
gpio_set_value(musb->config->gpio_vrsel, 0);
/* Ignore VBUSERROR and SUSPEND IRQ */
@@ -229,7 +264,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
val = MUSB_INTR_SUSPEND | MUSB_INTR_VBUSERROR;
musb_writeb(musb->mregs, MUSB_INTRUSB, val);
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
}
mod_timer(&musb_conn_timer, jiffies + TIMER_DELAY);
break;
@@ -245,7 +280,7 @@ static void musb_conn_timer_handler(unsigned long _musb)
if (!(val & MUSB_DEVCTL_BDEVICE)) {
gpio_set_value(musb->config->gpio_vrsel, 1);
- musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
} else {
gpio_set_value(musb->config->gpio_vrsel, 0);
@@ -280,13 +315,13 @@ static void musb_conn_timer_handler(unsigned long _musb)
break;
default:
dev_dbg(musb->controller, "%s state not handled\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
break;
}
spin_unlock_irqrestore(&musb->lock, flags);
dev_dbg(musb->controller, "state is %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
static void bfin_musb_enable(struct musb *musb)
@@ -307,7 +342,7 @@ static void bfin_musb_set_vbus(struct musb *musb, int is_on)
dev_dbg(musb->controller, "VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ "\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
musb_readb(musb->mregs, MUSB_DEVCTL));
}
@@ -433,6 +468,15 @@ static const struct musb_platform_ops bfin_ops = {
.init = bfin_musb_init,
.exit = bfin_musb_exit,
+ .readb = bfin_readb,
+ .writeb = bfin_writeb,
+ .readw = bfin_readw,
+ .writew = bfin_writew,
+ .readl = bfin_readl,
+ .writel = bfin_writel,
+ .fifo_mode = 2,
+ .read_fifo = bfin_read_fifo,
+ .write_fifo = bfin_write_fifo,
.enable = bfin_musb_enable,
.disable = bfin_musb_disable,
@@ -456,16 +500,12 @@ static int bfin_probe(struct platform_device *pdev)
int ret = -ENOMEM;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "failed to allocate glue context\n");
+ if (!glue)
goto err0;
- }
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
- if (!musb) {
- dev_err(&pdev->dev, "failed to allocate musb device\n");
+ if (!musb)
goto err0;
- }
musb->dev.parent = &pdev->dev;
musb->dev.dma_mask = &bfin_dmamask;
diff --git a/drivers/usb/musb/da8xx.c b/drivers/usb/musb/da8xx.c
index 058775e647ad..9a9c82a4d35d 100644
--- a/drivers/usb/musb/da8xx.c
+++ b/drivers/usb/musb/da8xx.c
@@ -198,20 +198,20 @@ static void otg_timer(unsigned long _musb)
*/
devctl = musb_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_BCON:
devctl &= ~MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
@@ -226,7 +226,7 @@ static void otg_timer(unsigned long _musb)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
break;
}
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
musb_writel(musb->ctrl_base, DA8XX_USB_INTR_SRC_SET_REG,
MUSB_INTR_VBUSERROR << DA8XX_INTR_USB_SHIFT);
break;
@@ -248,7 +248,7 @@ static void otg_timer(unsigned long _musb)
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
else
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
break;
default:
break;
@@ -265,9 +265,9 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || (musb->a_wait_bcon == 0 &&
- musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
+ musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
del_timer(&otg_workaround);
last_timer = jiffies;
return;
@@ -280,7 +280,7 @@ static void da8xx_musb_try_idle(struct musb *musb, unsigned long timeout)
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
jiffies_to_msecs(timeout - jiffies));
mod_timer(&otg_workaround, timeout);
}
@@ -341,26 +341,26 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
* devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
- musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&otg_workaround);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);
ret = IRQ_HANDLED;
@@ -375,7 +375,7 @@ static irqreturn_t da8xx_musb_interrupt(int irq, void *hci)
musb_writel(reg_base, DA8XX_USB_END_OF_INTR_REG, 0);
/* Poll for ID change */
- if (musb->xceiv->state == OTG_STATE_B_IDLE)
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -458,9 +458,11 @@ static int da8xx_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops da8xx_ops = {
+ .quirks = MUSB_INDEXED_EP,
.init = da8xx_musb_init,
.exit = da8xx_musb_exit,
+ .fifo_mode = 2,
.enable = da8xx_musb_enable,
.disable = da8xx_musb_disable,
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index 110b78415bf0..3c1d9b211b51 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -214,10 +214,10 @@ static void otg_timer(unsigned long _musb)
*/
devctl = musb_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "poll devctl %02x (%s)\n", devctl,
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_VFALL:
/* Wait till VBUS falls below SessionEnd (~0.2V); the 1.3 RTL
* seems to mis-handle session "start" otherwise (or in our
@@ -228,7 +228,7 @@ static void otg_timer(unsigned long _musb)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
break;
}
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
musb_writel(musb->ctrl_base, DAVINCI_USB_INT_SET_REG,
MUSB_INTR_VBUSERROR << DAVINCI_USB_USBINT_SHIFT);
break;
@@ -251,7 +251,7 @@ static void otg_timer(unsigned long _musb)
if (devctl & MUSB_DEVCTL_BDEVICE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
else
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
break;
default:
break;
@@ -325,20 +325,20 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
* to stop registering in devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
- musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
portstate(musb->port1_status |= USB_PORT_STAT_POWER);
del_timer(&otg_workaround);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
portstate(musb->port1_status &= ~USB_PORT_STAT_POWER);
}
@@ -348,7 +348,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
davinci_musb_source_power(musb, drvvbus, 0);
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);
retval = IRQ_HANDLED;
@@ -361,7 +361,7 @@ static irqreturn_t davinci_musb_interrupt(int irq, void *__hci)
musb_writel(tibase, DAVINCI_USB_EOI_REG, 0);
/* poll for ID change */
- if (musb->xceiv->state == OTG_STATE_B_IDLE)
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE)
mod_timer(&otg_workaround, jiffies + POLL_SECONDS * HZ);
spin_unlock_irqrestore(&musb->lock, flags);
@@ -520,10 +520,8 @@ static int davinci_probe(struct platform_device *pdev)
int ret = -ENOMEM;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "failed to allocate glue context\n");
+ if (!glue)
goto err0;
- }
clk = devm_clk_get(&pdev->dev, "usb");
if (IS_ERR(clk)) {
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index d1187290d4e3..bb7b26325a74 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -106,6 +106,8 @@ static int jz4740_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops jz4740_musb_ops = {
+ .quirks = MUSB_INDEXED_EP,
+ .fifo_mode = 2,
.init = jz4740_musb_init,
.exit = jz4740_musb_exit,
};
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 55ebd4b1daac..55fe0ff6fd87 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -224,12 +224,67 @@ static struct usb_phy_io_ops musb_ulpi_access = {
/*-------------------------------------------------------------------------*/
-#if !defined(CONFIG_USB_MUSB_TUSB6010) && !defined(CONFIG_USB_MUSB_BLACKFIN)
+static u32 musb_default_fifo_offset(u8 epnum)
+{
+ return 0x20 + (epnum * 4);
+}
+
+/* "flat" mapping: each endpoint has its own i/o address */
+static void musb_flat_ep_select(void __iomem *mbase, u8 epnum)
+{
+}
+
+static u32 musb_flat_ep_offset(u8 epnum, u16 offset)
+{
+ return 0x100 + (0x10 * epnum) + offset;
+}
+
+/* "indexed" mapping: INDEX register controls register bank select */
+static void musb_indexed_ep_select(void __iomem *mbase, u8 epnum)
+{
+ musb_writeb(mbase, MUSB_INDEX, epnum);
+}
+
+static u32 musb_indexed_ep_offset(u8 epnum, u16 offset)
+{
+ return 0x10 + offset;
+}
+
+static u8 musb_default_readb(const void __iomem *addr, unsigned offset)
+{
+ return __raw_readb(addr + offset);
+}
+
+static void musb_default_writeb(void __iomem *addr, unsigned offset, u8 data)
+{
+ __raw_writeb(data, addr + offset);
+}
+
+static u16 musb_default_readw(const void __iomem *addr, unsigned offset)
+{
+ return __raw_readw(addr + offset);
+}
+
+static void musb_default_writew(void __iomem *addr, unsigned offset, u16 data)
+{
+ __raw_writew(data, addr + offset);
+}
+
+static u32 musb_default_readl(const void __iomem *addr, unsigned offset)
+{
+ return __raw_readl(addr + offset);
+}
+
+static void musb_default_writel(void __iomem *addr, unsigned offset, u32 data)
+{
+ __raw_writel(data, addr + offset);
+}
/*
* Load an endpoint's FIFO
*/
-void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
+static void musb_default_write_fifo(struct musb_hw_ep *hw_ep, u16 len,
+ const u8 *src)
{
struct musb *musb = hw_ep->musb;
void __iomem *fifo = hw_ep->fifo;
@@ -270,11 +325,10 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
}
}
-#if !defined(CONFIG_USB_MUSB_AM35X)
/*
* Unload an endpoint's FIFO
*/
-void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
+static void musb_default_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
{
struct musb *musb = hw_ep->musb;
void __iomem *fifo = hw_ep->fifo;
@@ -312,10 +366,40 @@ void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
ioread8_rep(fifo, dst, len);
}
}
-#endif
-#endif /* normal PIO */
+/*
+ * Old style IO functions
+ */
+u8 (*musb_readb)(const void __iomem *addr, unsigned offset);
+EXPORT_SYMBOL_GPL(musb_readb);
+
+void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data);
+EXPORT_SYMBOL_GPL(musb_writeb);
+
+u16 (*musb_readw)(const void __iomem *addr, unsigned offset);
+EXPORT_SYMBOL_GPL(musb_readw);
+
+void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data);
+EXPORT_SYMBOL_GPL(musb_writew);
+u32 (*musb_readl)(const void __iomem *addr, unsigned offset);
+EXPORT_SYMBOL_GPL(musb_readl);
+
+void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data);
+EXPORT_SYMBOL_GPL(musb_writel);
+
+/*
+ * New style IO functions
+ */
+void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
+{
+ return hw_ep->musb->io.read_fifo(hw_ep, len, dst);
+}
+
+void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *src)
+{
+ return hw_ep->musb->io.write_fifo(hw_ep, len, src);
+}
/*-------------------------------------------------------------------------*/
@@ -360,23 +444,23 @@ static void musb_otg_timer_func(unsigned long data)
unsigned long flags;
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_B_WAIT_ACON:
dev_dbg(musb->controller, "HNP: b_wait_acon timeout; back to b_peripheral\n");
musb_g_disconnect(musb);
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
musb->is_active = 0;
break;
case OTG_STATE_A_SUSPEND:
case OTG_STATE_A_WAIT_BCON:
dev_dbg(musb->controller, "HNP: %s timeout\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
musb_platform_set_vbus(musb, 0);
- musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
break;
default:
dev_dbg(musb->controller, "HNP: Unhandled mode %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
spin_unlock_irqrestore(&musb->lock, flags);
}
@@ -391,19 +475,19 @@ void musb_hnp_stop(struct musb *musb)
u8 reg;
dev_dbg(musb->controller, "HNP: stop from %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_PERIPHERAL:
musb_g_disconnect(musb);
dev_dbg(musb->controller, "HNP: back to %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
break;
case OTG_STATE_B_HOST:
dev_dbg(musb->controller, "HNP: Disabling HR\n");
if (hcd)
hcd->self.is_b_host = 0;
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
MUSB_DEV_MODE(musb);
reg = musb_readb(mbase, MUSB_POWER);
reg |= MUSB_POWER_SUSPENDM;
@@ -412,7 +496,7 @@ void musb_hnp_stop(struct musb *musb)
break;
default:
dev_dbg(musb->controller, "HNP: Stopping in unknown state %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
/*
@@ -423,6 +507,7 @@ void musb_hnp_stop(struct musb *musb)
musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
}
+static void musb_generic_disable(struct musb *musb);
/*
* Interrupt Service Routine to record USB "global" interrupts.
* Since these do not happen often and signify things of
@@ -449,13 +534,13 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
*/
if (int_usb & MUSB_INTR_RESUME) {
handled = IRQ_HANDLED;
- dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->state));
+ dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->otg->state));
if (devctl & MUSB_DEVCTL_HM) {
void __iomem *mbase = musb->mregs;
u8 power;
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
/* remote wakeup? later, GetPortStatus
* will stop RESUME signaling
@@ -478,29 +563,26 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
| MUSB_PORT_STAT_RESUME;
musb->rh_timer = jiffies
+ msecs_to_jiffies(20);
- schedule_delayed_work(
- &musb->finish_resume_work,
- msecs_to_jiffies(20));
+ musb->need_finish_resume = 1;
- musb->xceiv->state = OTG_STATE_A_HOST;
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
musb->is_active = 1;
- musb_host_resume_root_hub(musb);
break;
case OTG_STATE_B_WAIT_ACON:
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
musb->is_active = 1;
MUSB_DEV_MODE(musb);
break;
default:
WARNING("bogus %s RESUME (%s)\n",
"host",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
} else {
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
/* possibly DISCONNECT is upcoming */
- musb->xceiv->state = OTG_STATE_A_HOST;
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
musb_host_resume_root_hub(musb);
break;
case OTG_STATE_B_WAIT_ACON:
@@ -523,7 +605,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
default:
WARNING("bogus %s RESUME (%s)\n",
"peripheral",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
}
}
@@ -539,7 +621,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
}
dev_dbg(musb->controller, "SESSION_REQUEST (%s)\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
/* IRQ arrives from ID pin sense or (later, if VBUS power
* is removed) SRP. responses are time critical:
@@ -550,7 +632,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
*/
musb_writeb(mbase, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
musb->ep0_stage = MUSB_EP0_START;
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
musb_platform_set_vbus(musb, 1);
@@ -576,7 +658,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
* REVISIT: do delays from lots of DEBUG_KERNEL checks
* make trouble here, keeping VBUS < 4.4V ?
*/
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
/* recovery is dicey once we've gotten past the
* initial stages of enumeration, but if VBUS
@@ -605,7 +687,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
dev_printk(ignore ? KERN_DEBUG : KERN_ERR, musb->controller,
"VBUS_ERROR in %s (%02x, %s), retry #%d, port1 %08x\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
devctl,
({ char *s;
switch (devctl & MUSB_DEVCTL_VBUS) {
@@ -630,10 +712,10 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
if (int_usb & MUSB_INTR_SUSPEND) {
dev_dbg(musb->controller, "SUSPEND (%s) devctl %02x\n",
- usb_otg_state_string(musb->xceiv->state), devctl);
+ usb_otg_state_string(musb->xceiv->otg->state), devctl);
handled = IRQ_HANDLED;
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_PERIPHERAL:
/* We also come here if the cable is removed, since
* this silicon doesn't report ID-no-longer-grounded.
@@ -657,7 +739,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb_g_suspend(musb);
musb->is_active = musb->g.b_hnp_enable;
if (musb->is_active) {
- musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
+ musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(
@@ -670,7 +752,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
+ msecs_to_jiffies(musb->a_wait_bcon));
break;
case OTG_STATE_A_HOST:
- musb->xceiv->state = OTG_STATE_A_SUSPEND;
+ musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
musb->is_active = musb->hcd->self.b_hnp_enable;
break;
case OTG_STATE_B_HOST:
@@ -713,7 +795,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->port1_status |= USB_PORT_STAT_LOW_SPEED;
/* indicate new connection to OTG machine */
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_B_PERIPHERAL:
if (int_usb & MUSB_INTR_SUSPEND) {
dev_dbg(musb->controller, "HNP: SUSPEND+CONNECT, now b_host\n");
@@ -725,7 +807,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
case OTG_STATE_B_WAIT_ACON:
dev_dbg(musb->controller, "HNP: CONNECT, now b_host\n");
b_host:
- musb->xceiv->state = OTG_STATE_B_HOST;
+ musb->xceiv->otg->state = OTG_STATE_B_HOST;
if (musb->hcd)
musb->hcd->self.is_b_host = 1;
del_timer(&musb->otg_timer);
@@ -733,7 +815,7 @@ b_host:
default:
if ((devctl & MUSB_DEVCTL_VBUS)
== (3 << MUSB_DEVCTL_VBUS_SHIFT)) {
- musb->xceiv->state = OTG_STATE_A_HOST;
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
if (hcd)
hcd->self.is_b_host = 0;
}
@@ -743,16 +825,16 @@ b_host:
musb_host_poke_root_hub(musb);
dev_dbg(musb->controller, "CONNECT (%s) devctl %02x\n",
- usb_otg_state_string(musb->xceiv->state), devctl);
+ usb_otg_state_string(musb->xceiv->otg->state), devctl);
}
if (int_usb & MUSB_INTR_DISCONNECT) {
dev_dbg(musb->controller, "DISCONNECT (%s) as %s, devctl %02x\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
MUSB_MODE(musb), devctl);
handled = IRQ_HANDLED;
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
case OTG_STATE_A_SUSPEND:
musb_host_resume_root_hub(musb);
@@ -770,7 +852,7 @@ b_host:
musb_root_disconnect(musb);
if (musb->hcd)
musb->hcd->self.is_b_host = 0;
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
MUSB_DEV_MODE(musb);
musb_g_disconnect(musb);
break;
@@ -786,7 +868,7 @@ b_host:
break;
default:
WARNING("unhandled DISCONNECT transition (%s)\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
break;
}
}
@@ -812,15 +894,15 @@ b_host:
}
} else {
dev_dbg(musb->controller, "BUS RESET as %s\n",
- usb_otg_state_string(musb->xceiv->state));
- switch (musb->xceiv->state) {
+ usb_otg_state_string(musb->xceiv->otg->state));
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
musb_g_reset(musb);
/* FALLTHROUGH */
case OTG_STATE_A_WAIT_BCON: /* OPT TD.4.7-900ms */
/* never use invalid T(a_wait_bcon) */
dev_dbg(musb->controller, "HNP: in %s, %d msec timeout\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
TA_WAIT_BCON(musb));
mod_timer(&musb->otg_timer, jiffies
+ msecs_to_jiffies(TA_WAIT_BCON(musb)));
@@ -831,27 +913,29 @@ b_host:
break;
case OTG_STATE_B_WAIT_ACON:
dev_dbg(musb->controller, "HNP: RESET (%s), to b_peripheral\n",
- usb_otg_state_string(musb->xceiv->state));
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ usb_otg_state_string(musb->xceiv->otg->state));
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
musb_g_reset(musb);
break;
case OTG_STATE_B_IDLE:
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
/* FALLTHROUGH */
case OTG_STATE_B_PERIPHERAL:
musb_g_reset(musb);
break;
default:
dev_dbg(musb->controller, "Unhandled BUS RESET as %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
}
}
/* handle babble condition */
- if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb))
+ if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) {
+ musb_generic_disable(musb);
schedule_delayed_work(&musb->recover_work,
msecs_to_jiffies(100));
+ }
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
@@ -1032,21 +1116,7 @@ static void musb_shutdown(struct platform_device *pdev)
* We don't currently use dynamic fifo setup capability to do anything
* more than selecting one of a bunch of predefined configurations.
*/
-#if defined(CONFIG_USB_MUSB_TUSB6010) \
- || defined(CONFIG_USB_MUSB_TUSB6010_MODULE) \
- || defined(CONFIG_USB_MUSB_OMAP2PLUS) \
- || defined(CONFIG_USB_MUSB_OMAP2PLUS_MODULE) \
- || defined(CONFIG_USB_MUSB_AM35X) \
- || defined(CONFIG_USB_MUSB_AM35X_MODULE) \
- || defined(CONFIG_USB_MUSB_DSPS) \
- || defined(CONFIG_USB_MUSB_DSPS_MODULE)
-static ushort fifo_mode = 4;
-#elif defined(CONFIG_USB_MUSB_UX500) \
- || defined(CONFIG_USB_MUSB_UX500_MODULE)
-static ushort fifo_mode = 5;
-#else
-static ushort fifo_mode = 2;
-#endif
+static ushort fifo_mode;
/* "modprobe ... fifo_mode=1" etc */
module_param(fifo_mode, ushort, 0);
@@ -1456,20 +1526,25 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
for (i = 0; i < musb->nr_endpoints; i++) {
struct musb_hw_ep *hw_ep = musb->endpoints + i;
- hw_ep->fifo = MUSB_FIFO_OFFSET(i) + mbase;
-#if defined(CONFIG_USB_MUSB_TUSB6010) || defined (CONFIG_USB_MUSB_TUSB6010_MODULE)
- hw_ep->fifo_async = musb->async + 0x400 + MUSB_FIFO_OFFSET(i);
- hw_ep->fifo_sync = musb->sync + 0x400 + MUSB_FIFO_OFFSET(i);
- hw_ep->fifo_sync_va =
- musb->sync_va + 0x400 + MUSB_FIFO_OFFSET(i);
-
- if (i == 0)
- hw_ep->conf = mbase - 0x400 + TUSB_EP0_CONF;
- else
- hw_ep->conf = mbase + 0x400 + (((i - 1) & 0xf) << 2);
+ hw_ep->fifo = musb->io.fifo_offset(i) + mbase;
+#if IS_ENABLED(CONFIG_USB_MUSB_TUSB6010)
+ if (musb->io.quirks & MUSB_IN_TUSB) {
+ hw_ep->fifo_async = musb->async + 0x400 +
+ musb->io.fifo_offset(i);
+ hw_ep->fifo_sync = musb->sync + 0x400 +
+ musb->io.fifo_offset(i);
+ hw_ep->fifo_sync_va =
+ musb->sync_va + 0x400 + musb->io.fifo_offset(i);
+
+ if (i == 0)
+ hw_ep->conf = mbase - 0x400 + TUSB_EP0_CONF;
+ else
+ hw_ep->conf = mbase + 0x400 +
+ (((i - 1) & 0xf) << 2);
+ }
#endif
- hw_ep->regs = MUSB_EP_OFFSET(i, 0) + mbase;
+ hw_ep->regs = musb->io.ep_offset(i, 0) + mbase;
hw_ep->target_regs = musb_read_target_reg_base(i, mbase);
hw_ep->rx_reinit = 1;
hw_ep->tx_reinit = 1;
@@ -1630,7 +1705,7 @@ musb_mode_show(struct device *dev, struct device_attribute *attr, char *buf)
int ret = -EINVAL;
spin_lock_irqsave(&musb->lock, flags);
- ret = sprintf(buf, "%s\n", usb_otg_state_string(musb->xceiv->state));
+ ret = sprintf(buf, "%s\n", usb_otg_state_string(musb->xceiv->otg->state));
spin_unlock_irqrestore(&musb->lock, flags);
return ret;
@@ -1675,7 +1750,7 @@ musb_vbus_store(struct device *dev, struct device_attribute *attr,
spin_lock_irqsave(&musb->lock, flags);
/* force T(a_wait_bcon) to be zero/unlimited *OR* valid */
musb->a_wait_bcon = val ? max_t(int, val, OTG_TIME_A_WAIT_BCON) : 0 ;
- if (musb->xceiv->state == OTG_STATE_A_WAIT_BCON)
+ if (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)
musb->is_active = 0;
musb_platform_try_idle(musb, jiffies + msecs_to_jiffies(val));
spin_unlock_irqrestore(&musb->lock, flags);
@@ -1743,8 +1818,8 @@ static void musb_irq_work(struct work_struct *data)
{
struct musb *musb = container_of(data, struct musb, irq_work);
- if (musb->xceiv->state != musb->xceiv_old_state) {
- musb->xceiv_old_state = musb->xceiv->state;
+ if (musb->xceiv->otg->state != musb->xceiv_old_state) {
+ musb->xceiv_old_state = musb->xceiv->otg->state;
sysfs_notify(&musb->controller->kobj, NULL, "mode");
}
}
@@ -1903,6 +1978,18 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
musb->ops = plat->platform_ops;
musb->port_mode = plat->mode;
+ /*
+ * Initialize the default IO functions. At least omap2430 needs
+ * these early. We initialize the platform specific IO functions
+ * later on.
+ */
+ musb_readb = musb_default_readb;
+ musb_writeb = musb_default_writeb;
+ musb_readw = musb_default_readw;
+ musb_writew = musb_default_writew;
+ musb_readl = musb_default_readl;
+ musb_writel = musb_default_writel;
+
/* The musb_platform_init() call:
* - adjusts musb->mregs
* - sets the musb->isr
@@ -1924,6 +2011,57 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
goto fail2;
}
+ if (musb->ops->quirks)
+ musb->io.quirks = musb->ops->quirks;
+
+ /* At least tusb6010 has it's own offsets.. */
+ if (musb->ops->ep_offset)
+ musb->io.ep_offset = musb->ops->ep_offset;
+ if (musb->ops->ep_select)
+ musb->io.ep_select = musb->ops->ep_select;
+
+ /* ..and some devices use indexed offset or flat offset */
+ if (musb->io.quirks & MUSB_INDEXED_EP) {
+ musb->io.ep_offset = musb_indexed_ep_offset;
+ musb->io.ep_select = musb_indexed_ep_select;
+ } else {
+ musb->io.ep_offset = musb_flat_ep_offset;
+ musb->io.ep_select = musb_flat_ep_select;
+ }
+
+ if (musb->ops->fifo_mode)
+ fifo_mode = musb->ops->fifo_mode;
+ else
+ fifo_mode = 4;
+
+ if (musb->ops->fifo_offset)
+ musb->io.fifo_offset = musb->ops->fifo_offset;
+ else
+ musb->io.fifo_offset = musb_default_fifo_offset;
+
+ if (musb->ops->readb)
+ musb_readb = musb->ops->readb;
+ if (musb->ops->writeb)
+ musb_writeb = musb->ops->writeb;
+ if (musb->ops->readw)
+ musb_readw = musb->ops->readw;
+ if (musb->ops->writew)
+ musb_writew = musb->ops->writew;
+ if (musb->ops->readl)
+ musb_readl = musb->ops->readl;
+ if (musb->ops->writel)
+ musb_writel = musb->ops->writel;
+
+ if (musb->ops->read_fifo)
+ musb->io.read_fifo = musb->ops->read_fifo;
+ else
+ musb->io.read_fifo = musb_default_read_fifo;
+
+ if (musb->ops->write_fifo)
+ musb->io.write_fifo = musb->ops->write_fifo;
+ else
+ musb->io.write_fifo = musb_default_write_fifo;
+
if (!musb->xceiv->io_ops) {
musb->xceiv->io_dev = musb->controller;
musb->xceiv->io_priv = musb->mregs;
@@ -1983,10 +2121,10 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
if (musb->xceiv->otg->default_a) {
MUSB_HST_MODE(musb);
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
} else {
MUSB_DEV_MODE(musb);
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
}
switch (musb->port_mode) {
@@ -2297,9 +2435,11 @@ static int musb_suspend(struct device *dev)
return 0;
}
-static int musb_resume_noirq(struct device *dev)
+static int musb_resume(struct device *dev)
{
struct musb *musb = dev_to_musb(dev);
+ u8 devctl;
+ u8 mask;
/*
* For static cmos like DaVinci, register values were preserved
@@ -2313,6 +2453,23 @@ static int musb_resume_noirq(struct device *dev)
musb_restore_context(musb);
+ devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+ mask = MUSB_DEVCTL_BDEVICE | MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV;
+ if ((devctl & mask) != (musb->context.devctl & mask))
+ musb->port1_status = 0;
+ if (musb->need_finish_resume) {
+ musb->need_finish_resume = 0;
+ schedule_delayed_work(&musb->finish_resume_work,
+ msecs_to_jiffies(20));
+ }
+
+ /*
+ * The USB HUB code expects the device to be in RPM_ACTIVE once it came
+ * out of suspend
+ */
+ pm_runtime_disable(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
return 0;
}
@@ -2348,7 +2505,7 @@ static int musb_runtime_resume(struct device *dev)
static const struct dev_pm_ops musb_dev_pm_ops = {
.suspend = musb_suspend,
- .resume_noirq = musb_resume_noirq,
+ .resume = musb_resume,
.runtime_suspend = musb_runtime_suspend,
.runtime_resume = musb_runtime_resume,
};
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 414e57a984bb..5e65958f7915 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -124,41 +124,6 @@ enum musb_g_ep0_state {
#define OTG_TIME_A_AIDL_BDIS 200 /* min 200 msec */
#define OTG_TIME_B_ASE0_BRST 100 /* min 3.125 ms */
-
-/*************************** REGISTER ACCESS ********************************/
-
-/* Endpoint registers (other than dynfifo setup) can be accessed either
- * directly with the "flat" model, or after setting up an index register.
- */
-
-#if defined(CONFIG_ARCH_DAVINCI) || defined(CONFIG_SOC_OMAP2430) \
- || defined(CONFIG_SOC_OMAP3430) || defined(CONFIG_BLACKFIN) \
- || defined(CONFIG_ARCH_OMAP4)
-/* REVISIT indexed access seemed to
- * misbehave (on DaVinci) for at least peripheral IN ...
- */
-#define MUSB_FLAT_REG
-#endif
-
-/* TUSB mapping: "flat" plus ep0 special cases */
-#if defined(CONFIG_USB_MUSB_TUSB6010) || \
- defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
-#define musb_ep_select(_mbase, _epnum) \
- musb_writeb((_mbase), MUSB_INDEX, (_epnum))
-#define MUSB_EP_OFFSET MUSB_TUSB_OFFSET
-
-/* "flat" mapping: each endpoint has its own i/o address */
-#elif defined(MUSB_FLAT_REG)
-#define musb_ep_select(_mbase, _epnum) (((void)(_mbase)), ((void)(_epnum)))
-#define MUSB_EP_OFFSET MUSB_FLAT_OFFSET
-
-/* "indexed" mapping: INDEX register controls register bank select */
-#else
-#define musb_ep_select(_mbase, _epnum) \
- musb_writeb((_mbase), MUSB_INDEX, (_epnum))
-#define MUSB_EP_OFFSET MUSB_INDEXED_OFFSET
-#endif
-
/****************************** FUNCTIONS ********************************/
#define MUSB_HST_MODE(_musb)\
@@ -173,8 +138,25 @@ enum musb_g_ep0_state {
/******************************** TYPES *************************************/
+struct musb_io;
+
/**
* struct musb_platform_ops - Operations passed to musb_core by HW glue layer
+ * @quirks: flags for platform specific quirks
+ * @enable: enable device
+ * @disable: disable device
+ * @ep_offset: returns the end point offset
+ * @ep_select: selects the specified end point
+ * @fifo_mode: sets the fifo mode
+ * @fifo_offset: returns the fifo offset
+ * @readb: read 8 bits
+ * @writeb: write 8 bits
+ * @readw: read 16 bits
+ * @writew: write 16 bits
+ * @readl: read 32 bits
+ * @writel: write 32 bits
+ * @read_fifo: reads the fifo
+ * @write_fifo: writes to fifo
* @init: turns on clocks, sets up platform-specific registers, etc
* @exit: undoes @init
* @set_mode: forcefully changes operating mode
@@ -184,12 +166,34 @@ enum musb_g_ep0_state {
* @adjust_channel_params: pre check for standard dma channel_program func
*/
struct musb_platform_ops {
+
+#define MUSB_DMA_UX500 BIT(6)
+#define MUSB_DMA_CPPI41 BIT(5)
+#define MUSB_DMA_CPPI BIT(4)
+#define MUSB_DMA_TUSB_OMAP BIT(3)
+#define MUSB_DMA_INVENTRA BIT(2)
+#define MUSB_IN_TUSB BIT(1)
+#define MUSB_INDEXED_EP BIT(0)
+ u32 quirks;
+
int (*init)(struct musb *musb);
int (*exit)(struct musb *musb);
void (*enable)(struct musb *musb);
void (*disable)(struct musb *musb);
+ u32 (*ep_offset)(u8 epnum, u16 offset);
+ void (*ep_select)(void __iomem *mbase, u8 epnum);
+ u16 fifo_mode;
+ u32 (*fifo_offset)(u8 epnum);
+ u8 (*readb)(const void __iomem *addr, unsigned offset);
+ void (*writeb)(void __iomem *addr, unsigned offset, u8 data);
+ u16 (*readw)(const void __iomem *addr, unsigned offset);
+ void (*writew)(void __iomem *addr, unsigned offset, u16 data);
+ u32 (*readl)(const void __iomem *addr, unsigned offset);
+ void (*writel)(void __iomem *addr, unsigned offset, u32 data);
+ void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
+ void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout);
int (*reset)(struct musb *musb);
@@ -212,8 +216,7 @@ struct musb_hw_ep {
void __iomem *fifo;
void __iomem *regs;
-#if defined(CONFIG_USB_MUSB_TUSB6010) || \
- defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
+#if IS_ENABLED(CONFIG_USB_MUSB_TUSB6010)
void __iomem *conf;
#endif
@@ -230,8 +233,7 @@ struct musb_hw_ep {
struct dma_channel *tx_channel;
struct dma_channel *rx_channel;
-#if defined(CONFIG_USB_MUSB_TUSB6010) || \
- defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
+#if IS_ENABLED(CONFIG_USB_MUSB_TUSB6010)
/* TUSB has "asynchronous" and "synchronous" dma modes */
dma_addr_t fifo_async;
dma_addr_t fifo_sync;
@@ -292,6 +294,7 @@ struct musb {
/* device lock */
spinlock_t lock;
+ struct musb_io io;
const struct musb_platform_ops *ops;
struct musb_context_registers context;
@@ -334,8 +337,7 @@ struct musb {
void __iomem *ctrl_base;
void __iomem *mregs;
-#if defined(CONFIG_USB_MUSB_TUSB6010) || \
- defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
+#if IS_ENABLED(CONFIG_USB_MUSB_TUSB6010)
dma_addr_t async;
dma_addr_t sync;
void __iomem *sync_va;
@@ -390,6 +392,7 @@ struct musb {
/* is_suspended means USB B_PERIPHERAL suspend */
unsigned is_suspended:1;
+ unsigned need_finish_resume :1;
/* may_wakeup means remote wakeup is enabled */
unsigned may_wakeup:1;
@@ -474,7 +477,7 @@ static inline int musb_read_fifosize(struct musb *musb,
u8 reg = 0;
/* read from core using indexed model */
- reg = musb_readb(mbase, MUSB_EP_OFFSET(epnum, MUSB_FIFOSIZE));
+ reg = musb_readb(mbase, musb->io.ep_offset(epnum, MUSB_FIFOSIZE));
/* 0's returned when no more endpoints */
if (!reg)
return -ENODEV;
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 5a9b977fbc19..f64fd964dc6d 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -253,6 +253,7 @@ static void cppi41_dma_callback(void *private_data)
cppi41_trans_done(cppi41_channel);
} else {
struct cppi41_dma_controller *controller;
+ int is_hs = 0;
/*
* On AM335x it has been observed that the TX interrupt fires
* too early that means the TXFIFO is not yet empty but the DMA
@@ -265,7 +266,14 @@ static void cppi41_dma_callback(void *private_data)
*/
controller = cppi41_channel->controller;
- if (musb->g.speed == USB_SPEED_HIGH) {
+ if (is_host_active(musb)) {
+ if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
+ is_hs = 1;
+ } else {
+ if (musb->g.speed == USB_SPEED_HIGH)
+ is_hs = 1;
+ }
+ if (is_hs) {
unsigned wait = 25;
do {
diff --git a/drivers/usb/musb/musb_debugfs.c b/drivers/usb/musb/musb_debugfs.c
index 4c216790e86b..ad3701a97389 100644
--- a/drivers/usb/musb/musb_debugfs.c
+++ b/drivers/usb/musb/musb_debugfs.c
@@ -49,33 +49,36 @@ struct musb_register_map {
};
static const struct musb_register_map musb_regmap[] = {
- { "FAddr", 0x00, 8 },
- { "Power", 0x01, 8 },
- { "Frame", 0x0c, 16 },
- { "Index", 0x0e, 8 },
- { "Testmode", 0x0f, 8 },
- { "TxMaxPp", 0x10, 16 },
- { "TxCSRp", 0x12, 16 },
- { "RxMaxPp", 0x14, 16 },
- { "RxCSR", 0x16, 16 },
- { "RxCount", 0x18, 16 },
- { "ConfigData", 0x1f, 8 },
- { "DevCtl", 0x60, 8 },
- { "MISC", 0x61, 8 },
- { "TxFIFOsz", 0x62, 8 },
- { "RxFIFOsz", 0x63, 8 },
- { "TxFIFOadd", 0x64, 16 },
- { "RxFIFOadd", 0x66, 16 },
- { "VControl", 0x68, 32 },
- { "HWVers", 0x6C, 16 },
- { "EPInfo", 0x78, 8 },
- { "RAMInfo", 0x79, 8 },
- { "LinkInfo", 0x7A, 8 },
- { "VPLen", 0x7B, 8 },
- { "HS_EOF1", 0x7C, 8 },
- { "FS_EOF1", 0x7D, 8 },
- { "LS_EOF1", 0x7E, 8 },
- { "SOFT_RST", 0x7F, 8 },
+ { "FAddr", MUSB_FADDR, 8 },
+ { "Power", MUSB_POWER, 8 },
+ { "Frame", MUSB_FRAME, 16 },
+ { "Index", MUSB_INDEX, 8 },
+ { "Testmode", MUSB_TESTMODE, 8 },
+ { "TxMaxPp", MUSB_TXMAXP, 16 },
+ { "TxCSRp", MUSB_TXCSR, 16 },
+ { "RxMaxPp", MUSB_RXMAXP, 16 },
+ { "RxCSR", MUSB_RXCSR, 16 },
+ { "RxCount", MUSB_RXCOUNT, 16 },
+ { "ConfigData", MUSB_CONFIGDATA,8 },
+ { "IntrRxE", MUSB_INTRRXE, 16 },
+ { "IntrTxE", MUSB_INTRTXE, 16 },
+ { "IntrUsbE", MUSB_INTRUSBE, 8 },
+ { "DevCtl", MUSB_DEVCTL, 8 },
+ { "BabbleCtl", MUSB_BABBLE_CTL,8 },
+ { "TxFIFOsz", MUSB_TXFIFOSZ, 8 },
+ { "RxFIFOsz", MUSB_RXFIFOSZ, 8 },
+ { "TxFIFOadd", MUSB_TXFIFOADD, 16 },
+ { "RxFIFOadd", MUSB_RXFIFOADD, 16 },
+ { "VControl", 0x68, 32 },
+ { "HWVers", 0x69, 16 },
+ { "EPInfo", MUSB_EPINFO, 8 },
+ { "RAMInfo", MUSB_RAMINFO, 8 },
+ { "LinkInfo", MUSB_LINKINFO, 8 },
+ { "VPLen", MUSB_VPLEN, 8 },
+ { "HS_EOF1", MUSB_HS_EOF1, 8 },
+ { "FS_EOF1", MUSB_FS_EOF1, 8 },
+ { "LS_EOF1", MUSB_LS_EOF1, 8 },
+ { "SOFT_RST", 0x7F, 8 },
{ "DMA_CNTLch0", 0x204, 16 },
{ "DMA_ADDRch0", 0x208, 32 },
{ "DMA_COUNTch0", 0x20C, 32 },
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 965c552f7035..53bd0e71d19f 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -179,9 +179,9 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || (musb->a_wait_bcon == 0 &&
- musb->xceiv->state == OTG_STATE_A_WAIT_BCON)) {
+ musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON)) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
del_timer(&glue->timer);
glue->last_timer = jiffies;
return;
@@ -201,7 +201,7 @@ static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
glue->last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, starting idle timer for %u ms\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
jiffies_to_msecs(timeout - jiffies));
mod_timer(&glue->timer, timeout);
}
@@ -265,10 +265,10 @@ static void otg_timer(unsigned long _musb)
*/
devctl = dsps_readb(mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "Poll devctl %02x (%s)\n", devctl,
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_BCON:
dsps_writeb(musb->mregs, MUSB_DEVCTL, 0);
skip_session = 1;
@@ -277,10 +277,10 @@ static void otg_timer(unsigned long _musb)
case OTG_STATE_A_IDLE:
case OTG_STATE_B_IDLE:
if (devctl & MUSB_DEVCTL_BDEVICE) {
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
@@ -288,7 +288,7 @@ static void otg_timer(unsigned long _musb)
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
break;
case OTG_STATE_A_WAIT_VFALL:
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
dsps_writel(musb->ctrl_base, wrp->coreintr_set,
MUSB_INTR_VBUSERROR << wrp->usb_shift);
break;
@@ -373,26 +373,26 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
* devctl.
*/
musb->int_usb &= ~MUSB_INTR_VBUSERROR;
- musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
mod_timer(&glue->timer,
jiffies + wrp->poll_seconds * HZ);
WARNING("VBUS error workaround (delay coming)\n");
} else if (drvvbus) {
MUSB_HST_MODE(musb);
musb->xceiv->otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
del_timer(&glue->timer);
} else {
musb->is_active = 0;
MUSB_DEV_MODE(musb);
musb->xceiv->otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
}
/* NOTE: this must complete power-on within 100 ms. */
dev_dbg(musb->controller, "VBUS %s (%s)%s, devctl %02x\n",
drvvbus ? "on" : "off",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
err ? " ERROR" : "",
devctl);
ret = IRQ_HANDLED;
@@ -402,7 +402,7 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
ret |= musb_interrupt(musb);
/* Poll for ID change in OTG port mode */
- if (musb->xceiv->state == OTG_STATE_B_IDLE &&
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
out:
@@ -633,6 +633,7 @@ static int dsps_musb_reset(struct musb *musb)
}
static struct musb_platform_ops dsps_ops = {
+ .quirks = MUSB_INDEXED_EP,
.init = dsps_musb_init,
.exit = dsps_musb_exit,
@@ -726,7 +727,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
config = devm_kzalloc(&parent->dev, sizeof(*config), GFP_KERNEL);
if (!config) {
- dev_err(dev, "failed to allocate musb hdrc config\n");
ret = -ENOMEM;
goto err;
}
@@ -778,10 +778,8 @@ static int dsps_probe(struct platform_device *pdev)
/* allocate glue */
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "unable to allocate glue memory\n");
+ if (!glue)
return -ENOMEM;
- }
glue->dev = &pdev->dev;
glue->wrp = wrp;
@@ -903,7 +901,7 @@ static int dsps_resume(struct device *dev)
dsps_writel(mbase, wrp->mode, glue->context.mode);
dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode);
dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
- if (musb->xceiv->state == OTG_STATE_B_IDLE &&
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 24c8c0219790..49b04cb6f5ca 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1546,7 +1546,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_B_PERIPHERAL:
/* NOTE: OTG state machine doesn't include B_SUSPENDED;
* that's part of the standard usb 1.1 state machine, and
@@ -1587,7 +1587,7 @@ static int musb_gadget_wakeup(struct usb_gadget *gadget)
goto done;
default:
dev_dbg(musb->controller, "Unhandled wake: %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
goto done;
}
@@ -1684,8 +1684,7 @@ static int musb_gadget_pullup(struct usb_gadget *gadget, int is_on)
static int musb_gadget_start(struct usb_gadget *g,
struct usb_gadget_driver *driver);
-static int musb_gadget_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver);
+static int musb_gadget_stop(struct usb_gadget *g);
static const struct usb_gadget_ops musb_gadget_operations = {
.get_frame = musb_gadget_get_frame,
@@ -1792,7 +1791,7 @@ int musb_gadget_setup(struct musb *musb)
MUSB_DEV_MODE(musb);
musb->xceiv->otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
/* this "gadget" abstracts/virtualizes the controller */
musb->g.name = musb_driver_name;
@@ -1851,8 +1850,6 @@ static int musb_gadget_start(struct usb_gadget *g,
pm_runtime_get_sync(musb->controller);
- dev_dbg(musb->controller, "registering driver %s\n", driver->function);
-
musb->softconnect = 0;
musb->gadget_driver = driver;
@@ -1860,7 +1857,7 @@ static int musb_gadget_start(struct usb_gadget *g,
musb->is_active = 1;
otg_set_peripheral(otg, &musb->g);
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
spin_unlock_irqrestore(&musb->lock, flags);
musb_start(musb);
@@ -1925,8 +1922,7 @@ static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
*
* @param driver the gadget driver to unregister
*/
-static int musb_gadget_stop(struct usb_gadget *g,
- struct usb_gadget_driver *driver)
+static int musb_gadget_stop(struct usb_gadget *g)
{
struct musb *musb = gadget_to_musb(g);
unsigned long flags;
@@ -1945,13 +1941,10 @@ static int musb_gadget_stop(struct usb_gadget *g,
(void) musb_gadget_vbus_draw(&musb->g, 0);
- musb->xceiv->state = OTG_STATE_UNDEFINED;
- stop_activity(musb, driver);
+ musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
+ stop_activity(musb, NULL);
otg_set_peripheral(musb->xceiv->otg, NULL);
- dev_dbg(musb->controller, "unregistering driver %s\n",
- driver ? driver->function : "(removed)");
-
musb->is_active = 0;
musb->gadget_driver = NULL;
musb_platform_try_idle(musb, 0);
@@ -1975,7 +1968,7 @@ static int musb_gadget_stop(struct usb_gadget *g,
void musb_g_resume(struct musb *musb)
{
musb->is_suspended = 0;
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_B_IDLE:
break;
case OTG_STATE_B_WAIT_ACON:
@@ -1989,7 +1982,7 @@ void musb_g_resume(struct musb *musb)
break;
default:
WARNING("unhandled RESUME transition (%s)\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
}
@@ -2001,10 +1994,10 @@ void musb_g_suspend(struct musb *musb)
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "devctl %02x\n", devctl);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_B_IDLE:
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
break;
case OTG_STATE_B_PERIPHERAL:
musb->is_suspended = 1;
@@ -2019,7 +2012,7 @@ void musb_g_suspend(struct musb *musb)
* A_PERIPHERAL may need care too
*/
WARNING("unhandled SUSPEND transition (%s)\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
}
@@ -2050,22 +2043,22 @@ void musb_g_disconnect(struct musb *musb)
spin_lock(&musb->lock);
}
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
default:
dev_dbg(musb->controller, "Unhandled disconnect %s, setting a_idle\n",
- usb_otg_state_string(musb->xceiv->state));
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ usb_otg_state_string(musb->xceiv->otg->state));
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
break;
case OTG_STATE_A_PERIPHERAL:
- musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
MUSB_HST_MODE(musb);
break;
case OTG_STATE_B_WAIT_ACON:
case OTG_STATE_B_HOST:
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_IDLE:
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
break;
case OTG_STATE_B_SRP_INIT:
break;
@@ -2090,9 +2083,12 @@ __acquires(musb->lock)
: NULL
);
- /* report disconnect, if we didn't already (flushing EP state) */
- if (musb->g.speed != USB_SPEED_UNKNOWN)
- musb_g_disconnect(musb);
+ /* report reset, if we didn't already (flushing EP state) */
+ if (musb->gadget_driver && musb->g.speed != USB_SPEED_UNKNOWN) {
+ spin_unlock(&musb->lock);
+ usb_gadget_udc_reset(&musb->g, musb->gadget_driver);
+ spin_lock(&musb->lock);
+ }
/* clear HR */
else if (devctl & MUSB_DEVCTL_HR)
@@ -2125,13 +2121,13 @@ __acquires(musb->lock)
* In that case, do not rely on devctl for setting
* peripheral mode.
*/
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
musb->g.is_a_peripheral = 0;
} else if (devctl & MUSB_DEVCTL_BDEVICE) {
- musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
musb->g.is_a_peripheral = 0;
} else {
- musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_A_PERIPHERAL;
musb->g.is_a_peripheral = 1;
}
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 855793d701bb..23d474d3d7f4 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2463,7 +2463,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
if (!is_host_active(musb))
return 0;
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
return 0;
case OTG_STATE_A_WAIT_VRISE:
@@ -2473,7 +2473,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
*/
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if ((devctl & MUSB_DEVCTL_VBUS) == MUSB_DEVCTL_VBUS)
- musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
break;
default:
break;
@@ -2481,7 +2481,7 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
if (musb->is_active) {
WARNING("trying to suspend as %s while active\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
return -EBUSY;
} else
return 0;
@@ -2678,7 +2678,7 @@ int musb_host_setup(struct musb *musb, int power_budget)
MUSB_HST_MODE(musb);
musb->xceiv->otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
otg_set_host(musb->xceiv->otg, &hcd->self);
hcd->self.otg_port = 1;
diff --git a/drivers/usb/musb/musb_io.h b/drivers/usb/musb/musb_io.h
index eebeed78edd6..8a57a6f4b3a6 100644
--- a/drivers/usb/musb/musb_io.h
+++ b/drivers/usb/musb/musb_io.h
@@ -37,86 +37,32 @@
#include <linux/io.h>
-#ifndef CONFIG_BLACKFIN
-
-/* NOTE: these offsets are all in bytes */
-
-static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
- { return __raw_readw(addr + offset); }
-
-static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
- { return __raw_readl(addr + offset); }
-
-
-static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
- { __raw_writew(data, addr + offset); }
-
-static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
- { __raw_writel(data, addr + offset); }
-
-
-#if defined(CONFIG_USB_MUSB_TUSB6010) || defined (CONFIG_USB_MUSB_TUSB6010_MODULE)
-
-/*
- * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
+#define musb_ep_select(_mbase, _epnum) musb->io.ep_select((_mbase), (_epnum))
+
+/**
+ * struct musb_io - IO functions for MUSB
+ * @quirks: platform specific flags
+ * @ep_offset: platform specific function to get end point offset
+ * @ep_select: platform specific function to select end point
+ * @fifo_offset: platform specific function to get fifo offset
+ * @read_fifo: platform specific function to read fifo
+ * @write_fifo: platform specific function to write fifo
*/
-static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
-{
- u16 tmp;
- u8 val;
-
- tmp = __raw_readw(addr + (offset & ~1));
- if (offset & 1)
- val = (tmp >> 8);
- else
- val = tmp & 0xff;
-
- return val;
-}
-
-static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
-{
- u16 tmp;
-
- tmp = __raw_readw(addr + (offset & ~1));
- if (offset & 1)
- tmp = (data << 8) | (tmp & 0xff);
- else
- tmp = (tmp & 0xff00) | data;
-
- __raw_writew(tmp, addr + (offset & ~1));
-}
-
-#else
-
-static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
- { return __raw_readb(addr + offset); }
-
-static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
- { __raw_writeb(data, addr + offset); }
-
-#endif /* CONFIG_USB_MUSB_TUSB6010 */
-
-#else
-
-static inline u8 musb_readb(const void __iomem *addr, unsigned offset)
- { return (u8) (bfin_read16(addr + offset)); }
-
-static inline u16 musb_readw(const void __iomem *addr, unsigned offset)
- { return bfin_read16(addr + offset); }
-
-static inline u32 musb_readl(const void __iomem *addr, unsigned offset)
- { return (u32) (bfin_read16(addr + offset)); }
-
-static inline void musb_writeb(void __iomem *addr, unsigned offset, u8 data)
- { bfin_write16(addr + offset, (u16) data); }
-
-static inline void musb_writew(void __iomem *addr, unsigned offset, u16 data)
- { bfin_write16(addr + offset, data); }
-
-static inline void musb_writel(void __iomem *addr, unsigned offset, u32 data)
- { bfin_write16(addr + offset, (u16) data); }
-
-#endif /* CONFIG_BLACKFIN */
+struct musb_io {
+ u32 quirks;
+ u32 (*ep_offset)(u8 epnum, u16 offset);
+ void (*ep_select)(void __iomem *mbase, u8 epnum);
+ u32 (*fifo_offset)(u8 epnum);
+ void (*read_fifo)(struct musb_hw_ep *hw_ep, u16 len, u8 *buf);
+ void (*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
+};
+
+/* Do not add new entries here, add them the struct musb_io instead */
+extern u8 (*musb_readb)(const void __iomem *addr, unsigned offset);
+extern void (*musb_writeb)(void __iomem *addr, unsigned offset, u8 data);
+extern u16 (*musb_readw)(const void __iomem *addr, unsigned offset);
+extern void (*musb_writew)(void __iomem *addr, unsigned offset, u16 data);
+extern u32 (*musb_readl)(const void __iomem *addr, unsigned offset);
+extern void (*musb_writel)(void __iomem *addr, unsigned offset, u32 data);
#endif
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index 37122a480bc1..11f0be07491e 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -239,14 +239,6 @@
#define MUSB_INDEX 0x0E /* 8 bit */
#define MUSB_TESTMODE 0x0F /* 8 bit */
-/* Get offset for a given FIFO from musb->mregs */
-#if defined(CONFIG_USB_MUSB_TUSB6010) || \
- defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
-#define MUSB_FIFO_OFFSET(epnum) (0x200 + ((epnum) * 0x20))
-#else
-#define MUSB_FIFO_OFFSET(epnum) (0x20 + ((epnum) * 4))
-#endif
-
/*
* Additional Control Registers
*/
@@ -295,21 +287,7 @@
#define MUSB_FIFOSIZE 0x0F
#define MUSB_CONFIGDATA MUSB_FIFOSIZE /* Re-used for EP0 */
-/* Offsets to endpoint registers in indexed model (using INDEX register) */
-#define MUSB_INDEXED_OFFSET(_epnum, _offset) \
- (0x10 + (_offset))
-
-/* Offsets to endpoint registers in flat models */
-#define MUSB_FLAT_OFFSET(_epnum, _offset) \
- (0x100 + (0x10*(_epnum)) + (_offset))
-
-#if defined(CONFIG_USB_MUSB_TUSB6010) || \
- defined(CONFIG_USB_MUSB_TUSB6010_MODULE)
-/* TUSB6010 EP0 configuration register is special */
-#define MUSB_TUSB_OFFSET(_epnum, _offset) \
- (0x10 + _offset)
#include "tusb6010.h" /* Needed "only" for TUSB_EP0_CONF */
-#endif
#define MUSB_TXCSR_MODE 0x2000
@@ -480,10 +458,6 @@ static inline u8 musb_read_txhubport(void __iomem *mbase, u8 epnum)
#define MUSB_INDEX USB_OFFSET(USB_INDEX) /* 8 bit */
#define MUSB_TESTMODE USB_OFFSET(USB_TESTMODE)/* 8 bit */
-/* Get offset for a given FIFO from musb->mregs */
-#define MUSB_FIFO_OFFSET(epnum) \
- (USB_OFFSET(USB_EP0_FIFO) + ((epnum) * 8))
-
/*
* Additional Control Registers
*/
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index e2d2d8c9891b..b072420e44f5 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -69,9 +69,10 @@ void musb_host_finish_resume(struct work_struct *work)
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
usb_hcd_poll_rh_status(musb->hcd);
/* NOTE: it might really be A_WAIT_BCON ... */
- musb->xceiv->state = OTG_STATE_A_HOST;
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
spin_unlock_irqrestore(&musb->lock, flags);
+ musb_host_resume_root_hub(musb);
}
void musb_port_suspend(struct musb *musb, bool do_suspend)
@@ -107,9 +108,9 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
dev_dbg(musb->controller, "Root port suspended, power %02x\n", power);
musb->port1_status |= USB_PORT_STAT_SUSPEND;
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_HOST:
- musb->xceiv->state = OTG_STATE_A_SUSPEND;
+ musb->xceiv->otg->state = OTG_STATE_A_SUSPEND;
musb->is_active = otg->host->b_hnp_enable;
if (musb->is_active)
mod_timer(&musb->otg_timer, jiffies
@@ -118,13 +119,13 @@ void musb_port_suspend(struct musb *musb, bool do_suspend)
musb_platform_try_idle(musb, 0);
break;
case OTG_STATE_B_HOST:
- musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
+ musb->xceiv->otg->state = OTG_STATE_B_WAIT_ACON;
musb->is_active = otg->host->b_hnp_enable;
musb_platform_try_idle(musb, 0);
break;
default:
dev_dbg(musb->controller, "bogus rh suspend? %s\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
} else if (power & MUSB_POWER_SUSPENDM) {
power &= ~MUSB_POWER_SUSPENDM;
@@ -145,7 +146,7 @@ void musb_port_reset(struct musb *musb, bool do_reset)
u8 power;
void __iomem *mbase = musb->mregs;
- if (musb->xceiv->state == OTG_STATE_B_IDLE) {
+ if (musb->xceiv->otg->state == OTG_STATE_B_IDLE) {
dev_dbg(musb->controller, "HNP: Returning from HNP; no hub reset from b_idle\n");
musb->port1_status &= ~USB_PORT_STAT_RESET;
return;
@@ -224,24 +225,24 @@ void musb_root_disconnect(struct musb *musb)
usb_hcd_poll_rh_status(musb->hcd);
musb->is_active = 0;
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_SUSPEND:
if (otg->host->b_hnp_enable) {
- musb->xceiv->state = OTG_STATE_A_PERIPHERAL;
+ musb->xceiv->otg->state = OTG_STATE_A_PERIPHERAL;
musb->g.is_a_peripheral = 1;
break;
}
/* FALLTHROUGH */
case OTG_STATE_A_HOST:
- musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
musb->is_active = 0;
break;
case OTG_STATE_A_WAIT_VFALL:
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
break;
default:
dev_dbg(musb->controller, "host disconnect (%s)\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
}
diff --git a/drivers/usb/musb/musbhsdma.c b/drivers/usb/musb/musbhsdma.c
index e8e9f9aab203..ab7ec09a8afe 100644
--- a/drivers/usb/musb/musbhsdma.c
+++ b/drivers/usb/musb/musbhsdma.c
@@ -195,6 +195,7 @@ static int dma_channel_abort(struct dma_channel *channel)
{
struct musb_dma_channel *musb_channel = channel->private_data;
void __iomem *mbase = musb_channel->controller->base;
+ struct musb *musb = musb_channel->controller->private_data;
u8 bchannel = musb_channel->idx;
int offset;
@@ -202,7 +203,7 @@ static int dma_channel_abort(struct dma_channel *channel)
if (channel->status == MUSB_DMA_STATUS_BUSY) {
if (musb_channel->transmit) {
- offset = MUSB_EP_OFFSET(musb_channel->epnum,
+ offset = musb->io.ep_offset(musb_channel->epnum,
MUSB_TXCSR);
/*
@@ -215,7 +216,7 @@ static int dma_channel_abort(struct dma_channel *channel)
csr &= ~MUSB_TXCSR_DMAMODE;
musb_writew(mbase, offset, csr);
} else {
- offset = MUSB_EP_OFFSET(musb_channel->epnum,
+ offset = musb->io.ep_offset(musb_channel->epnum,
MUSB_RXCSR);
csr = musb_readw(mbase, offset);
@@ -326,7 +327,7 @@ static irqreturn_t dma_controller_irq(int irq, void *private_data)
(musb_channel->max_packet_sz - 1)))
) {
u8 epnum = musb_channel->epnum;
- int offset = MUSB_EP_OFFSET(epnum,
+ int offset = musb->io.ep_offset(epnum,
MUSB_TXCSR);
u16 txcsr;
diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c
index d369bf1f3936..763649eb4987 100644
--- a/drivers/usb/musb/omap2430.c
+++ b/drivers/usb/musb/omap2430.c
@@ -65,15 +65,15 @@ static void musb_do_idle(unsigned long _musb)
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_BCON:
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE) {
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
} else {
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
MUSB_HST_MODE(musb);
}
break;
@@ -90,15 +90,15 @@ static void musb_do_idle(unsigned long _musb)
musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
usb_hcd_poll_rh_status(musb->hcd);
/* NOTE: it might really be A_WAIT_BCON ... */
- musb->xceiv->state = OTG_STATE_A_HOST;
+ musb->xceiv->otg->state = OTG_STATE_A_HOST;
}
break;
case OTG_STATE_A_HOST:
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (devctl & MUSB_DEVCTL_BDEVICE)
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
else
- musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
default:
break;
}
@@ -116,9 +116,9 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout)
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || ((musb->a_wait_bcon == 0)
- && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ && (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
del_timer(&musb_idle_timer);
last_timer = jiffies;
return;
@@ -135,7 +135,7 @@ static void omap2430_musb_try_idle(struct musb *musb, unsigned long timeout)
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
(unsigned long)jiffies_to_msecs(timeout - jiffies));
mod_timer(&musb_idle_timer, timeout);
}
@@ -153,7 +153,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (is_on) {
- if (musb->xceiv->state == OTG_STATE_A_IDLE) {
+ if (musb->xceiv->otg->state == OTG_STATE_A_IDLE) {
int loops = 100;
/* start the session */
devctl |= MUSB_DEVCTL_SESSION;
@@ -162,7 +162,8 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
* Wait for the musb to set as A device to enable the
* VBUS
*/
- while (musb_readb(musb->mregs, MUSB_DEVCTL) & 0x80) {
+ while (musb_readb(musb->mregs, MUSB_DEVCTL) &
+ MUSB_DEVCTL_BDEVICE) {
mdelay(5);
cpu_relax();
@@ -179,7 +180,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
} else {
musb->is_active = 1;
otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
}
@@ -191,7 +192,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
*/
otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
devctl &= ~MUSB_DEVCTL_SESSION;
MUSB_DEV_MODE(musb);
@@ -200,7 +201,7 @@ static void omap2430_musb_set_vbus(struct musb *musb, int is_on)
dev_dbg(musb->controller, "VBUS %s, devctl %02x "
/* otg %3x conf %08x prcm %08x */ "\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
musb_readb(musb->mregs, MUSB_DEVCTL));
}
@@ -265,7 +266,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
dev_dbg(dev, "ID GND\n");
otg->default_a = true;
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
musb->xceiv->last_event = USB_EVENT_ID;
if (musb->gadget_driver) {
pm_runtime_get_sync(dev);
@@ -279,7 +280,7 @@ static void omap_musb_set_mailbox(struct omap2430_glue *glue)
dev_dbg(dev, "VBUS Connect\n");
otg->default_a = false;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
musb->xceiv->last_event = USB_EVENT_VBUS;
if (musb->gadget_driver)
pm_runtime_get_sync(dev);
@@ -518,10 +519,8 @@ static int omap2430_probe(struct platform_device *pdev)
int ret = -ENOMEM;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "failed to allocate glue context\n");
+ if (!glue)
goto err0;
- }
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
@@ -543,25 +542,16 @@ static int omap2430_probe(struct platform_device *pdev)
struct platform_device *control_pdev;
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (!pdata) {
- dev_err(&pdev->dev,
- "failed to allocate musb platform data\n");
+ if (!pdata)
goto err2;
- }
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
- if (!data) {
- dev_err(&pdev->dev,
- "failed to allocate musb board data\n");
+ if (!data)
goto err2;
- }
config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
- if (!config) {
- dev_err(&pdev->dev,
- "failed to allocate musb hdrc config\n");
+ if (!config)
goto err2;
- }
of_property_read_u32(np, "mode", (u32 *)&pdata->mode);
of_property_read_u32(np, "interface-type",
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 2daa779f1382..3a5ffd575438 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -126,6 +126,52 @@ static void tusb_wbus_quirk(struct musb *musb, int enabled)
}
}
+static u32 tusb_fifo_offset(u8 epnum)
+{
+ return 0x200 + (epnum * 0x20);
+}
+
+static u32 tusb_ep_offset(u8 epnum, u16 offset)
+{
+ return 0x10 + offset;
+}
+
+/* TUSB mapping: "flat" plus ep0 special cases */
+static void tusb_ep_select(void __iomem *mbase, u8 epnum)
+{
+ musb_writeb(mbase, MUSB_INDEX, epnum);
+}
+
+/*
+ * TUSB6010 doesn't allow 8-bit access; 16-bit access is the minimum.
+ */
+static u8 tusb_readb(const void __iomem *addr, unsigned offset)
+{
+ u16 tmp;
+ u8 val;
+
+ tmp = __raw_readw(addr + (offset & ~1));
+ if (offset & 1)
+ val = (tmp >> 8);
+ else
+ val = tmp & 0xff;
+
+ return val;
+}
+
+static void tusb_writeb(void __iomem *addr, unsigned offset, u8 data)
+{
+ u16 tmp;
+
+ tmp = __raw_readw(addr + (offset & ~1));
+ if (offset & 1)
+ tmp = (data << 8) | (tmp & 0xff);
+ else
+ tmp = (tmp & 0xff00) | data;
+
+ __raw_writew(tmp, addr + (offset & ~1));
+}
+
/*
* TUSB 6010 may use a parallel bus that doesn't support byte ops;
* so both loading and unloading FIFOs need explicit byte counts.
@@ -173,7 +219,7 @@ static inline void tusb_fifo_read_unaligned(void __iomem *fifo,
}
}
-void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf)
+static void tusb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf)
{
struct musb *musb = hw_ep->musb;
void __iomem *ep_conf = hw_ep->conf;
@@ -223,7 +269,7 @@ void musb_write_fifo(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf)
tusb_fifo_write_unaligned(fifo, buf, len);
}
-void musb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
+static void tusb_read_fifo(struct musb_hw_ep *hw_ep, u16 len, u8 *buf)
{
struct musb *musb = hw_ep->musb;
void __iomem *ep_conf = hw_ep->conf;
@@ -415,13 +461,13 @@ static void musb_do_idle(unsigned long _musb)
spin_lock_irqsave(&musb->lock, flags);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_BCON:
if ((musb->a_wait_bcon != 0)
&& (musb->idle_timeout == 0
|| time_after(jiffies, musb->idle_timeout))) {
dev_dbg(musb->controller, "Nothing connected %s, turning off VBUS\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
}
/* FALLTHROUGH */
case OTG_STATE_A_IDLE:
@@ -474,9 +520,9 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout)
/* Never idle if active, or when VBUS timeout is not set as host */
if (musb->is_active || ((musb->a_wait_bcon == 0)
- && (musb->xceiv->state == OTG_STATE_A_WAIT_BCON))) {
+ && (musb->xceiv->otg->state == OTG_STATE_A_WAIT_BCON))) {
dev_dbg(musb->controller, "%s active, deleting timer\n",
- usb_otg_state_string(musb->xceiv->state));
+ usb_otg_state_string(musb->xceiv->otg->state));
del_timer(&musb_idle_timer);
last_timer = jiffies;
return;
@@ -493,7 +539,7 @@ static void tusb_musb_try_idle(struct musb *musb, unsigned long timeout)
last_timer = timeout;
dev_dbg(musb->controller, "%s inactive, for idle timer for %lu ms\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
(unsigned long)jiffies_to_msecs(timeout - jiffies));
mod_timer(&musb_idle_timer, timeout);
}
@@ -524,7 +570,7 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on)
if (is_on) {
timer = OTG_TIMER_MS(OTG_TIME_A_WAIT_VRISE);
otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
conf |= TUSB_DEV_CONF_USB_HOST_MODE;
@@ -537,16 +583,16 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on)
/* If ID pin is grounded, we want to be a_idle */
otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT);
if (!(otg_stat & TUSB_DEV_OTG_STAT_ID_STATUS)) {
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_VRISE:
case OTG_STATE_A_WAIT_BCON:
- musb->xceiv->state = OTG_STATE_A_WAIT_VFALL;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
break;
case OTG_STATE_A_WAIT_VFALL:
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
break;
default:
- musb->xceiv->state = OTG_STATE_A_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_A_IDLE;
}
musb->is_active = 0;
otg->default_a = 1;
@@ -554,7 +600,7 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on)
} else {
musb->is_active = 0;
otg->default_a = 0;
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
MUSB_DEV_MODE(musb);
}
@@ -569,7 +615,7 @@ static void tusb_musb_set_vbus(struct musb *musb, int is_on)
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
dev_dbg(musb->controller, "VBUS %s, devctl %02x otg %3x conf %08x prcm %08x\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
musb_readb(musb->mregs, MUSB_DEVCTL),
musb_readl(tbase, TUSB_DEV_OTG_STAT),
conf, prcm);
@@ -668,23 +714,23 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
if (otg_stat & TUSB_DEV_OTG_STAT_SESS_END) {
dev_dbg(musb->controller, "Forcing disconnect (no interrupt)\n");
- if (musb->xceiv->state != OTG_STATE_B_IDLE) {
+ if (musb->xceiv->otg->state != OTG_STATE_B_IDLE) {
/* INTR_DISCONNECT can hide... */
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
musb->int_usb |= MUSB_INTR_DISCONNECT;
}
musb->is_active = 0;
}
dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
- usb_otg_state_string(musb->xceiv->state), otg_stat);
+ usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
idle_timeout = jiffies + (1 * HZ);
schedule_work(&musb->irq_work);
} else /* A-dev state machine */ {
dev_dbg(musb->controller, "vbus change, %s, otg %03x\n",
- usb_otg_state_string(musb->xceiv->state), otg_stat);
+ usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_IDLE:
dev_dbg(musb->controller, "Got SRP, turning on VBUS\n");
musb_platform_set_vbus(musb, 1);
@@ -731,9 +777,9 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
u8 devctl;
dev_dbg(musb->controller, "%s timer, %03x\n",
- usb_otg_state_string(musb->xceiv->state), otg_stat);
+ usb_otg_state_string(musb->xceiv->otg->state), otg_stat);
- switch (musb->xceiv->state) {
+ switch (musb->xceiv->otg->state) {
case OTG_STATE_A_WAIT_VRISE:
/* VBUS has probably been valid for a while now,
* but may well have bounced out of range a bit
@@ -745,7 +791,7 @@ tusb_otg_ints(struct musb *musb, u32 int_src, void __iomem *tbase)
dev_dbg(musb->controller, "devctl %02x\n", devctl);
break;
}
- musb->xceiv->state = OTG_STATE_A_WAIT_BCON;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_BCON;
musb->is_active = 0;
idle_timeout = jiffies
+ msecs_to_jiffies(musb->a_wait_bcon);
@@ -1135,9 +1181,17 @@ static int tusb_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops tusb_ops = {
+ .quirks = MUSB_IN_TUSB,
.init = tusb_musb_init,
.exit = tusb_musb_exit,
+ .ep_offset = tusb_ep_offset,
+ .ep_select = tusb_ep_select,
+ .fifo_offset = tusb_fifo_offset,
+ .readb = tusb_readb,
+ .writeb = tusb_writeb,
+ .read_fifo = tusb_read_fifo,
+ .write_fifo = tusb_write_fifo,
.enable = tusb_musb_enable,
.disable = tusb_musb_disable,
@@ -1164,10 +1218,8 @@ static int tusb_probe(struct platform_device *pdev)
int ret;
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "failed to allocate glue context\n");
+ if (!glue)
return -ENOMEM;
- }
glue->dev = &pdev->dev;
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index dc666e96f45f..abf72728825f 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -56,7 +56,7 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
if (is_on) {
- if (musb->xceiv->state == OTG_STATE_A_IDLE) {
+ if (musb->xceiv->otg->state == OTG_STATE_A_IDLE) {
/* start the session */
devctl |= MUSB_DEVCTL_SESSION;
musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
@@ -76,7 +76,7 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
} else {
musb->is_active = 1;
musb->xceiv->otg->default_a = 1;
- musb->xceiv->state = OTG_STATE_A_WAIT_VRISE;
+ musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
devctl |= MUSB_DEVCTL_SESSION;
MUSB_HST_MODE(musb);
}
@@ -102,7 +102,7 @@ static void ux500_musb_set_vbus(struct musb *musb, int is_on)
mdelay(200);
dev_dbg(musb->controller, "VBUS %s, devctl %02x\n",
- usb_otg_state_string(musb->xceiv->state),
+ usb_otg_state_string(musb->xceiv->otg->state),
musb_readb(musb->mregs, MUSB_DEVCTL));
}
@@ -112,7 +112,7 @@ static int musb_otg_notifications(struct notifier_block *nb,
struct musb *musb = container_of(nb, struct musb, nb);
dev_dbg(musb->controller, "musb_otg_notifications %ld %s\n",
- event, usb_otg_state_string(musb->xceiv->state));
+ event, usb_otg_state_string(musb->xceiv->otg->state));
switch (event) {
case UX500_MUSB_ID:
@@ -127,7 +127,7 @@ static int musb_otg_notifications(struct notifier_block *nb,
if (is_host_active(musb))
ux500_musb_set_vbus(musb, 0);
else
- musb->xceiv->state = OTG_STATE_B_IDLE;
+ musb->xceiv->otg->state = OTG_STATE_B_IDLE;
break;
default:
dev_dbg(musb->controller, "ID float\n");
@@ -188,8 +188,10 @@ static int ux500_musb_exit(struct musb *musb)
}
static const struct musb_platform_ops ux500_ops = {
+ .quirks = MUSB_INDEXED_EP,
.init = ux500_musb_init,
.exit = ux500_musb_exit,
+ .fifo_mode = 5,
.set_vbus = ux500_musb_set_vbus,
};
@@ -247,10 +249,8 @@ static int ux500_probe(struct platform_device *pdev)
}
glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
- if (!glue) {
- dev_err(&pdev->dev, "failed to allocate glue context\n");
+ if (!glue)
goto err0;
- }
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index 221faed9f074..e93845c26bdb 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -91,9 +91,9 @@ static bool ux500_configure_channel(struct dma_channel *channel,
struct scatterlist sg;
struct dma_slave_config slave_conf;
enum dma_slave_buswidth addr_width;
- dma_addr_t usb_fifo_addr = (MUSB_FIFO_OFFSET(hw_ep->epnum) +
- ux500_channel->controller->phy_base);
struct musb *musb = ux500_channel->controller->private_data;
+ dma_addr_t usb_fifo_addr = (musb->io.fifo_offset(hw_ep->epnum) +
+ ux500_channel->controller->phy_base);
dev_dbg(musb->controller,
"packet_sz=%d, mode=%d, dma_addr=0x%llx, len=%d is_tx=%d\n",
@@ -121,8 +121,7 @@ static bool ux500_configure_channel(struct dma_channel *channel,
slave_conf.dst_maxburst = 16;
slave_conf.device_fc = false;
- dma_chan->device->device_control(dma_chan, DMA_SLAVE_CONFIG,
- (unsigned long) &slave_conf);
+ dmaengine_slave_config(dma_chan, &slave_conf);
dma_desc = dmaengine_prep_slave_sg(dma_chan, &sg, 1, direction,
DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
@@ -246,9 +245,7 @@ static int ux500_dma_channel_abort(struct dma_channel *channel)
musb_writew(epio, MUSB_RXCSR, csr);
}
- ux500_channel->dma_chan->device->
- device_control(ux500_channel->dma_chan,
- DMA_TERMINATE_ALL, 0);
+ dmaengine_terminate_all(ux500_channel->dma_chan);
channel->status = MUSB_DMA_STATUS_FREE;
}
return 0;
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 11ab2c45e462..8cd7d193c2ca 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -1,6 +1,4 @@
/*
- * drivers/usb/otg/ab8500_usb.c
- *
* USB transceiver driver for AB8500 family chips
*
* Copyright (C) 2010-2013 ST-Ericsson AB
@@ -446,7 +444,8 @@ static int ab9540_usb_link_status_update(struct ab8500_usb *ab,
if (event != UX500_MUSB_RIDB)
event = UX500_MUSB_NONE;
/* Fallback to default B_IDLE as nothing is connected. */
- ab->phy.state = OTG_STATE_B_IDLE;
+ ab->phy.otg->state = OTG_STATE_B_IDLE;
+ usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
break;
case USB_LINK_ACA_RID_C_NM_9540:
@@ -461,12 +460,14 @@ static int ab9540_usb_link_status_update(struct ab8500_usb *ab,
ab8500_usb_peri_phy_en(ab);
atomic_notifier_call_chain(&ab->phy.notifier,
UX500_MUSB_PREPARE, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
}
if (ab->mode == USB_IDLE) {
ab->mode = USB_PERIPHERAL;
ab8500_usb_peri_phy_en(ab);
atomic_notifier_call_chain(&ab->phy.notifier,
UX500_MUSB_PREPARE, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
}
if (event != UX500_MUSB_RIDC)
event = UX500_MUSB_VBUS;
@@ -502,6 +503,7 @@ static int ab9540_usb_link_status_update(struct ab8500_usb *ab,
event = UX500_MUSB_CHARGER;
atomic_notifier_call_chain(&ab->phy.notifier,
event, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
break;
case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_9540:
@@ -526,6 +528,7 @@ static int ab9540_usb_link_status_update(struct ab8500_usb *ab,
ab->mode = USB_IDLE;
ab->phy.otg->default_a = false;
ab->vbus_draw = 0;
+ usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
}
}
break;
@@ -584,7 +587,8 @@ static int ab8540_usb_link_status_update(struct ab8500_usb *ab,
* Fallback to default B_IDLE as nothing
* is connected
*/
- ab->phy.state = OTG_STATE_B_IDLE;
+ ab->phy.otg->state = OTG_STATE_B_IDLE;
+ usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
break;
case USB_LINK_ACA_RID_C_NM_8540:
@@ -598,6 +602,7 @@ static int ab8540_usb_link_status_update(struct ab8500_usb *ab,
ab8500_usb_peri_phy_en(ab);
atomic_notifier_call_chain(&ab->phy.notifier,
UX500_MUSB_PREPARE, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
}
if (event != UX500_MUSB_RIDC)
event = UX500_MUSB_VBUS;
@@ -626,6 +631,7 @@ static int ab8540_usb_link_status_update(struct ab8500_usb *ab,
event = UX500_MUSB_CHARGER;
atomic_notifier_call_chain(&ab->phy.notifier,
event, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
break;
case USB_LINK_PHYEN_NO_VBUS_NO_IDGND_8540:
@@ -648,6 +654,7 @@ static int ab8540_usb_link_status_update(struct ab8500_usb *ab,
ab->mode = USB_IDLE;
ab->phy.otg->default_a = false;
ab->vbus_draw = 0;
+ usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
}
break;
@@ -693,7 +700,8 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
* Fallback to default B_IDLE as nothing
* is connected
*/
- ab->phy.state = OTG_STATE_B_IDLE;
+ ab->phy.otg->state = OTG_STATE_B_IDLE;
+ usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
break;
case USB_LINK_ACA_RID_C_NM_8505:
@@ -707,6 +715,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
ab8500_usb_peri_phy_en(ab);
atomic_notifier_call_chain(&ab->phy.notifier,
UX500_MUSB_PREPARE, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
}
if (event != UX500_MUSB_RIDC)
event = UX500_MUSB_VBUS;
@@ -734,6 +743,7 @@ static int ab8505_usb_link_status_update(struct ab8500_usb *ab,
event = UX500_MUSB_CHARGER;
atomic_notifier_call_chain(&ab->phy.notifier,
event, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
break;
default:
@@ -776,7 +786,8 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
if (event != UX500_MUSB_RIDB)
event = UX500_MUSB_NONE;
/* Fallback to default B_IDLE as nothing is connected */
- ab->phy.state = OTG_STATE_B_IDLE;
+ ab->phy.otg->state = OTG_STATE_B_IDLE;
+ usb_phy_set_event(&ab->phy, USB_EVENT_NONE);
break;
case USB_LINK_ACA_RID_C_NM_8500:
@@ -794,6 +805,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
ab8500_usb_peri_phy_en(ab);
atomic_notifier_call_chain(&ab->phy.notifier,
UX500_MUSB_PREPARE, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_ENUMERATED);
}
if (event != UX500_MUSB_RIDC)
event = UX500_MUSB_VBUS;
@@ -820,6 +832,7 @@ static int ab8500_usb_link_status_update(struct ab8500_usb *ab,
event = UX500_MUSB_CHARGER;
atomic_notifier_call_chain(&ab->phy.notifier,
event, &ab->vbus_draw);
+ usb_phy_set_event(&ab->phy, USB_EVENT_CHARGER);
break;
case USB_LINK_RESERVED_8500:
@@ -1056,7 +1069,7 @@ static int ab8500_usb_set_peripheral(struct usb_otg *otg,
if (!otg)
return -ENODEV;
- ab = phy_to_ab(otg->phy);
+ ab = phy_to_ab(otg->usb_phy);
ab->phy.otg->gadget = gadget;
@@ -1080,7 +1093,7 @@ static int ab8500_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
if (!otg)
return -ENODEV;
- ab = phy_to_ab(otg->phy);
+ ab = phy_to_ab(otg->usb_phy);
ab->phy.otg->host = host;
@@ -1380,9 +1393,9 @@ static int ab8500_usb_probe(struct platform_device *pdev)
ab->phy.label = "ab8500";
ab->phy.set_suspend = ab8500_usb_set_suspend;
ab->phy.set_power = ab8500_usb_set_power;
- ab->phy.state = OTG_STATE_UNDEFINED;
+ ab->phy.otg->state = OTG_STATE_UNDEFINED;
- otg->phy = &ab->phy;
+ otg->usb_phy = &ab->phy;
otg->set_host = ab8500_usb_set_host;
otg->set_peripheral = ab8500_usb_set_peripheral;
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 35b6083b7999..2e923c581809 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -147,10 +147,8 @@ static int am335x_control_usb_probe(struct platform_device *pdev)
phy_ctrl = of_id->data;
ctrl_usb = devm_kzalloc(&pdev->dev, sizeof(*ctrl_usb), GFP_KERNEL);
- if (!ctrl_usb) {
- dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
+ if (!ctrl_usb)
return -ENOMEM;
- }
ctrl_usb->dev = &pdev->dev;
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index f1ea5990a50a..ab38aa32a6c1 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -274,7 +274,7 @@ void b_srp_end(unsigned long foo)
fsl_otg_dischrg_vbus(0);
srp_wait_done = 1;
- if ((fsl_otg_dev->phy.state == OTG_STATE_B_SRP_INIT) &&
+ if ((fsl_otg_dev->phy.otg->state == OTG_STATE_B_SRP_INIT) &&
fsl_otg_dev->fsm.b_sess_vld)
fsl_otg_dev->fsm.b_srp_done = 1;
}
@@ -499,7 +499,8 @@ int fsl_otg_start_host(struct otg_fsm *fsm, int on)
{
struct usb_otg *otg = fsm->otg;
struct device *dev;
- struct fsl_otg *otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ struct fsl_otg *otg_dev =
+ container_of(otg->usb_phy, struct fsl_otg, phy);
u32 retval = 0;
if (!otg->host)
@@ -594,7 +595,7 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
if (!otg)
return -ENODEV;
- otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
if (otg_dev != fsl_otg_dev)
return -ENODEV;
@@ -623,7 +624,7 @@ static int fsl_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
/* Mini-A cable connected */
struct otg_fsm *fsm = &otg_dev->fsm;
- otg->phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
fsm->protocol = PROTO_UNDEF;
}
}
@@ -644,7 +645,7 @@ static int fsl_otg_set_peripheral(struct usb_otg *otg,
if (!otg)
return -ENODEV;
- otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
VDBG("otg_dev 0x%x\n", (int)otg_dev);
VDBG("fsl_otg_dev 0x%x\n", (int)fsl_otg_dev);
if (otg_dev != fsl_otg_dev)
@@ -681,7 +682,7 @@ static int fsl_otg_set_power(struct usb_phy *phy, unsigned mA)
{
if (!fsl_otg_dev)
return -ENODEV;
- if (phy->state == OTG_STATE_B_PERIPHERAL)
+ if (phy->otg->state == OTG_STATE_B_PERIPHERAL)
pr_info("FSL OTG: Draw %d mA\n", mA);
return 0;
@@ -714,10 +715,10 @@ static int fsl_otg_start_srp(struct usb_otg *otg)
{
struct fsl_otg *otg_dev;
- if (!otg || otg->phy->state != OTG_STATE_B_IDLE)
+ if (!otg || otg->state != OTG_STATE_B_IDLE)
return -ENODEV;
- otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
if (otg_dev != fsl_otg_dev)
return -ENODEV;
@@ -735,7 +736,7 @@ static int fsl_otg_start_hnp(struct usb_otg *otg)
if (!otg)
return -ENODEV;
- otg_dev = container_of(otg->phy, struct fsl_otg, phy);
+ otg_dev = container_of(otg->usb_phy, struct fsl_otg, phy);
if (otg_dev != fsl_otg_dev)
return -ENODEV;
@@ -857,7 +858,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
fsl_otg_tc->phy.dev = &pdev->dev;
fsl_otg_tc->phy.set_power = fsl_otg_set_power;
- fsl_otg_tc->phy.otg->phy = &fsl_otg_tc->phy;
+ fsl_otg_tc->phy.otg->usb_phy = &fsl_otg_tc->phy;
fsl_otg_tc->phy.otg->set_host = fsl_otg_set_host;
fsl_otg_tc->phy.otg->set_peripheral = fsl_otg_set_peripheral;
fsl_otg_tc->phy.otg->start_hnp = fsl_otg_start_hnp;
@@ -989,10 +990,10 @@ int usb_otg_start(struct platform_device *pdev)
* Also: record initial state of ID pin
*/
if (fsl_readl(&p_otg->dr_mem_map->otgsc) & OTGSC_STS_USB_ID) {
- p_otg->phy.state = OTG_STATE_UNDEFINED;
+ p_otg->phy.otg->state = OTG_STATE_UNDEFINED;
p_otg->fsm.id = 1;
} else {
- p_otg->phy.state = OTG_STATE_A_IDLE;
+ p_otg->phy.otg->state = OTG_STATE_A_IDLE;
p_otg->fsm.id = 0;
}
@@ -1047,7 +1048,7 @@ static int show_fsl_usb2_otg_state(struct device *dev,
/* State */
t = scnprintf(next, size,
"OTG state: %s\n\n",
- usb_otg_state_string(fsl_otg_dev->phy.state));
+ usb_otg_state_string(fsl_otg_dev->phy.otg->state));
size -= t;
next += t;
diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
index 5986c96354df..23149954a09c 100644
--- a/drivers/usb/phy/phy-fsl-usb.h
+++ b/drivers/usb/phy/phy-fsl-usb.h
@@ -298,7 +298,7 @@
/* SE0 Time Before SRP */
#define TB_SE0_SRP (2) /* b_idle,minimum 2 ms, section:5.3.2 */
-#define SET_OTG_STATE(otg_ptr, newstate) ((otg_ptr)->state = newstate)
+#define SET_OTG_STATE(phy, newstate) ((phy)->otg->state = newstate)
struct usb_dr_mmap {
/* Capability register */
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index 7594e5069ae5..4ba1f57220f6 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -1,6 +1,4 @@
/*
- * drivers/usb/otg/nop-usb-xceiv.c
- *
* NOP USB transceiver for all USB transceiver which are either built-in
* into USB IP or which are mostly autonomous.
*
@@ -123,7 +121,7 @@ static int nop_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
}
otg->gadget = gadget;
- otg->phy->state = OTG_STATE_B_IDLE;
+ otg->state = OTG_STATE_B_IDLE;
return 0;
}
@@ -225,10 +223,10 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
nop->phy.dev = nop->dev;
nop->phy.label = "nop-xceiv";
nop->phy.set_suspend = nop_set_suspend;
- nop->phy.state = OTG_STATE_UNDEFINED;
nop->phy.type = type;
- nop->phy.otg->phy = &nop->phy;
+ nop->phy.otg->state = OTG_STATE_UNDEFINED;
+ nop->phy.otg->usb_phy = &nop->phy;
nop->phy.otg->set_host = nop_set_host;
nop->phy.otg->set_peripheral = nop_set_peripheral;
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index f4b14bd97e14..54511725caaf 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -121,7 +121,7 @@ static void gpio_vbus_work(struct work_struct *work)
if (vbus) {
status = USB_EVENT_VBUS;
- gpio_vbus->phy.state = OTG_STATE_B_PERIPHERAL;
+ gpio_vbus->phy.otg->state = OTG_STATE_B_PERIPHERAL;
gpio_vbus->phy.last_event = status;
usb_gadget_vbus_connect(gpio_vbus->phy.otg->gadget);
@@ -134,6 +134,7 @@ static void gpio_vbus_work(struct work_struct *work)
atomic_notifier_call_chain(&gpio_vbus->phy.notifier,
status, gpio_vbus->phy.otg->gadget);
+ usb_phy_set_event(&gpio_vbus->phy, USB_EVENT_ENUMERATED);
} else {
/* optionally disable D+ pullup */
if (gpio_is_valid(gpio))
@@ -143,11 +144,12 @@ static void gpio_vbus_work(struct work_struct *work)
usb_gadget_vbus_disconnect(gpio_vbus->phy.otg->gadget);
status = USB_EVENT_NONE;
- gpio_vbus->phy.state = OTG_STATE_B_IDLE;
+ gpio_vbus->phy.otg->state = OTG_STATE_B_IDLE;
gpio_vbus->phy.last_event = status;
atomic_notifier_call_chain(&gpio_vbus->phy.notifier,
status, gpio_vbus->phy.otg->gadget);
+ usb_phy_set_event(&gpio_vbus->phy, USB_EVENT_NONE);
}
}
@@ -180,7 +182,7 @@ static int gpio_vbus_set_peripheral(struct usb_otg *otg,
struct platform_device *pdev;
int gpio;
- gpio_vbus = container_of(otg->phy, struct gpio_vbus_data, phy);
+ gpio_vbus = container_of(otg->usb_phy, struct gpio_vbus_data, phy);
pdev = to_platform_device(gpio_vbus->dev);
pdata = dev_get_platdata(gpio_vbus->dev);
gpio = pdata->gpio_pullup;
@@ -196,7 +198,7 @@ static int gpio_vbus_set_peripheral(struct usb_otg *otg,
set_vbus_draw(gpio_vbus, 0);
usb_gadget_vbus_disconnect(otg->gadget);
- otg->phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
otg->gadget = NULL;
return 0;
@@ -218,7 +220,7 @@ static int gpio_vbus_set_power(struct usb_phy *phy, unsigned mA)
gpio_vbus = container_of(phy, struct gpio_vbus_data, phy);
- if (phy->state == OTG_STATE_B_PERIPHERAL)
+ if (phy->otg->state == OTG_STATE_B_PERIPHERAL)
set_vbus_draw(gpio_vbus, mA);
return 0;
}
@@ -269,9 +271,9 @@ static int gpio_vbus_probe(struct platform_device *pdev)
gpio_vbus->phy.dev = gpio_vbus->dev;
gpio_vbus->phy.set_power = gpio_vbus_set_power;
gpio_vbus->phy.set_suspend = gpio_vbus_set_suspend;
- gpio_vbus->phy.state = OTG_STATE_UNDEFINED;
- gpio_vbus->phy.otg->phy = &gpio_vbus->phy;
+ gpio_vbus->phy.otg->state = OTG_STATE_UNDEFINED;
+ gpio_vbus->phy.otg->usb_phy = &gpio_vbus->phy;
gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral;
err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect");
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index 8eea56d3ded6..a2dfb2ae520e 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -234,7 +234,7 @@ isp1301_clear_bits(struct isp1301 *isp, u8 reg, u8 bits)
static inline const char *state_name(struct isp1301 *isp)
{
- return usb_otg_state_string(isp->phy.state);
+ return usb_otg_state_string(isp->phy.otg->state);
}
/*-------------------------------------------------------------------------*/
@@ -249,7 +249,7 @@ static inline const char *state_name(struct isp1301 *isp)
static void power_down(struct isp1301 *isp)
{
- isp->phy.state = OTG_STATE_UNDEFINED;
+ isp->phy.otg->state = OTG_STATE_UNDEFINED;
// isp1301_set_bits(isp, ISP1301_MODE_CONTROL_2, MC2_GLOBAL_PWR_DN);
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_SUSPEND);
@@ -339,7 +339,7 @@ static void a_idle(struct isp1301 *isp, const char *tag)
{
u32 l;
- if (isp->phy.state == OTG_STATE_A_IDLE)
+ if (isp->phy.otg->state == OTG_STATE_A_IDLE)
return;
isp->phy.otg->default_a = 1;
@@ -351,7 +351,7 @@ static void a_idle(struct isp1301 *isp, const char *tag)
isp->phy.otg->gadget->is_a_peripheral = 1;
gadget_suspend(isp);
}
- isp->phy.state = OTG_STATE_A_IDLE;
+ isp->phy.otg->state = OTG_STATE_A_IDLE;
l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
omap_writel(l, OTG_CTRL);
isp->last_otg_ctrl = l;
@@ -363,7 +363,7 @@ static void b_idle(struct isp1301 *isp, const char *tag)
{
u32 l;
- if (isp->phy.state == OTG_STATE_B_IDLE)
+ if (isp->phy.otg->state == OTG_STATE_B_IDLE)
return;
isp->phy.otg->default_a = 0;
@@ -375,7 +375,7 @@ static void b_idle(struct isp1301 *isp, const char *tag)
isp->phy.otg->gadget->is_a_peripheral = 0;
gadget_suspend(isp);
}
- isp->phy.state = OTG_STATE_B_IDLE;
+ isp->phy.otg->state = OTG_STATE_B_IDLE;
l = omap_readl(OTG_CTRL) & OTG_XCEIV_OUTPUTS;
omap_writel(l, OTG_CTRL);
isp->last_otg_ctrl = l;
@@ -474,7 +474,7 @@ static void check_state(struct isp1301 *isp, const char *tag)
default:
break;
}
- if (isp->phy.state == state && !extra)
+ if (isp->phy.otg->state == state && !extra)
return;
pr_debug("otg: %s FSM %s/%02x, %s, %06x\n", tag,
usb_otg_state_string(state), fsm, state_name(isp),
@@ -498,23 +498,23 @@ static void update_otg1(struct isp1301 *isp, u8 int_src)
if (int_src & INTR_SESS_VLD)
otg_ctrl |= OTG_ASESSVLD;
- else if (isp->phy.state == OTG_STATE_A_WAIT_VFALL) {
+ else if (isp->phy.otg->state == OTG_STATE_A_WAIT_VFALL) {
a_idle(isp, "vfall");
otg_ctrl &= ~OTG_CTRL_BITS;
}
if (int_src & INTR_VBUS_VLD)
otg_ctrl |= OTG_VBUSVLD;
if (int_src & INTR_ID_GND) { /* default-A */
- if (isp->phy.state == OTG_STATE_B_IDLE
- || isp->phy.state
+ if (isp->phy.otg->state == OTG_STATE_B_IDLE
+ || isp->phy.otg->state
== OTG_STATE_UNDEFINED) {
a_idle(isp, "init");
return;
}
} else { /* default-B */
otg_ctrl |= OTG_ID;
- if (isp->phy.state == OTG_STATE_A_IDLE
- || isp->phy.state == OTG_STATE_UNDEFINED) {
+ if (isp->phy.otg->state == OTG_STATE_A_IDLE
+ || isp->phy.otg->state == OTG_STATE_UNDEFINED) {
b_idle(isp, "init");
return;
}
@@ -548,14 +548,14 @@ static void otg_update_isp(struct isp1301 *isp)
isp->last_otg_ctrl = otg_ctrl;
otg_ctrl = otg_ctrl & OTG_XCEIV_INPUTS;
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_B_IDLE:
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_SRP_INIT:
if (!(otg_ctrl & OTG_PULLUP)) {
// if (otg_ctrl & OTG_B_HNPEN) {
if (isp->phy.otg->gadget->b_hnp_enable) {
- isp->phy.state = OTG_STATE_B_WAIT_ACON;
+ isp->phy.otg->state = OTG_STATE_B_WAIT_ACON;
pr_debug(" --> b_wait_acon\n");
}
goto pulldown;
@@ -585,7 +585,7 @@ pulldown:
if (!(isp->phy.otg->host))
otg_ctrl &= ~OTG_DRV_VBUS;
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_A_SUSPEND:
if (otg_ctrl & OTG_DRV_VBUS) {
set |= OTG1_VBUS_DRV;
@@ -596,7 +596,7 @@ pulldown:
/* FALLTHROUGH */
case OTG_STATE_A_VBUS_ERR:
- isp->phy.state = OTG_STATE_A_WAIT_VFALL;
+ isp->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
pr_debug(" --> a_wait_vfall\n");
/* FALLTHROUGH */
case OTG_STATE_A_WAIT_VFALL:
@@ -605,7 +605,7 @@ pulldown:
break;
case OTG_STATE_A_IDLE:
if (otg_ctrl & OTG_DRV_VBUS) {
- isp->phy.state = OTG_STATE_A_WAIT_VRISE;
+ isp->phy.otg->state = OTG_STATE_A_WAIT_VRISE;
pr_debug(" --> a_wait_vrise\n");
}
/* FALLTHROUGH */
@@ -625,17 +625,17 @@ pulldown:
if (otg_change & OTG_PULLUP) {
u32 l;
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_B_IDLE:
if (clr & OTG1_DP_PULLUP)
break;
- isp->phy.state = OTG_STATE_B_PERIPHERAL;
+ isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
pr_debug(" --> b_peripheral\n");
break;
case OTG_STATE_A_SUSPEND:
if (clr & OTG1_DP_PULLUP)
break;
- isp->phy.state = OTG_STATE_A_PERIPHERAL;
+ isp->phy.otg->state = OTG_STATE_A_PERIPHERAL;
pr_debug(" --> a_peripheral\n");
break;
default:
@@ -673,7 +673,7 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
* remote wakeup (SRP, normal) using their own timer
* to give "check cable and A-device" messages.
*/
- if (isp->phy.state == OTG_STATE_B_SRP_INIT)
+ if (isp->phy.otg->state == OTG_STATE_B_SRP_INIT)
b_idle(isp, "srp_timeout");
omap_writew(B_SRP_TMROUT, OTG_IRQ_SRC);
@@ -691,7 +691,7 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
omap_writel(otg_ctrl, OTG_CTRL);
/* subset of b_peripheral()... */
- isp->phy.state = OTG_STATE_B_PERIPHERAL;
+ isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
pr_debug(" --> b_peripheral\n");
omap_writew(B_HNP_FAIL, OTG_IRQ_SRC);
@@ -703,7 +703,7 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
state_name(isp), omap_readl(OTG_CTRL));
isp1301_defer_work(isp, WORK_UPDATE_OTG);
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_A_IDLE:
if (!otg->host)
break;
@@ -734,7 +734,7 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
otg_ctrl |= OTG_BUSDROP;
otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
omap_writel(otg_ctrl, OTG_CTRL);
- isp->phy.state = OTG_STATE_A_WAIT_VFALL;
+ isp->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
omap_writew(A_REQ_TMROUT, OTG_IRQ_SRC);
ret = IRQ_HANDLED;
@@ -748,7 +748,7 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
otg_ctrl |= OTG_BUSDROP;
otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK & ~OTG_XCEIV_INPUTS;
omap_writel(otg_ctrl, OTG_CTRL);
- isp->phy.state = OTG_STATE_A_VBUS_ERR;
+ isp->phy.otg->state = OTG_STATE_A_VBUS_ERR;
omap_writew(A_VBUS_ERR, OTG_IRQ_SRC);
ret = IRQ_HANDLED;
@@ -769,7 +769,7 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
/* role is peripheral */
if (otg_ctrl & OTG_DRIVER_SEL) {
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_A_IDLE:
b_idle(isp, __func__);
break;
@@ -786,18 +786,18 @@ static irqreturn_t omap_otg_irq(int irq, void *_isp)
}
if (otg->host) {
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_B_WAIT_ACON:
- isp->phy.state = OTG_STATE_B_HOST;
+ isp->phy.otg->state = OTG_STATE_B_HOST;
pr_debug(" --> b_host\n");
kick = 1;
break;
case OTG_STATE_A_WAIT_BCON:
- isp->phy.state = OTG_STATE_A_HOST;
+ isp->phy.otg->state = OTG_STATE_A_HOST;
pr_debug(" --> a_host\n");
break;
case OTG_STATE_A_PERIPHERAL:
- isp->phy.state = OTG_STATE_A_WAIT_BCON;
+ isp->phy.otg->state = OTG_STATE_A_WAIT_BCON;
pr_debug(" --> a_wait_bcon\n");
break;
default:
@@ -937,7 +937,7 @@ static void b_peripheral(struct isp1301 *isp)
/* UDC driver just set OTG_BSESSVLD */
isp1301_set_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLUP);
isp1301_clear_bits(isp, ISP1301_OTG_CONTROL_1, OTG1_DP_PULLDOWN);
- isp->phy.state = OTG_STATE_B_PERIPHERAL;
+ isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
pr_debug(" --> b_peripheral\n");
dump_regs(isp, "2periph");
#endif
@@ -947,7 +947,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
{
struct usb_otg *otg = isp->phy.otg;
u8 isp_stat, isp_bstat;
- enum usb_otg_state state = isp->phy.state;
+ enum usb_otg_state state = isp->phy.otg->state;
if (stat & INTR_BDIS_ACON)
pr_debug("OTG: BDIS_ACON, %s\n", state_name(isp));
@@ -970,7 +970,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
* when HNP is used.
*/
if (isp_stat & INTR_VBUS_VLD)
- isp->phy.state = OTG_STATE_A_HOST;
+ isp->phy.otg->state = OTG_STATE_A_HOST;
break;
case OTG_STATE_A_WAIT_VFALL:
if (!(isp_stat & INTR_SESS_VLD))
@@ -978,7 +978,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
break;
default:
if (!(isp_stat & INTR_VBUS_VLD))
- isp->phy.state = OTG_STATE_A_VBUS_ERR;
+ isp->phy.otg->state = OTG_STATE_A_VBUS_ERR;
break;
}
isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
@@ -1007,7 +1007,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
if (otg->default_a) {
switch (state) {
default:
- isp->phy.state = OTG_STATE_A_WAIT_VFALL;
+ isp->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
break;
case OTG_STATE_A_WAIT_VFALL:
state = OTG_STATE_A_IDLE;
@@ -1020,7 +1020,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
host_suspend(isp);
isp1301_clear_bits(isp, ISP1301_MODE_CONTROL_1,
MC1_BDIS_ACON_EN);
- isp->phy.state = OTG_STATE_B_IDLE;
+ isp->phy.otg->state = OTG_STATE_B_IDLE;
l = omap_readl(OTG_CTRL) & OTG_CTRL_MASK;
l &= ~OTG_CTRL_BITS;
omap_writel(l, OTG_CTRL);
@@ -1031,7 +1031,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
}
isp_bstat = isp1301_get_u8(isp, ISP1301_OTG_STATUS);
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_B_PERIPHERAL:
case OTG_STATE_B_WAIT_ACON:
case OTG_STATE_B_HOST:
@@ -1071,7 +1071,7 @@ static void isp_update_otg(struct isp1301 *isp, u8 stat)
}
}
- if (state != isp->phy.state)
+ if (state != isp->phy.otg->state)
pr_debug(" isp, %s -> %s\n",
usb_otg_state_string(state), state_name(isp));
@@ -1129,10 +1129,10 @@ isp1301_work(struct work_struct *work)
* skip A_WAIT_VRISE; hc transitions invisibly
* skip A_WAIT_BCON; same.
*/
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_A_WAIT_BCON:
case OTG_STATE_A_WAIT_VRISE:
- isp->phy.state = OTG_STATE_A_HOST;
+ isp->phy.otg->state = OTG_STATE_A_HOST;
pr_debug(" --> a_host\n");
otg_ctrl = omap_readl(OTG_CTRL);
otg_ctrl |= OTG_A_BUSREQ;
@@ -1141,7 +1141,7 @@ isp1301_work(struct work_struct *work)
omap_writel(otg_ctrl, OTG_CTRL);
break;
case OTG_STATE_B_WAIT_ACON:
- isp->phy.state = OTG_STATE_B_HOST;
+ isp->phy.otg->state = OTG_STATE_B_HOST;
pr_debug(" --> b_host (acon)\n");
break;
case OTG_STATE_B_HOST:
@@ -1275,7 +1275,7 @@ static int isp1301_otg_enable(struct isp1301 *isp)
static int
isp1301_set_host(struct usb_otg *otg, struct usb_bus *host)
{
- struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
+ struct isp1301 *isp = container_of(otg->usb_phy, struct isp1301, phy);
if (isp != the_transceiver)
return -ENODEV;
@@ -1331,7 +1331,7 @@ isp1301_set_host(struct usb_otg *otg, struct usb_bus *host)
static int
isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
{
- struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
+ struct isp1301 *isp = container_of(otg->usb_phy, struct isp1301, phy);
if (isp != the_transceiver)
return -ENODEV;
@@ -1368,7 +1368,7 @@ isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
}
power_up(isp);
- isp->phy.state = OTG_STATE_B_IDLE;
+ isp->phy.otg->state = OTG_STATE_B_IDLE;
if (machine_is_omap_h2() || machine_is_omap_h3())
isp1301_set_bits(isp, ISP1301_MODE_CONTROL_1, MC1_DAT_SE0);
@@ -1403,7 +1403,7 @@ isp1301_set_power(struct usb_phy *dev, unsigned mA)
{
if (!the_transceiver)
return -ENODEV;
- if (dev->state == OTG_STATE_B_PERIPHERAL)
+ if (dev->otg->state == OTG_STATE_B_PERIPHERAL)
enable_vbus_draw(the_transceiver, mA);
return 0;
}
@@ -1411,10 +1411,10 @@ isp1301_set_power(struct usb_phy *dev, unsigned mA)
static int
isp1301_start_srp(struct usb_otg *otg)
{
- struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
+ struct isp1301 *isp = container_of(otg->usb_phy, struct isp1301, phy);
u32 otg_ctrl;
- if (isp != the_transceiver || isp->phy.state != OTG_STATE_B_IDLE)
+ if (isp != the_transceiver || isp->phy.otg->state != OTG_STATE_B_IDLE)
return -ENODEV;
otg_ctrl = omap_readl(OTG_CTRL);
@@ -1424,7 +1424,7 @@ isp1301_start_srp(struct usb_otg *otg)
otg_ctrl |= OTG_B_BUSREQ;
otg_ctrl &= ~OTG_A_BUSREQ & OTG_CTRL_MASK;
omap_writel(otg_ctrl, OTG_CTRL);
- isp->phy.state = OTG_STATE_B_SRP_INIT;
+ isp->phy.otg->state = OTG_STATE_B_SRP_INIT;
pr_debug("otg: SRP, %s ... %06x\n", state_name(isp),
omap_readl(OTG_CTRL));
@@ -1438,7 +1438,7 @@ static int
isp1301_start_hnp(struct usb_otg *otg)
{
#ifdef CONFIG_USB_OTG
- struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
+ struct isp1301 *isp = container_of(otg->usb_phy, struct isp1301, phy);
u32 l;
if (isp != the_transceiver)
@@ -1452,9 +1452,9 @@ isp1301_start_hnp(struct usb_otg *otg)
/* We want hardware to manage most HNP protocol timings.
* So do this part as early as possible...
*/
- switch (isp->phy.state) {
+ switch (isp->phy.otg->state) {
case OTG_STATE_B_HOST:
- isp->phy.state = OTG_STATE_B_PERIPHERAL;
+ isp->phy.otg->state = OTG_STATE_B_PERIPHERAL;
/* caller will suspend next */
break;
case OTG_STATE_A_HOST:
@@ -1583,7 +1583,7 @@ isp1301_probe(struct i2c_client *i2c, const struct i2c_device_id *id)
isp->phy.label = DRIVER_NAME;
isp->phy.set_power = isp1301_set_power,
- isp->phy.otg->phy = &isp->phy;
+ isp->phy.otg->usb_phy = &isp->phy;
isp->phy.otg->set_host = isp1301_set_host,
isp->phy.otg->set_peripheral = isp1301_set_peripheral,
isp->phy.otg->start_srp = isp1301_start_srp,
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index 7843ef7dd0ff..e120d87778b2 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -708,7 +708,7 @@ static void msm_otg_start_host(struct usb_phy *phy, int on)
static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
{
- struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+ struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy);
struct usb_hcd *hcd;
/*
@@ -716,16 +716,16 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
* only peripheral configuration.
*/
if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL) {
- dev_info(otg->phy->dev, "Host mode is not supported\n");
+ dev_info(otg->usb_phy->dev, "Host mode is not supported\n");
return -ENODEV;
}
if (!host) {
- if (otg->phy->state == OTG_STATE_A_HOST) {
- pm_runtime_get_sync(otg->phy->dev);
- msm_otg_start_host(otg->phy, 0);
+ if (otg->state == OTG_STATE_A_HOST) {
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ msm_otg_start_host(otg->usb_phy, 0);
otg->host = NULL;
- otg->phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
schedule_work(&motg->sm_work);
} else {
otg->host = NULL;
@@ -738,14 +738,14 @@ static int msm_otg_set_host(struct usb_otg *otg, struct usb_bus *host)
hcd->power_budget = motg->pdata->power_budget;
otg->host = host;
- dev_dbg(otg->phy->dev, "host driver registered w/ tranceiver\n");
+ dev_dbg(otg->usb_phy->dev, "host driver registered w/ tranceiver\n");
/*
* Kick the state machine work, if peripheral is not supported
* or peripheral is already registered with us.
*/
if (motg->pdata->mode == USB_DR_MODE_HOST || otg->gadget) {
- pm_runtime_get_sync(otg->phy->dev);
+ pm_runtime_get_sync(otg->usb_phy->dev);
schedule_work(&motg->sm_work);
}
@@ -782,23 +782,23 @@ static void msm_otg_start_peripheral(struct usb_phy *phy, int on)
static int msm_otg_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
- struct msm_otg *motg = container_of(otg->phy, struct msm_otg, phy);
+ struct msm_otg *motg = container_of(otg->usb_phy, struct msm_otg, phy);
/*
* Fail peripheral registration if this board can support
* only host configuration.
*/
if (motg->pdata->mode == USB_DR_MODE_HOST) {
- dev_info(otg->phy->dev, "Peripheral mode is not supported\n");
+ dev_info(otg->usb_phy->dev, "Peripheral mode is not supported\n");
return -ENODEV;
}
if (!gadget) {
- if (otg->phy->state == OTG_STATE_B_PERIPHERAL) {
- pm_runtime_get_sync(otg->phy->dev);
- msm_otg_start_peripheral(otg->phy, 0);
+ if (otg->state == OTG_STATE_B_PERIPHERAL) {
+ pm_runtime_get_sync(otg->usb_phy->dev);
+ msm_otg_start_peripheral(otg->usb_phy, 0);
otg->gadget = NULL;
- otg->phy->state = OTG_STATE_UNDEFINED;
+ otg->state = OTG_STATE_UNDEFINED;
schedule_work(&motg->sm_work);
} else {
otg->gadget = NULL;
@@ -807,14 +807,15 @@ static int msm_otg_set_peripheral(struct usb_otg *otg,
return 0;
}
otg->gadget = gadget;
- dev_dbg(otg->phy->dev, "peripheral driver registered w/ tranceiver\n");
+ dev_dbg(otg->usb_phy->dev,
+ "peripheral driver registered w/ tranceiver\n");
/*
* Kick the state machine work, if host is not supported
* or host is already registered with us.
*/
if (motg->pdata->mode == USB_DR_MODE_PERIPHERAL || otg->host) {
- pm_runtime_get_sync(otg->phy->dev);
+ pm_runtime_get_sync(otg->usb_phy->dev);
schedule_work(&motg->sm_work);
}
@@ -1170,20 +1171,20 @@ static void msm_otg_sm_work(struct work_struct *w)
struct msm_otg *motg = container_of(w, struct msm_otg, sm_work);
struct usb_otg *otg = motg->phy.otg;
- switch (otg->phy->state) {
+ switch (otg->state) {
case OTG_STATE_UNDEFINED:
- dev_dbg(otg->phy->dev, "OTG_STATE_UNDEFINED state\n");
- msm_otg_reset(otg->phy);
+ dev_dbg(otg->usb_phy->dev, "OTG_STATE_UNDEFINED state\n");
+ msm_otg_reset(otg->usb_phy);
msm_otg_init_sm(motg);
- otg->phy->state = OTG_STATE_B_IDLE;
+ otg->state = OTG_STATE_B_IDLE;
/* FALL THROUGH */
case OTG_STATE_B_IDLE:
- dev_dbg(otg->phy->dev, "OTG_STATE_B_IDLE state\n");
+ dev_dbg(otg->usb_phy->dev, "OTG_STATE_B_IDLE state\n");
if (!test_bit(ID, &motg->inputs) && otg->host) {
/* disable BSV bit */
writel(readl(USB_OTGSC) & ~OTGSC_BSVIE, USB_OTGSC);
- msm_otg_start_host(otg->phy, 1);
- otg->phy->state = OTG_STATE_A_HOST;
+ msm_otg_start_host(otg->usb_phy, 1);
+ otg->state = OTG_STATE_A_HOST;
} else if (test_bit(B_SESS_VLD, &motg->inputs)) {
switch (motg->chg_state) {
case USB_CHG_STATE_UNDEFINED:
@@ -1198,14 +1199,16 @@ static void msm_otg_sm_work(struct work_struct *w)
case USB_CDP_CHARGER:
msm_otg_notify_charger(motg,
IDEV_CHG_MAX);
- msm_otg_start_peripheral(otg->phy, 1);
- otg->phy->state
+ msm_otg_start_peripheral(otg->usb_phy,
+ 1);
+ otg->state
= OTG_STATE_B_PERIPHERAL;
break;
case USB_SDP_CHARGER:
msm_otg_notify_charger(motg, IUNIT);
- msm_otg_start_peripheral(otg->phy, 1);
- otg->phy->state
+ msm_otg_start_peripheral(otg->usb_phy,
+ 1);
+ otg->state
= OTG_STATE_B_PERIPHERAL;
break;
default:
@@ -1222,36 +1225,36 @@ static void msm_otg_sm_work(struct work_struct *w)
* is incremented in charger detection work.
*/
if (cancel_delayed_work_sync(&motg->chg_work)) {
- pm_runtime_put_sync(otg->phy->dev);
- msm_otg_reset(otg->phy);
+ pm_runtime_put_sync(otg->usb_phy->dev);
+ msm_otg_reset(otg->usb_phy);
}
msm_otg_notify_charger(motg, 0);
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
}
- if (otg->phy->state == OTG_STATE_B_IDLE)
- pm_runtime_put_sync(otg->phy->dev);
+ if (otg->state == OTG_STATE_B_IDLE)
+ pm_runtime_put_sync(otg->usb_phy->dev);
break;
case OTG_STATE_B_PERIPHERAL:
- dev_dbg(otg->phy->dev, "OTG_STATE_B_PERIPHERAL state\n");
+ dev_dbg(otg->usb_phy->dev, "OTG_STATE_B_PERIPHERAL state\n");
if (!test_bit(B_SESS_VLD, &motg->inputs) ||
!test_bit(ID, &motg->inputs)) {
msm_otg_notify_charger(motg, 0);
- msm_otg_start_peripheral(otg->phy, 0);
+ msm_otg_start_peripheral(otg->usb_phy, 0);
motg->chg_state = USB_CHG_STATE_UNDEFINED;
motg->chg_type = USB_INVALID_CHARGER;
- otg->phy->state = OTG_STATE_B_IDLE;
- msm_otg_reset(otg->phy);
+ otg->state = OTG_STATE_B_IDLE;
+ msm_otg_reset(otg->usb_phy);
schedule_work(w);
}
break;
case OTG_STATE_A_HOST:
- dev_dbg(otg->phy->dev, "OTG_STATE_A_HOST state\n");
+ dev_dbg(otg->usb_phy->dev, "OTG_STATE_A_HOST state\n");
if (test_bit(ID, &motg->inputs)) {
- msm_otg_start_host(otg->phy, 0);
- otg->phy->state = OTG_STATE_B_IDLE;
- msm_otg_reset(otg->phy);
+ msm_otg_start_host(otg->usb_phy, 0);
+ otg->state = OTG_STATE_B_IDLE;
+ msm_otg_reset(otg->usb_phy);
schedule_work(w);
}
break;
@@ -1303,7 +1306,7 @@ static int msm_otg_mode_show(struct seq_file *s, void *unused)
struct msm_otg *motg = s->private;
struct usb_otg *otg = motg->phy.otg;
- switch (otg->phy->state) {
+ switch (otg->state) {
case OTG_STATE_A_HOST:
seq_puts(s, "host\n");
break;
@@ -1353,7 +1356,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
switch (req_mode) {
case USB_DR_MODE_UNKNOWN:
- switch (otg->phy->state) {
+ switch (otg->state) {
case OTG_STATE_A_HOST:
case OTG_STATE_B_PERIPHERAL:
set_bit(ID, &motg->inputs);
@@ -1364,7 +1367,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
}
break;
case USB_DR_MODE_PERIPHERAL:
- switch (otg->phy->state) {
+ switch (otg->state) {
case OTG_STATE_B_IDLE:
case OTG_STATE_A_HOST:
set_bit(ID, &motg->inputs);
@@ -1375,7 +1378,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
}
break;
case USB_DR_MODE_HOST:
- switch (otg->phy->state) {
+ switch (otg->state) {
case OTG_STATE_B_IDLE:
case OTG_STATE_B_PERIPHERAL:
clear_bit(ID, &motg->inputs);
@@ -1388,7 +1391,7 @@ static ssize_t msm_otg_mode_write(struct file *file, const char __user *ubuf,
goto out;
}
- pm_runtime_get_sync(otg->phy->dev);
+ pm_runtime_get_sync(otg->usb_phy->dev);
schedule_work(&motg->sm_work);
out:
return status;
@@ -1505,10 +1508,8 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
}
pdata->phy_init_seq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
- if (!pdata->phy_init_seq) {
- dev_warn(&pdev->dev, "No space for PHY init sequence\n");
+ if (!pdata->phy_init_seq)
return 0;
- }
ret = of_property_read_u32_array(node, "qcom,phy-init-sequence",
pdata->phy_init_seq, words);
@@ -1530,10 +1531,8 @@ static int msm_otg_probe(struct platform_device *pdev)
void __iomem *phy_select;
motg = devm_kzalloc(&pdev->dev, sizeof(struct msm_otg), GFP_KERNEL);
- if (!motg) {
- dev_err(&pdev->dev, "unable to allocate msm_otg\n");
+ if (!motg)
return -ENOMEM;
- }
pdata = dev_get_platdata(&pdev->dev);
if (!pdata) {
@@ -1546,10 +1545,8 @@ static int msm_otg_probe(struct platform_device *pdev)
motg->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
GFP_KERNEL);
- if (!motg->phy.otg) {
- dev_err(&pdev->dev, "unable to allocate msm_otg\n");
+ if (!motg->phy.otg)
return -ENOMEM;
- }
phy = &motg->phy;
phy->dev = &pdev->dev;
@@ -1674,7 +1671,7 @@ static int msm_otg_probe(struct platform_device *pdev)
phy->io_ops = &msm_otg_io_ops;
- phy->otg->phy = &motg->phy;
+ phy->otg->usb_phy = &motg->phy;
phy->otg->set_host = msm_otg_set_host;
phy->otg->set_peripheral = msm_otg_set_peripheral;
@@ -1775,7 +1772,7 @@ static int msm_otg_runtime_idle(struct device *dev)
* This 1 sec delay also prevents entering into LPM immediately
* after asynchronous interrupt.
*/
- if (otg->phy->state != OTG_STATE_UNDEFINED)
+ if (otg->state != OTG_STATE_UNDEFINED)
pm_schedule_suspend(dev, 1000);
return -EAGAIN;
diff --git a/drivers/usb/phy/phy-mv-usb.c b/drivers/usb/phy/phy-mv-usb.c
index 7d80c54f0ac6..b234d46cd58b 100644
--- a/drivers/usb/phy/phy-mv-usb.c
+++ b/drivers/usb/phy/phy-mv-usb.c
@@ -56,7 +56,7 @@ static char *state_string[] = {
static int mv_otg_set_vbus(struct usb_otg *otg, bool on)
{
- struct mv_otg *mvotg = container_of(otg->phy, struct mv_otg, phy);
+ struct mv_otg *mvotg = container_of(otg->usb_phy, struct mv_otg, phy);
if (mvotg->pdata->set_vbus == NULL)
return -ENODEV;
@@ -339,68 +339,68 @@ static void mv_otg_update_state(struct mv_otg *mvotg)
{
struct mv_otg_ctrl *otg_ctrl = &mvotg->otg_ctrl;
struct usb_phy *phy = &mvotg->phy;
- int old_state = phy->state;
+ int old_state = mvotg->phy.otg->state;
switch (old_state) {
case OTG_STATE_UNDEFINED:
- phy->state = OTG_STATE_B_IDLE;
+ mvotg->phy.otg->state = OTG_STATE_B_IDLE;
/* FALL THROUGH */
case OTG_STATE_B_IDLE:
if (otg_ctrl->id == 0)
- phy->state = OTG_STATE_A_IDLE;
+ mvotg->phy.otg->state = OTG_STATE_A_IDLE;
else if (otg_ctrl->b_sess_vld)
- phy->state = OTG_STATE_B_PERIPHERAL;
+ mvotg->phy.otg->state = OTG_STATE_B_PERIPHERAL;
break;
case OTG_STATE_B_PERIPHERAL:
if (!otg_ctrl->b_sess_vld || otg_ctrl->id == 0)
- phy->state = OTG_STATE_B_IDLE;
+ mvotg->phy.otg->state = OTG_STATE_B_IDLE;
break;
case OTG_STATE_A_IDLE:
if (otg_ctrl->id)
- phy->state = OTG_STATE_B_IDLE;
+ mvotg->phy.otg->state = OTG_STATE_B_IDLE;
else if (!(otg_ctrl->a_bus_drop) &&
(otg_ctrl->a_bus_req || otg_ctrl->a_srp_det))
- phy->state = OTG_STATE_A_WAIT_VRISE;
+ mvotg->phy.otg->state = OTG_STATE_A_WAIT_VRISE;
break;
case OTG_STATE_A_WAIT_VRISE:
if (otg_ctrl->a_vbus_vld)
- phy->state = OTG_STATE_A_WAIT_BCON;
+ mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
break;
case OTG_STATE_A_WAIT_BCON:
if (otg_ctrl->id || otg_ctrl->a_bus_drop
|| otg_ctrl->a_wait_bcon_timeout) {
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
- phy->state = OTG_STATE_A_WAIT_VFALL;
+ mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
otg_ctrl->a_bus_req = 0;
} else if (!otg_ctrl->a_vbus_vld) {
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
- phy->state = OTG_STATE_A_VBUS_ERR;
+ mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
} else if (otg_ctrl->b_conn) {
mv_otg_cancel_timer(mvotg, A_WAIT_BCON_TIMER);
mvotg->otg_ctrl.a_wait_bcon_timeout = 0;
- phy->state = OTG_STATE_A_HOST;
+ mvotg->phy.otg->state = OTG_STATE_A_HOST;
}
break;
case OTG_STATE_A_HOST:
if (otg_ctrl->id || !otg_ctrl->b_conn
|| otg_ctrl->a_bus_drop)
- phy->state = OTG_STATE_A_WAIT_BCON;
+ mvotg->phy.otg->state = OTG_STATE_A_WAIT_BCON;
else if (!otg_ctrl->a_vbus_vld)
- phy->state = OTG_STATE_A_VBUS_ERR;
+ mvotg->phy.otg->state = OTG_STATE_A_VBUS_ERR;
break;
case OTG_STATE_A_WAIT_VFALL:
if (otg_ctrl->id
|| (!otg_ctrl->b_conn && otg_ctrl->a_sess_vld)
|| otg_ctrl->a_bus_req)
- phy->state = OTG_STATE_A_IDLE;
+ mvotg->phy.otg->state = OTG_STATE_A_IDLE;
break;
case OTG_STATE_A_VBUS_ERR:
if (otg_ctrl->id || otg_ctrl->a_clr_err
|| otg_ctrl->a_bus_drop) {
otg_ctrl->a_clr_err = 0;
- phy->state = OTG_STATE_A_WAIT_VFALL;
+ mvotg->phy.otg->state = OTG_STATE_A_WAIT_VFALL;
}
break;
default:
@@ -420,8 +420,8 @@ static void mv_otg_work(struct work_struct *work)
run:
/* work queue is single thread, or we need spin_lock to protect */
phy = &mvotg->phy;
- otg = phy->otg;
- old_state = phy->state;
+ otg = mvotg->phy.otg;
+ old_state = otg->state;
if (!mvotg->active)
return;
@@ -429,22 +429,24 @@ run:
mv_otg_update_inputs(mvotg);
mv_otg_update_state(mvotg);
- if (old_state != phy->state) {
+ if (old_state != mvotg->phy.otg->state) {
dev_info(&mvotg->pdev->dev, "change from state %s to %s\n",
state_string[old_state],
- state_string[phy->state]);
+ state_string[mvotg->phy.otg->state]);
- switch (phy->state) {
+ switch (mvotg->phy.otg->state) {
case OTG_STATE_B_IDLE:
otg->default_a = 0;
if (old_state == OTG_STATE_B_PERIPHERAL)
mv_otg_start_periphrals(mvotg, 0);
mv_otg_reset(mvotg);
mv_otg_disable(mvotg);
+ usb_phy_set_event(&mvotg->phy, USB_EVENT_NONE);
break;
case OTG_STATE_B_PERIPHERAL:
mv_otg_enable(mvotg);
mv_otg_start_periphrals(mvotg, 1);
+ usb_phy_set_event(&mvotg->phy, USB_EVENT_ENUMERATED);
break;
case OTG_STATE_A_IDLE:
otg->default_a = 1;
@@ -545,8 +547,8 @@ set_a_bus_req(struct device *dev, struct device_attribute *attr,
return -1;
/* We will use this interface to change to A device */
- if (mvotg->phy.state != OTG_STATE_B_IDLE
- && mvotg->phy.state != OTG_STATE_A_IDLE)
+ if (mvotg->phy.otg->state != OTG_STATE_B_IDLE
+ && mvotg->phy.otg->state != OTG_STATE_A_IDLE)
return -1;
/* The clock may disabled and we need to set irq for ID detected */
@@ -686,10 +688,8 @@ static int mv_otg_probe(struct platform_device *pdev)
}
mvotg = devm_kzalloc(&pdev->dev, sizeof(*mvotg), GFP_KERNEL);
- if (!mvotg) {
- dev_err(&pdev->dev, "failed to allocate memory!\n");
+ if (!mvotg)
return -ENOMEM;
- }
otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
if (!otg)
@@ -717,9 +717,9 @@ static int mv_otg_probe(struct platform_device *pdev)
mvotg->phy.dev = &pdev->dev;
mvotg->phy.otg = otg;
mvotg->phy.label = driver_name;
- mvotg->phy.state = OTG_STATE_UNDEFINED;
- otg->phy = &mvotg->phy;
+ otg->state = OTG_STATE_UNDEFINED;
+ otg->usb_phy = &mvotg->phy;
otg->set_host = mv_otg_set_host;
otg->set_peripheral = mv_otg_set_peripheral;
otg->set_vbus = mv_otg_set_vbus;
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 0e0c41587a08..a55dadcd47f3 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -390,10 +390,8 @@ static int mxs_phy_probe(struct platform_device *pdev)
}
mxs_phy = devm_kzalloc(&pdev->dev, sizeof(*mxs_phy), GFP_KERNEL);
- if (!mxs_phy) {
- dev_err(&pdev->dev, "Failed to allocate USB PHY structure!\n");
+ if (!mxs_phy)
return -ENOMEM;
- }
/* Some SoCs don't have anatop registers */
if (of_get_property(np, "fsl,anatop", NULL)) {
diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c
index 388d89f6b141..f83808413ba2 100644
--- a/drivers/usb/phy/phy-rcar-gen2-usb.c
+++ b/drivers/usb/phy/phy-rcar-gen2-usb.c
@@ -195,10 +195,8 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(base);
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(dev, "Memory allocation failed\n");
+ if (!priv)
return -ENOMEM;
- }
spin_lock_init(&priv->lock);
priv->clk = clk;
diff --git a/drivers/usb/phy/phy-rcar-usb.c b/drivers/usb/phy/phy-rcar-usb.c
index e9f3a93e42b7..1e09b8377885 100644
--- a/drivers/usb/phy/phy-rcar-usb.c
+++ b/drivers/usb/phy/phy-rcar-usb.c
@@ -200,10 +200,8 @@ static int rcar_usb_phy_probe(struct platform_device *pdev)
return PTR_ERR(reg1);
priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
- if (!priv) {
- dev_err(dev, "priv data allocation error\n");
+ if (!priv)
return -ENOMEM;
- }
priv->reg0 = reg0;
priv->reg1 = reg1;
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
index cc61ee44b911..d391c79a542a 100644
--- a/drivers/usb/phy/phy-tahvo.c
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -81,33 +81,35 @@ static void check_vbus_state(struct tahvo_usb *tu)
reg = retu_read(rdev, TAHVO_REG_IDSR);
if (reg & TAHVO_STAT_VBUS) {
- switch (tu->phy.state) {
+ switch (tu->phy.otg->state) {
case OTG_STATE_B_IDLE:
/* Enable the gadget driver */
if (tu->phy.otg->gadget)
usb_gadget_vbus_connect(tu->phy.otg->gadget);
- tu->phy.state = OTG_STATE_B_PERIPHERAL;
+ tu->phy.otg->state = OTG_STATE_B_PERIPHERAL;
+ usb_phy_set_event(&tu->phy, USB_EVENT_ENUMERATED);
break;
case OTG_STATE_A_IDLE:
/*
* Session is now valid assuming the USB hub is driving
* Vbus.
*/
- tu->phy.state = OTG_STATE_A_HOST;
+ tu->phy.otg->state = OTG_STATE_A_HOST;
break;
default:
break;
}
dev_info(&tu->pt_dev->dev, "USB cable connected\n");
} else {
- switch (tu->phy.state) {
+ switch (tu->phy.otg->state) {
case OTG_STATE_B_PERIPHERAL:
if (tu->phy.otg->gadget)
usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
- tu->phy.state = OTG_STATE_B_IDLE;
+ tu->phy.otg->state = OTG_STATE_B_IDLE;
+ usb_phy_set_event(&tu->phy, USB_EVENT_NONE);
break;
case OTG_STATE_A_HOST:
- tu->phy.state = OTG_STATE_A_IDLE;
+ tu->phy.otg->state = OTG_STATE_A_IDLE;
break;
default:
break;
@@ -132,14 +134,14 @@ static void tahvo_usb_become_host(struct tahvo_usb *tu)
/* Power up the transceiver in USB host mode */
retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
USBR_MASTER_SW2 | USBR_MASTER_SW1);
- tu->phy.state = OTG_STATE_A_IDLE;
+ tu->phy.otg->state = OTG_STATE_A_IDLE;
check_vbus_state(tu);
}
static void tahvo_usb_stop_host(struct tahvo_usb *tu)
{
- tu->phy.state = OTG_STATE_A_IDLE;
+ tu->phy.otg->state = OTG_STATE_A_IDLE;
}
static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
@@ -151,7 +153,7 @@ static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
/* Power up transceiver and set it in USB peripheral mode */
retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
USBR_NSUSPEND | USBR_SLAVE_SW);
- tu->phy.state = OTG_STATE_B_IDLE;
+ tu->phy.otg->state = OTG_STATE_B_IDLE;
check_vbus_state(tu);
}
@@ -160,7 +162,7 @@ static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
{
if (tu->phy.otg->gadget)
usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
- tu->phy.state = OTG_STATE_B_IDLE;
+ tu->phy.otg->state = OTG_STATE_B_IDLE;
}
static void tahvo_usb_power_off(struct tahvo_usb *tu)
@@ -173,7 +175,7 @@ static void tahvo_usb_power_off(struct tahvo_usb *tu)
/* Power off transceiver */
retu_write(rdev, TAHVO_REG_USBR, 0);
- tu->phy.state = OTG_STATE_UNDEFINED;
+ tu->phy.otg->state = OTG_STATE_UNDEFINED;
}
static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
@@ -196,7 +198,8 @@ static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
{
- struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy);
+ struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
+ phy);
dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host);
@@ -225,7 +228,8 @@ static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
static int tahvo_usb_set_peripheral(struct usb_otg *otg,
struct usb_gadget *gadget)
{
- struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy);
+ struct tahvo_usb *tu = container_of(otg->usb_phy, struct tahvo_usb,
+ phy);
dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget);
@@ -379,11 +383,11 @@ static int tahvo_usb_probe(struct platform_device *pdev)
/* Create OTG interface */
tahvo_usb_power_off(tu);
tu->phy.dev = &pdev->dev;
- tu->phy.state = OTG_STATE_UNDEFINED;
+ tu->phy.otg->state = OTG_STATE_UNDEFINED;
tu->phy.label = DRIVER_NAME;
tu->phy.set_suspend = tahvo_usb_set_suspend;
- tu->phy.otg->phy = &tu->phy;
+ tu->phy.otg->usb_phy = &tu->phy;
tu->phy.otg->set_host = tahvo_usb_set_host;
tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral;
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index 886f1807a67b..fa2bfa4ee34e 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -880,11 +880,8 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy,
tegra_phy->config = devm_kzalloc(&pdev->dev, sizeof(*config),
GFP_KERNEL);
- if (!tegra_phy->config) {
- dev_err(&pdev->dev,
- "unable to allocate memory for USB UTMIP config\n");
+ if (!tegra_phy->config)
return -ENOMEM;
- }
config = tegra_phy->config;
@@ -979,10 +976,8 @@ static int tegra_usb_phy_probe(struct platform_device *pdev)
int err;
tegra_phy = devm_kzalloc(&pdev->dev, sizeof(*tegra_phy), GFP_KERNEL);
- if (!tegra_phy) {
- dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
+ if (!tegra_phy)
return -ENOMEM;
- }
match = of_match_device(tegra_usb_phy_id_table, &pdev->dev);
if (!match) {
diff --git a/drivers/usb/phy/phy-ulpi.c b/drivers/usb/phy/phy-ulpi.c
index 4e3877c329f2..f48a7a21e3c2 100644
--- a/drivers/usb/phy/phy-ulpi.c
+++ b/drivers/usb/phy/phy-ulpi.c
@@ -211,7 +211,7 @@ static int ulpi_init(struct usb_phy *phy)
static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
{
- struct usb_phy *phy = otg->phy;
+ struct usb_phy *phy = otg->usb_phy;
unsigned int flags = usb_phy_io_read(phy, ULPI_IFC_CTRL);
if (!host) {
@@ -237,7 +237,7 @@ static int ulpi_set_host(struct usb_otg *otg, struct usb_bus *host)
static int ulpi_set_vbus(struct usb_otg *otg, bool on)
{
- struct usb_phy *phy = otg->phy;
+ struct usb_phy *phy = otg->usb_phy;
unsigned int flags = usb_phy_io_read(phy, ULPI_OTG_CTRL);
flags &= ~(ULPI_OTG_CTRL_DRVVBUS | ULPI_OTG_CTRL_DRVVBUS_EXT);
@@ -276,7 +276,7 @@ otg_ulpi_create(struct usb_phy_io_ops *ops,
phy->otg = otg;
phy->init = ulpi_init;
- otg->phy = phy;
+ otg->usb_phy = phy;
otg->set_host = ulpi_set_host;
otg->set_vbus = ulpi_set_vbus;
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index 045cd309367a..b4066a001ba0 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -191,7 +191,9 @@ struct usb_phy *devm_usb_get_phy_by_phandle(struct device *dev,
phy = __of_usb_find_phy(node);
if (IS_ERR(phy) || !try_module_get(phy->dev->driver->owner)) {
- phy = ERR_PTR(-EPROBE_DEFER);
+ if (!IS_ERR(phy))
+ phy = ERR_PTR(-EPROBE_DEFER);
+
devres_free(ptr);
goto err1;
}
@@ -444,3 +446,15 @@ int usb_bind_phy(const char *dev_name, u8 index,
return 0;
}
EXPORT_SYMBOL_GPL(usb_bind_phy);
+
+/**
+ * usb_phy_set_event - set event to phy event
+ * @x: the phy returned by usb_get_phy();
+ *
+ * This sets event to phy event
+ */
+void usb_phy_set_event(struct usb_phy *x, unsigned long event)
+{
+ x->last_event = event;
+}
+EXPORT_SYMBOL_GPL(usb_phy_set_event);
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 169307ba08b7..371478704899 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -126,13 +126,15 @@ void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable)
{
u16 mask = DCFM | DRPD | DPRPU | HSE | USBE;
- u16 val = DPRPU | HSE | USBE;
+ u16 val = HSE | USBE;
/*
* if enable
*
* - select Function mode
- * - D+ Line Pull-up
+ * - D+ Line Pull-up is disabled
+ * When D+ Line Pull-up is enabled,
+ * calling usbhs_sys_function_pullup(,1)
*/
usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
}
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index a7996da6a1bd..0427cdd1a483 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -102,6 +102,10 @@ struct usbhs_priv;
#define DEVADD8 0x00E0
#define DEVADD9 0x00E2
#define DEVADDA 0x00E4
+#define D2FIFOSEL 0x00F0 /* for R-Car Gen2 */
+#define D2FIFOCTR 0x00F2 /* for R-Car Gen2 */
+#define D3FIFOSEL 0x00F4 /* for R-Car Gen2 */
+#define D3FIFOCTR 0x00F6 /* for R-Car Gen2 */
/* SYSCFG */
#define SCKE (1 << 10) /* USB Module Clock Enable */
@@ -269,7 +273,8 @@ struct usbhs_priv {
*/
struct usbhs_fifo_info fifo_info;
- struct usb_phy *phy;
+ struct usb_phy *usb_phy;
+ struct phy *phy;
};
/*
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index b0c97a3f1bfe..f46271ce1b15 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -21,8 +21,6 @@
#include "pipe.h"
#define usbhsf_get_cfifo(p) (&((p)->fifo_info.cfifo))
-#define usbhsf_get_d0fifo(p) (&((p)->fifo_info.d0fifo))
-#define usbhsf_get_d1fifo(p) (&((p)->fifo_info.d1fifo))
#define usbhsf_is_cfifo(p, f) (usbhsf_get_cfifo(p) == f)
#define usbhsf_fifo_is_busy(f) ((f)->pipe) /* see usbhs_pipe_select_fifo */
@@ -577,14 +575,6 @@ static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
usbhs_pipe_number(pipe),
pkt->length, pkt->actual, *is_done, pkt->zero);
- /*
- * Transmission end
- */
- if (*is_done) {
- if (usbhs_pipe_is_dcp(pipe))
- usbhs_dcp_control_transfer_done(pipe);
- }
-
usbhsf_fifo_unselect(pipe, fifo);
return 0;
@@ -722,14 +712,6 @@ usbhs_fifo_read_end:
usbhs_pipe_number(pipe),
pkt->length, pkt->actual, *is_done, pkt->zero);
- /*
- * Transmission end
- */
- if (*is_done) {
- if (usbhs_pipe_is_dcp(pipe))
- usbhs_dcp_control_transfer_done(pipe);
- }
-
usbhs_fifo_read_busy:
usbhsf_fifo_unselect(pipe, fifo);
@@ -777,18 +759,13 @@ static struct usbhs_fifo *usbhsf_get_dma_fifo(struct usbhs_priv *priv,
struct usbhs_pkt *pkt)
{
struct usbhs_fifo *fifo;
+ int i;
- /* DMA :: D0FIFO */
- fifo = usbhsf_get_d0fifo(priv);
- if (usbhsf_dma_chan_get(fifo, pkt) &&
- !usbhsf_fifo_is_busy(fifo))
- return fifo;
-
- /* DMA :: D1FIFO */
- fifo = usbhsf_get_d1fifo(priv);
- if (usbhsf_dma_chan_get(fifo, pkt) &&
- !usbhsf_fifo_is_busy(fifo))
- return fifo;
+ usbhs_for_each_dfifo(priv, fifo, i) {
+ if (usbhsf_dma_chan_get(fifo, pkt) &&
+ !usbhsf_fifo_is_busy(fifo))
+ return fifo;
+ }
return NULL;
}
@@ -1176,6 +1153,24 @@ static void usbhsf_dma_complete(void *arg)
usbhs_pipe_number(pipe), ret);
}
+void usbhs_fifo_clear_dcp(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+ struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+
+ /* clear DCP FIFO of transmission */
+ if (usbhsf_fifo_select(pipe, fifo, 1) < 0)
+ return;
+ usbhsf_fifo_clear(pipe, fifo);
+ usbhsf_fifo_unselect(pipe, fifo);
+
+ /* clear DCP FIFO of reception */
+ if (usbhsf_fifo_select(pipe, fifo, 0) < 0)
+ return;
+ usbhsf_fifo_clear(pipe, fifo);
+ usbhsf_fifo_unselect(pipe, fifo);
+}
+
/*
* fifo init
*/
@@ -1183,8 +1178,8 @@ void usbhs_fifo_init(struct usbhs_priv *priv)
{
struct usbhs_mod *mod = usbhs_mod_get_current(priv);
struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv);
- struct usbhs_fifo *d0fifo = usbhsf_get_d0fifo(priv);
- struct usbhs_fifo *d1fifo = usbhsf_get_d1fifo(priv);
+ struct usbhs_fifo *dfifo;
+ int i;
mod->irq_empty = usbhsf_irq_empty;
mod->irq_ready = usbhsf_irq_ready;
@@ -1192,8 +1187,8 @@ void usbhs_fifo_init(struct usbhs_priv *priv)
mod->irq_brdysts = 0;
cfifo->pipe = NULL;
- d0fifo->pipe = NULL;
- d1fifo->pipe = NULL;
+ usbhs_for_each_dfifo(priv, dfifo, i)
+ dfifo->pipe = NULL;
}
void usbhs_fifo_quit(struct usbhs_priv *priv)
@@ -1206,6 +1201,25 @@ void usbhs_fifo_quit(struct usbhs_priv *priv)
mod->irq_brdysts = 0;
}
+#define __USBHS_DFIFO_INIT(priv, fifo, channel, fifo_port) \
+do { \
+ fifo = usbhsf_get_dnfifo(priv, channel); \
+ fifo->name = "D"#channel"FIFO"; \
+ fifo->port = fifo_port; \
+ fifo->sel = D##channel##FIFOSEL; \
+ fifo->ctr = D##channel##FIFOCTR; \
+ fifo->tx_slave.shdma_slave.slave_id = \
+ usbhs_get_dparam(priv, d##channel##_tx_id); \
+ fifo->rx_slave.shdma_slave.slave_id = \
+ usbhs_get_dparam(priv, d##channel##_rx_id); \
+ usbhsf_dma_init(priv, fifo); \
+} while (0)
+
+#define USBHS_DFIFO_INIT(priv, fifo, channel) \
+ __USBHS_DFIFO_INIT(priv, fifo, channel, D##channel##FIFO)
+#define USBHS_DFIFO_INIT_NO_PORT(priv, fifo, channel) \
+ __USBHS_DFIFO_INIT(priv, fifo, channel, 0)
+
int usbhs_fifo_probe(struct usbhs_priv *priv)
{
struct usbhs_fifo *fifo;
@@ -1217,31 +1231,20 @@ int usbhs_fifo_probe(struct usbhs_priv *priv)
fifo->sel = CFIFOSEL;
fifo->ctr = CFIFOCTR;
- /* D0FIFO */
- fifo = usbhsf_get_d0fifo(priv);
- fifo->name = "D0FIFO";
- fifo->port = D0FIFO;
- fifo->sel = D0FIFOSEL;
- fifo->ctr = D0FIFOCTR;
- fifo->tx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d0_tx_id);
- fifo->rx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d0_rx_id);
- usbhsf_dma_init(priv, fifo);
-
- /* D1FIFO */
- fifo = usbhsf_get_d1fifo(priv);
- fifo->name = "D1FIFO";
- fifo->port = D1FIFO;
- fifo->sel = D1FIFOSEL;
- fifo->ctr = D1FIFOCTR;
- fifo->tx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d1_tx_id);
- fifo->rx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d1_rx_id);
- usbhsf_dma_init(priv, fifo);
+ /* DFIFO */
+ USBHS_DFIFO_INIT(priv, fifo, 0);
+ USBHS_DFIFO_INIT(priv, fifo, 1);
+ USBHS_DFIFO_INIT_NO_PORT(priv, fifo, 2);
+ USBHS_DFIFO_INIT_NO_PORT(priv, fifo, 3);
return 0;
}
void usbhs_fifo_remove(struct usbhs_priv *priv)
{
- usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv));
- usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv));
+ struct usbhs_fifo *fifo;
+ int i;
+
+ usbhs_for_each_dfifo(priv, fifo, i)
+ usbhsf_dma_quit(priv, fifo);
}
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h
index a168a1760fce..f07037c1185f 100644
--- a/drivers/usb/renesas_usbhs/fifo.h
+++ b/drivers/usb/renesas_usbhs/fifo.h
@@ -38,11 +38,16 @@ struct usbhs_fifo {
struct sh_dmae_slave rx_slave;
};
+#define USBHS_MAX_NUM_DFIFO 4
struct usbhs_fifo_info {
struct usbhs_fifo cfifo;
- struct usbhs_fifo d0fifo;
- struct usbhs_fifo d1fifo;
+ struct usbhs_fifo dfifo[USBHS_MAX_NUM_DFIFO];
};
+#define usbhsf_get_dnfifo(p, n) (&((p)->fifo_info.dfifo[n]))
+#define usbhs_for_each_dfifo(priv, dfifo, i) \
+ for ((i) = 0, dfifo = usbhsf_get_dnfifo(priv, (i)); \
+ ((i) < USBHS_MAX_NUM_DFIFO); \
+ (i)++, dfifo = usbhsf_get_dnfifo(priv, (i)))
struct usbhs_pkt_handle;
struct usbhs_pkt {
@@ -74,6 +79,7 @@ int usbhs_fifo_probe(struct usbhs_priv *priv);
void usbhs_fifo_remove(struct usbhs_priv *priv);
void usbhs_fifo_init(struct usbhs_priv *priv);
void usbhs_fifo_quit(struct usbhs_priv *priv);
+void usbhs_fifo_clear_dcp(struct usbhs_pipe *pipe);
/*
* packet info
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 2d17c10a0428..8697e6efcabf 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -56,6 +56,7 @@ struct usbhsg_gpriv {
#define USBHSG_STATUS_REGISTERD (1 << 1)
#define USBHSG_STATUS_WEDGE (1 << 2)
#define USBHSG_STATUS_SELF_POWERED (1 << 3)
+#define USBHSG_STATUS_SOFT_CONNECT (1 << 4)
};
struct usbhsg_recip_handle {
@@ -484,6 +485,9 @@ static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
case NODATA_STATUS_STAGE:
pipe->handler = &usbhs_ctrl_stage_end_handler;
break;
+ case READ_STATUS_STAGE:
+ case WRITE_STATUS_STAGE:
+ usbhs_dcp_control_transfer_done(pipe);
default:
return ret;
}
@@ -602,6 +606,9 @@ static int usbhsg_ep_disable(struct usb_ep *ep)
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ if (!pipe)
+ return -EINVAL;
+
usbhsg_pipe_disable(uep);
usbhs_pipe_free(pipe);
@@ -723,6 +730,25 @@ static struct usb_ep_ops usbhsg_ep_ops = {
};
/*
+ * pullup control
+ */
+static int usbhsg_can_pullup(struct usbhs_priv *priv)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+ return gpriv->driver &&
+ usbhsg_status_has(gpriv, USBHSG_STATUS_SOFT_CONNECT);
+}
+
+static void usbhsg_update_pullup(struct usbhs_priv *priv)
+{
+ if (usbhsg_can_pullup(priv))
+ usbhs_sys_function_pullup(priv, 1);
+ else
+ usbhs_sys_function_pullup(priv, 0);
+}
+
+/*
* usb module start/end
*/
static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
@@ -756,9 +782,9 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
/*
* pipe initialize and enable DCP
*/
+ usbhs_fifo_init(priv);
usbhs_pipe_init(priv,
usbhsg_dma_map_ctrl);
- usbhs_fifo_init(priv);
/* dcp init instead of usbhsg_ep_enable() */
dcp->pipe = usbhs_dcp_malloc(priv);
@@ -772,6 +798,7 @@ static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
* - usb module
*/
usbhs_sys_function_ctrl(priv, 1);
+ usbhsg_update_pullup(priv);
/*
* enable irq callback
@@ -851,8 +878,7 @@ static int usbhsg_gadget_start(struct usb_gadget *gadget,
return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD);
}
-static int usbhsg_gadget_stop(struct usb_gadget *gadget,
- struct usb_gadget_driver *driver)
+static int usbhsg_gadget_stop(struct usb_gadget *gadget)
{
struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
@@ -878,8 +904,15 @@ static int usbhsg_pullup(struct usb_gadget *gadget, int is_on)
{
struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+ unsigned long flags;
- usbhs_sys_function_pullup(priv, is_on);
+ usbhs_lock(priv, flags);
+ if (is_on)
+ usbhsg_status_set(gpriv, USBHSG_STATUS_SOFT_CONNECT);
+ else
+ usbhsg_status_clr(gpriv, USBHSG_STATUS_SOFT_CONNECT);
+ usbhsg_update_pullup(priv);
+ usbhs_unlock(priv, flags);
return 0;
}
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
index 10e1ded9c9cc..f0d323125871 100644
--- a/drivers/usb/renesas_usbhs/mod_host.c
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -1474,9 +1474,9 @@ static int usbhsh_start(struct usbhs_priv *priv)
/*
* pipe initialize and enable DCP
*/
+ usbhs_fifo_init(priv);
usbhs_pipe_init(priv,
usbhsh_dma_map_ctrl);
- usbhs_fifo_init(priv);
usbhsh_pipe_init_for_host(priv);
/*
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 040bcefcb040..007f45abe96c 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -618,8 +618,12 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
void usbhs_pipe_clear(struct usbhs_pipe *pipe)
{
- usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
- usbhsp_pipectrl_set(pipe, ACLRM, 0);
+ if (usbhs_pipe_is_dcp(pipe)) {
+ usbhs_fifo_clear_dcp(pipe);
+ } else {
+ usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
+ usbhsp_pipectrl_set(pipe, ACLRM, 0);
+ }
}
static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c
index e6b9dcc1c289..8fc15c0ba339 100644
--- a/drivers/usb/renesas_usbhs/rcar2.c
+++ b/drivers/usb/renesas_usbhs/rcar2.c
@@ -12,6 +12,7 @@
#include <linux/gpio.h>
#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
#include <linux/platform_data/gpio-rcar.h>
#include <linux/usb/phy.h>
#include "common.h"
@@ -20,25 +21,43 @@
static int usbhs_rcar2_hardware_init(struct platform_device *pdev)
{
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
- struct usb_phy *phy;
- phy = usb_get_phy_dev(&pdev->dev, 0);
- if (IS_ERR(phy))
- return PTR_ERR(phy);
+ if (IS_ENABLED(CONFIG_GENERIC_PHY)) {
+ struct phy *phy = phy_get(&pdev->dev, "usb");
- priv->phy = phy;
- return 0;
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ priv->phy = phy;
+ return 0;
+ }
+
+ if (IS_ENABLED(CONFIG_USB_PHY)) {
+ struct usb_phy *usb_phy = usb_get_phy_dev(&pdev->dev, 0);
+
+ if (IS_ERR(usb_phy))
+ return PTR_ERR(usb_phy);
+
+ priv->usb_phy = usb_phy;
+ return 0;
+ }
+
+ return -ENXIO;
}
static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
{
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
- if (!priv->phy)
- return 0;
+ if (priv->phy) {
+ phy_put(priv->phy);
+ priv->phy = NULL;
+ }
- usb_put_phy(priv->phy);
- priv->phy = NULL;
+ if (priv->usb_phy) {
+ usb_put_phy(priv->usb_phy);
+ priv->usb_phy = NULL;
+ }
return 0;
}
@@ -47,21 +66,35 @@ static int usbhs_rcar2_power_ctrl(struct platform_device *pdev,
void __iomem *base, int enable)
{
struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+ int retval = -ENODEV;
+
+ if (priv->phy) {
+ if (enable) {
+ retval = phy_init(priv->phy);
- if (!priv->phy)
- return -ENODEV;
+ if (!retval)
+ retval = phy_power_on(priv->phy);
+ } else {
+ phy_power_off(priv->phy);
+ phy_exit(priv->phy);
+ retval = 0;
+ }
+ }
- if (enable) {
- int retval = usb_phy_init(priv->phy);
+ if (priv->usb_phy) {
+ if (enable) {
+ retval = usb_phy_init(priv->usb_phy);
- if (!retval)
- retval = usb_phy_set_suspend(priv->phy, 0);
- return retval;
+ if (!retval)
+ retval = usb_phy_set_suspend(priv->usb_phy, 0);
+ } else {
+ usb_phy_set_suspend(priv->usb_phy, 1);
+ usb_phy_shutdown(priv->usb_phy);
+ retval = 0;
+ }
}
- usb_phy_set_suspend(priv->phy, 1);
- usb_phy_shutdown(priv->phy);
- return 0;
+ return retval;
}
static int usbhs_rcar2_get_id(struct platform_device *pdev)
diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h
index 1fa99a301817..5decad77d8ed 100644
--- a/include/linux/pci_ids.h
+++ b/include/linux/pci_ids.h
@@ -562,6 +562,7 @@
#define PCI_DEVICE_ID_AMD_8131_BRIDGE 0x7450
#define PCI_DEVICE_ID_AMD_8131_APIC 0x7451
#define PCI_DEVICE_ID_AMD_8132_BRIDGE 0x7458
+#define PCI_DEVICE_ID_AMD_NL_USB 0x7912
#define PCI_DEVICE_ID_AMD_CS5535_IDE 0x208F
#define PCI_DEVICE_ID_AMD_CS5536_ISA 0x2090
#define PCI_DEVICE_ID_AMD_CS5536_FLASH 0x2091
diff --git a/include/linux/platform_data/dwc3-exynos.h b/include/linux/platform_data/dwc3-exynos.h
deleted file mode 100644
index 5eb7da9b3772..000000000000
--- a/include/linux/platform_data/dwc3-exynos.h
+++ /dev/null
@@ -1,24 +0,0 @@
-/**
- * dwc3-exynos.h - Samsung EXYNOS DWC3 Specific Glue layer, header.
- *
- * Copyright (c) 2012 Samsung Electronics Co., Ltd.
- * http://www.samsung.com
- *
- * Author: Anton Tikhomirov <av.tikhomirov@samsung.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#ifndef _DWC3_EXYNOS_H_
-#define _DWC3_EXYNOS_H_
-
-struct dwc3_exynos_data {
- int phy_type;
- int (*phy_init)(struct platform_device *pdev, int type);
- int (*phy_exit)(struct platform_device *pdev, int type);
-};
-
-#endif /* _DWC3_EXYNOS_H_ */
diff --git a/include/linux/usb/chipidea.h b/include/linux/usb/chipidea.h
index e14c09a45c5a..c01bf4ea27b9 100644
--- a/include/linux/usb/chipidea.h
+++ b/include/linux/usb/chipidea.h
@@ -13,7 +13,9 @@ struct ci_hdrc_platform_data {
/* offset of the capability registers */
uintptr_t capoffset;
unsigned power_budget;
- struct usb_phy *phy;
+ struct phy *phy;
+ /* old usb_phy interface */
+ struct usb_phy *usb_phy;
enum usb_phy_interface phy_mode;
unsigned long flags;
#define CI_HDRC_REGS_SHARED BIT(0)
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index c330f5ef42cf..3d87defcc527 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -427,6 +427,8 @@ static inline struct usb_composite_driver *to_cdriver(
* @b_vendor_code: bMS_VendorCode part of the OS string
* @use_os_string: false by default, interested gadgets set it
* @os_desc_config: the configuration to be used with OS descriptors
+ * @setup_pending: true when setup request is queued but not completed
+ * @os_desc_pending: true when os_desc request is queued but not completed
*
* One of these devices is allocated and initialized before the
* associated device driver's bind() is called.
@@ -488,6 +490,9 @@ struct usb_composite_dev {
/* protects deactivations and delayed_status counts*/
spinlock_t lock;
+
+ unsigned setup_pending:1;
+ unsigned os_desc_pending:1;
};
extern int usb_string_id(struct usb_composite_dev *c);
@@ -501,6 +506,8 @@ extern int usb_string_ids_n(struct usb_composite_dev *c, unsigned n);
extern void composite_disconnect(struct usb_gadget *gadget);
extern int composite_setup(struct usb_gadget *gadget,
const struct usb_ctrlrequest *ctrl);
+extern void composite_suspend(struct usb_gadget *gadget);
+extern void composite_resume(struct usb_gadget *gadget);
/*
* Some systems will need runtime overrides for the product identifiers
diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h
index 522cafe26790..70ddb3943b62 100644
--- a/include/linux/usb/gadget.h
+++ b/include/linux/usb/gadget.h
@@ -490,8 +490,7 @@ struct usb_gadget_ops {
void (*get_config_params)(struct usb_dcd_config_params *);
int (*udc_start)(struct usb_gadget *,
struct usb_gadget_driver *);
- int (*udc_stop)(struct usb_gadget *,
- struct usb_gadget_driver *);
+ int (*udc_stop)(struct usb_gadget *);
};
/**
@@ -925,7 +924,7 @@ extern int usb_add_gadget_udc_release(struct device *parent,
struct usb_gadget *gadget, void (*release)(struct device *dev));
extern int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget);
extern void usb_del_gadget_udc(struct usb_gadget *gadget);
-extern int udc_attach_driver(const char *name,
+extern int usb_udc_attach_driver(const char *name,
struct usb_gadget_driver *driver);
/*-------------------------------------------------------------------------*/
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 154332b7c8c0..52661c5da690 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -9,15 +9,20 @@
#ifndef __LINUX_USB_OTG_H
#define __LINUX_USB_OTG_H
+#include <linux/phy/phy.h>
#include <linux/usb/phy.h>
struct usb_otg {
u8 default_a;
- struct usb_phy *phy;
+ struct phy *phy;
+ /* old usb_phy interface */
+ struct usb_phy *usb_phy;
struct usb_bus *host;
struct usb_gadget *gadget;
+ enum usb_otg_state state;
+
/* bind/unbind the host controller */
int (*set_host)(struct usb_otg *otg, struct usb_bus *host);
diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h
index 353053a33f21..f499c23e6342 100644
--- a/include/linux/usb/phy.h
+++ b/include/linux/usb/phy.h
@@ -77,7 +77,6 @@ struct usb_phy {
unsigned int flags;
enum usb_phy_type type;
- enum usb_otg_state state;
enum usb_phy_events last_event;
struct usb_otg *otg;
@@ -210,6 +209,7 @@ extern void usb_put_phy(struct usb_phy *);
extern void devm_usb_put_phy(struct device *dev, struct usb_phy *x);
extern int usb_bind_phy(const char *dev_name, u8 index,
const char *phy_dev_name);
+extern void usb_phy_set_event(struct usb_phy *x, unsigned long event);
#else
static inline struct usb_phy *usb_get_phy(enum usb_phy_type type)
{
@@ -251,6 +251,10 @@ static inline int usb_bind_phy(const char *dev_name, u8 index,
{
return -EOPNOTSUPP;
}
+
+static inline void usb_phy_set_event(struct usb_phy *x, unsigned long event)
+{
+}
#endif
static inline int
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
index d5952bb66752..9fd9e481ea98 100644
--- a/include/linux/usb/renesas_usbhs.h
+++ b/include/linux/usb/renesas_usbhs.h
@@ -145,6 +145,10 @@ struct renesas_usbhs_driver_param {
int d0_rx_id;
int d1_tx_id;
int d1_rx_id;
+ int d2_tx_id;
+ int d2_rx_id;
+ int d3_tx_id;
+ int d3_rx_id;
/*
* option: