Merge tag 'usb-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 2 Apr 2014 00:06:09 +0000 (17:06 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 2 Apr 2014 00:06:09 +0000 (17:06 -0700)
Pull USB patches from Greg KH:
 "Here's the big USB pull request for 3.15-rc1.

  The normal set of patches, lots of controller driver updates, and a
  smattering of individual USB driver updates as well.

  All have been in linux-next for a while"

* tag 'usb-3.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (249 commits)
  xhci: Transition maintainership to Mathias Nyman.
  USB: disable reset-resume when USB_QUIRK_RESET is set
  USB: unbind all interfaces before rebinding any
  usb: phy: Add ulpi IDs for SMSC USB3320 and TI TUSB1210
  usb: gadget: tcm_usb_gadget: stop format strings
  usb: gadget: f_fs: add missing spinlock and mutex unlock
  usb: gadget: composite: switch over to ERR_CAST()
  usb: gadget: inode: switch over to memdup_user()
  usb: gadget: f_subset: switch over to PTR_RET
  usb: gadget: lpc32xx_udc: fix wrong clk_put() sequence
  USB: keyspan: remove dead debugging code
  USB: serial: add missing newlines to dev_<level> messages.
  USB: serial: add missing braces
  USB: serial: continue to write on errors
  USB: serial: continue to read on errors
  USB: serial: make bulk_out_size a lower limit
  USB: cypress_m8: fix potential scheduling while atomic
  devicetree: bindings: document lsi,zevio-usb
  usb: chipidea: add support for USB OTG controller on LSI Zevio SoCs
  usb: chipidea: imx: Use dev_name() for ci_hdrc name to distinguish USBs
  ...

160 files changed:
Documentation/devicetree/bindings/phy/apm-xgene-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/samsung-phy.txt
Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/ti-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/ci-hdrc-imx.txt
Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/dwc3.txt
Documentation/devicetree/bindings/usb/mxs-phy.txt
Documentation/devicetree/bindings/usb/omap-usb.txt
Documentation/devicetree/bindings/usb/platform-uhci.txt [deleted file]
Documentation/devicetree/bindings/usb/usb-ehci.txt
Documentation/devicetree/bindings/usb/usb-ohci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/usb-phy.txt [deleted file]
Documentation/devicetree/bindings/usb/usb-uhci.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/usb-xhci.txt
Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt [deleted file]
Documentation/devicetree/bindings/usb/vt8500-ehci.txt [deleted file]
Documentation/phy/samsung-usb2.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/Kconfig
arch/arm/mach-exynos/Kconfig
arch/arm/mach-omap2/Kconfig
arch/arm/mach-shmobile/Kconfig
arch/arm/mach-tegra/Kconfig
arch/mips/Kconfig
arch/mips/ath79/Kconfig
arch/mips/ralink/Kconfig
arch/powerpc/platforms/44x/Kconfig
arch/powerpc/platforms/ps3/Kconfig
arch/sh/Kconfig
drivers/hid/hid-core.c
drivers/hid/hid-ids.h
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/phy-bcm-kona-usb2.c
drivers/phy/phy-core.c
drivers/phy/phy-exynos4210-usb2.c [new file with mode: 0644]
drivers/phy/phy-exynos4x12-usb2.c [new file with mode: 0644]
drivers/phy/phy-exynos5250-sata.c [new file with mode: 0644]
drivers/phy/phy-exynos5250-usb2.c [new file with mode: 0644]
drivers/phy/phy-omap-control.c [new file with mode: 0644]
drivers/phy/phy-omap-usb2.c
drivers/phy/phy-samsung-usb2.c [new file with mode: 0644]
drivers/phy/phy-samsung-usb2.h [new file with mode: 0644]
drivers/phy/phy-sun4i-usb.c [new file with mode: 0644]
drivers/phy/phy-ti-pipe3.c [new file with mode: 0644]
drivers/phy/phy-twl4030-usb.c
drivers/phy/phy-xgene.c [new file with mode: 0644]
drivers/usb/Kconfig
drivers/usb/chipidea/Makefile
drivers/usb/chipidea/bits.h
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/ci_hdrc_zevio.c [new file with mode: 0644]
drivers/usb/chipidea/core.c
drivers/usb/chipidea/udc.c
drivers/usb/core/config.c
drivers/usb/core/devio.c
drivers/usb/core/driver.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/usb.h
drivers/usb/dwc2/core_intr.c
drivers/usb/dwc2/hcd_intr.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/dwc3-omap.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h
drivers/usb/gadget/Kconfig
drivers/usb/gadget/at91_udc.c
drivers/usb/gadget/atmel_usba_udc.c
drivers/usb/gadget/atmel_usba_udc.h
drivers/usb/gadget/composite.c
drivers/usb/gadget/f_fs.c
drivers/usb/gadget/f_subset.c
drivers/usb/gadget/gr_udc.c
drivers/usb/gadget/inode.c
drivers/usb/gadget/lpc32xx_udc.c
drivers/usb/gadget/printer.c
drivers/usb/gadget/s3c-hsotg.c
drivers/usb/gadget/s3c-hsudc.c
drivers/usb/gadget/tcm_usb_gadget.c
drivers/usb/gadget/u_ether.c
drivers/usb/gadget/u_fs.h
drivers/usb/host/Kconfig
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci-tegra.c
drivers/usb/host/hwa-hc.c
drivers/usb/host/ohci-platform.c
drivers/usb/host/uhci-platform.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/Kconfig
drivers/usb/misc/sisusbvga/sisusb.c
drivers/usb/misc/usbled.c
drivers/usb/musb/Kconfig
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_cppi41.c
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/omap2430.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
drivers/usb/phy/phy-fsm-usb.c
drivers/usb/phy/phy-msm-usb.c
drivers/usb/phy/phy-mxs-usb.c
drivers/usb/phy/phy-omap-control.c [deleted file]
drivers/usb/phy/phy-omap-usb3.c [deleted file]
drivers/usb/phy/phy-rcar-gen2-usb.c
drivers/usb/phy/phy-twl6030-usb.c
drivers/usb/phy/phy-ulpi.c
drivers/usb/serial/ch341.c
drivers/usb/serial/cyberjack.c
drivers/usb/serial/cypress_m8.c
drivers/usb/serial/generic.c
drivers/usb/serial/iuu_phoenix.c
drivers/usb/serial/keyspan.c
drivers/usb/serial/keyspan_pda.c
drivers/usb/serial/kl5kusb105.c
drivers/usb/serial/kobil_sct.c
drivers/usb/serial/mos7720.c
drivers/usb/serial/mos7840.c
drivers/usb/serial/quatech2.c
drivers/usb/serial/spcp8x5.c
drivers/usb/serial/symbolserial.c
drivers/usb/serial/ti_usb_3410_5052.c
drivers/usb/serial/usb-serial.c
drivers/usb/storage/Kconfig
drivers/usb/storage/uas-detect.h [new file with mode: 0644]
drivers/usb/storage/uas.c
drivers/usb/storage/unusual_devs.h
drivers/usb/storage/unusual_uas.h [new file with mode: 0644]
drivers/usb/storage/usb.c
drivers/usb/storage/usb.h
drivers/usb/wusbcore/devconnect.c
drivers/usb/wusbcore/wa-hc.c
drivers/usb/wusbcore/wa-hc.h
drivers/usb/wusbcore/wa-rpipe.c
drivers/usb/wusbcore/wa-xfer.c
include/linux/phy/omap_control_phy.h [new file with mode: 0644]
include/linux/phy/omap_usb.h [new file with mode: 0644]
include/linux/phy/phy.h
include/linux/usb.h
include/linux/usb/chipidea.h
include/linux/usb/hcd.h
include/linux/usb/omap_control_usb.h [deleted file]
include/linux/usb/omap_usb.h [deleted file]
include/linux/usb/phy.h
include/linux/usb/serial.h
include/linux/usb/uas.h
include/linux/usb_usual.h
include/uapi/linux/usb/functionfs.h
include/uapi/linux/usbdevice_fs.h

diff --git a/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt b/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt
new file mode 100644 (file)
index 0000000..5f3a65a
--- /dev/null
@@ -0,0 +1,79 @@
+* APM X-Gene 15Gbps Multi-purpose PHY nodes
+
+PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each
+PHY (pair of lanes) has its own node.
+
+Required properties:
+- compatible           : Shall be "apm,xgene-phy".
+- reg                  : PHY memory resource is the SDS PHY access resource.
+- #phy-cells           : Shall be 1 as it expects one argument for setting
+                         the mode of the PHY. Possible values are 0 (SATA),
+                         1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI).
+
+Optional properties:
+- status               : Shall be "ok" if enabled or "disabled" if disabled.
+                         Default is "ok".
+- clocks               : Reference to the clock entry.
+- apm,tx-eye-tuning    : Manual control to fine tune the capture of the serial
+                         bit lines from the automatic calibrated position.
+                         Two set of 3-tuple setting for each (up to 3)
+                         supported link speed on the host. Range from 0 to
+                         127 in unit of one bit period. Default is 10.
+- apm,tx-eye-direction : Eye tuning manual control direction. 0 means sample
+                         data earlier than the nominal sampling point. 1 means
+                         sample data later than the nominal sampling point.
+                         Two set of 3-tuple setting for each (up to 3)
+                         supported link speed on the host. Default is 0.
+- apm,tx-boost-gain    : Frequency boost AC (LSB 3-bit) and DC (2-bit)
+                         gain control. Two set of 3-tuple setting for each
+                         (up to 3) supported link speed on the host. Range is
+                         between 0 to 31 in unit of dB. Default is 3.
+- apm,tx-amplitude     : Amplitude control. Two set of 3-tuple setting for
+                         each (up to 3) supported link speed on the host.
+                         Range is between 0 to 199500 in unit of uV.
+                         Default is 199500 uV.
+- apm,tx-pre-cursor1   : 1st pre-cursor emphasis taps control. Two set of
+                         3-tuple setting for each (up to 3) supported link
+                         speed on the host. Range is 0 to 273000 in unit of
+                         uV. Default is 0.
+- apm,tx-pre-cursor2   : 2st pre-cursor emphasis taps control. Two set of
+                         3-tuple setting for each (up to 3) supported link
+                         speed on the host. Range is 0 to 127400 in unit uV.
+                         Default is 0x0.
+- apm,tx-post-cursor   : Post-cursor emphasis taps control. Two set of
+                         3-tuple setting for Gen1, Gen2, and Gen3. Range is
+                         between 0 to 0x1f in unit of 18.2mV. Default is 0xf.
+- apm,tx-speed         : Tx operating speed. One set of 3-tuple for each
+                         supported link speed on the host.
+                          0 = 1-2Gbps
+                          1 = 2-4Gbps (1st tuple default)
+                          2 = 4-8Gbps
+                          3 = 8-15Gbps (2nd tuple default)
+                          4 = 2.5-4Gbps
+                          5 = 4-5Gbps
+                          6 = 5-6Gbps
+                          7 = 6-16Gbps (3rd tuple default)
+
+NOTE: PHY override parameters are board specific setting.
+
+Example:
+               phy1: phy@1f21a000 {
+                       compatible = "apm,xgene-phy";
+                       reg = <0x0 0x1f21a000 0x0 0x100>;
+                       #phy-cells = <1>;
+                       status = "disabled";
+               };
+
+               phy2: phy@1f22a000 {
+                       compatible = "apm,xgene-phy";
+                       reg = <0x0 0x1f22a000 0x0 0x100>;
+                       #phy-cells = <1>;
+                       status = "ok";
+               };
+
+               phy3: phy@1f23a000 {
+                       compatible = "apm,xgene-phy";
+                       reg = <0x0 0x1f23a000 0x0 0x100>;
+                       #phy-cells = <1>;
+                       status = "ok";
+               };
index c0fccaa1671ec8da7e403d0a8478860da3e586ba..28f9edb8f19c6f9966610fdac0589407245f3ea7 100644 (file)
@@ -20,3 +20,57 @@ Required properties:
 - compatible : should be "samsung,exynos5250-dp-video-phy";
 - reg : offset and length of the Display Port PHY register set;
 - #phy-cells : from the generic PHY bindings, must be 0;
+
+Samsung S5P/EXYNOS SoC series USB PHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be one of the listed compatibles:
+       - "samsung,exynos4210-usb2-phy"
+       - "samsung,exynos4x12-usb2-phy"
+       - "samsung,exynos5250-usb2-phy"
+- reg : a list of registers used by phy driver
+       - first and obligatory is the location of phy modules registers
+- samsung,sysreg-phandle - handle to syscon used to control the system registers
+- samsung,pmureg-phandle - handle to syscon used to control PMU registers
+- #phy-cells : from the generic phy bindings, must be 1;
+- clocks and clock-names:
+       - the "phy" clock is required by the phy module, used as a gate
+       - the "ref" clock is used to get the rate of the clock provided to the
+         PHY module
+
+The first phandle argument in the PHY specifier identifies the PHY, its
+meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
+and Exynos 4212) it is as follows:
+  0 - USB device ("device"),
+  1 - USB host ("host"),
+  2 - HSIC0 ("hsic0"),
+  3 - HSIC1 ("hsic1"),
+
+Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
+register is supplied.
+
+Example:
+
+For Exynos 4412 (compatible with Exynos 4212):
+
+usbphy: phy@125b0000 {
+       compatible = "samsung,exynos4x12-usb2-phy";
+       reg = <0x125b0000 0x100>;
+       clocks = <&clock 305>, <&clock 2>;
+       clock-names = "phy", "ref";
+       status = "okay";
+       #phy-cells = <1>;
+       samsung,sysreg-phandle = <&sys_reg>;
+       samsung,pmureg-phandle = <&pmu_reg>;
+};
+
+Then the PHY can be used in other nodes such as:
+
+phy-consumer@12340000 {
+       phys = <&usbphy 2>;
+       phy-names = "phy";
+};
+
+Refer to DT bindings documentation of particular PHY consumer devices for more
+information about required PHYs and the way of specification.
diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
new file mode 100644 (file)
index 0000000..a82361b
--- /dev/null
@@ -0,0 +1,26 @@
+Allwinner sun4i USB PHY
+-----------------------
+
+Required properties:
+- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
+  "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
+- reg : a list of offset + length pairs
+- reg-names : "phy_ctrl", "pmu1" and for sun4i or sun7i "pmu2"
+- #phy-cells : from the generic phy bindings, must be 1
+- clocks : phandle + clock specifier for the phy clock
+- clock-names : "usb_phy"
+- resets : a list of phandle + reset specifier pairs
+- reset-names : "usb0_reset", "usb1_reset" and for sun4i or sun7i "usb2_reset"
+
+Example:
+       usbphy: phy@0x01c13400 {
+               #phy-cells = <1>;
+               compatible = "allwinner,sun4i-a10-usb-phy";
+               /* phy base regs, phy1 pmu reg, phy2 pmu reg */
+               reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+               reg-names = "phy_ctrl", "pmu1", "pmu2";
+               clocks = <&usb_clk 8>;
+               clock-names = "usb_phy";
+               resets = <&usb_clk 1>, <&usb_clk 2>;
+               reset-names = "usb1_reset", "usb2_reset";
+       };
diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt
new file mode 100644 (file)
index 0000000..788fb0f
--- /dev/null
@@ -0,0 +1,86 @@
+TI PHY: DT DOCUMENTATION FOR PHYs in TI PLATFORMs
+
+OMAP CONTROL PHY
+
+Required properties:
+ - compatible: Should be one of
+ "ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
+ "ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
+                        e.g. USB2_PHY on OMAP5.
+ "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
+                        e.g. USB3 PHY and SATA PHY on OMAP5.
+ "ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
+                        DRA7 platform.
+ "ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
+                        AM437 platform.
+ - reg : Address and length of the register set for the device. It contains
+   the address of "otghs_control" for control-phy-otghs or "power" register
+   for other types.
+ - reg-names: should be "otghs_control" control-phy-otghs and "power" for
+   other types.
+
+omap_control_usb: omap-control-usb@4a002300 {
+        compatible = "ti,control-phy-otghs";
+        reg = <0x4a00233c 0x4>;
+        reg-names = "otghs_control";
+};
+
+OMAP USB2 PHY
+
+Required properties:
+ - compatible: Should be "ti,omap-usb2"
+ - reg : Address and length of the register set for the device.
+ - #phy-cells: determine the number of cells that should be given in the
+   phandle while referencing this phy.
+
+Optional properties:
+ - ctrl-module : phandle of the control module used by PHY driver to power on
+   the PHY.
+
+This is usually a subnode of ocp2scp to which it is connected.
+
+usb2phy@4a0ad080 {
+       compatible = "ti,omap-usb2";
+       reg = <0x4a0ad080 0x58>;
+       ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
+};
+
+TI PIPE3 PHY
+
+Required properties:
+ - compatible: Should be "ti,phy-usb3" or "ti,phy-pipe3-sata".
+   "ti,omap-usb3" is deprecated.
+ - reg : Address and length of the register set for the device.
+ - reg-names: The names of the register addresses corresponding to the registers
+   filled in "reg".
+ - #phy-cells: determine the number of cells that should be given in the
+   phandle while referencing this phy.
+ - clocks: a list of phandles and clock-specifier pairs, one for each entry in
+   clock-names.
+ - clock-names: should include:
+   * "wkupclk" - wakeup clock.
+   * "sysclk" - system clock.
+   * "refclk" - reference clock.
+
+Optional properties:
+ - ctrl-module : phandle of the control module used by PHY driver to power on
+   the PHY.
+
+This is usually a subnode of ocp2scp to which it is connected.
+
+usb3phy@4a084400 {
+       compatible = "ti,phy-usb3";
+       reg = <0x4a084400 0x80>,
+             <0x4a084800 0x64>,
+             <0x4a084c00 0x40>;
+       reg-names = "phy_rx", "phy_tx", "pll_ctrl";
+       ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
+       clocks = <&usb_phy_cm_clk32k>,
+                <&sys_clkin>,
+                <&usb_otg_ss_refclk960m>;
+       clock-names =   "wkupclk",
+                       "sysclk",
+                       "refclk";
+};
index b4b5b7906c88b0d2e06111068c9e3279e14b07a8..a6a32cb7f7772469688a62258b677dd10c85a92f 100644 (file)
@@ -18,6 +18,7 @@ Optional properties:
 - vbus-supply: regulator for vbus
 - disable-over-current: disable over current detect
 - external-vbus-divider: enables off-chip resistor divider for Vbus
+- maximum-speed: limit the maximum connection speed to "full-speed".
 
 Examples:
 usb@02184000 { /* USB OTG */
@@ -28,4 +29,5 @@ usb@02184000 { /* USB OTG */
        fsl,usbmisc = <&usbmisc 0>;
        disable-over-current;
        external-vbus-divider;
+       maximum-speed = "full-speed";
 };
diff --git a/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt b/Documentation/devicetree/bindings/usb/ci-hdrc-zevio.txt
new file mode 100644 (file)
index 0000000..abbcb2a
--- /dev/null
@@ -0,0 +1,17 @@
+* LSI Zevio USB OTG Controller
+
+Required properties:
+- compatible: Should be "lsi,zevio-usb"
+- reg: Should contain registers location and length
+- interrupts: Should contain controller interrupt
+
+Optional properties:
+- vbus-supply: regulator for vbus
+
+Examples:
+               usb0: usb@b0000000 {
+                       reg = <0xb0000000 0x1000>;
+                       compatible = "lsi,zevio-usb";
+                       interrupts = <8>;
+                       vbus-supply = <&vbus_reg>;
+               };
index e807635f9e1c72acec52de0f296804b14e5d458c..471366d6a1291846852e24cb0b03fec0e5fa8b23 100644 (file)
@@ -6,11 +6,13 @@ Required properties:
  - compatible: must be "snps,dwc3"
  - reg : Address and length of the register set for the device
  - interrupts: Interrupts used by the dwc3 controller.
+
+Optional properties:
  - usb-phy : array of phandle for the PHY device.  The first element
    in the array is expected to be a handle to the USB2/HS PHY and
    the second element is expected to be a handle to the USB3/SS PHY
-
-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.
 
 This is usually a subnode to DWC3 glue to which it is connected.
index 5835b27146eaa2160f8a8627e3ac8dc6925a0616..cef181a9d8bd6506699cd98889fd36f8f6bbc306 100644 (file)
@@ -1,13 +1,19 @@
 * Freescale MXS USB Phy Device
 
 Required properties:
-- compatible: Should be "fsl,imx23-usbphy"
+- compatible: should contain:
+       * "fsl,imx23-usbphy" for imx23 and imx28
+       * "fsl,imx6q-usbphy" for imx6dq and imx6dl
+       * "fsl,imx6sl-usbphy" for imx6sl
+  "fsl,imx23-usbphy" is still a fallback for other strings
 - reg: Should contain registers location and length
 - interrupts: Should contain phy interrupt
+- fsl,anatop: phandle for anatop register, it is only for imx6 SoC series
 
 Example:
 usbphy1: usbphy@020c9000 {
        compatible = "fsl,imx6q-usbphy", "fsl,imx23-usbphy";
        reg = <0x020c9000 0x1000>;
        interrupts = <0 44 0x04>;
+       fsl,anatop = <&anatop>;
 };
index c495135115cb7accc23d997877382c36223cd768..38b2faec4199d9da6ac352f0578be53d76ffcefa 100644 (file)
@@ -76,27 +76,3 @@ omap_dwc3 {
        ranges;
 };
 
-OMAP CONTROL USB
-
-Required properties:
- - compatible: Should be one of
- "ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
- "ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
-                       e.g. USB2_PHY on OMAP5.
- "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
-                       e.g. USB3 PHY and SATA PHY on OMAP5.
- "ti,control-phy-dra7usb2" - if it has power down register like USB2 PHY on
-                       DRA7 platform.
- "ti,control-phy-am437usb2" - if it has power down register like USB2 PHY on
-                       AM437 platform.
- - reg : Address and length of the register set for the device. It contains
-   the address of "otghs_control" for control-phy-otghs or "power" register
-   for other types.
- - reg-names: should be "otghs_control" control-phy-otghs and "power" for
-   other types.
-
-omap_control_usb: omap-control-usb@4a002300 {
-       compatible = "ti,control-phy-otghs";
-       reg = <0x4a00233c 0x4>;
-       reg-names = "otghs_control";
-};
diff --git a/Documentation/devicetree/bindings/usb/platform-uhci.txt b/Documentation/devicetree/bindings/usb/platform-uhci.txt
deleted file mode 100644 (file)
index a4fb071..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-Generic Platform UHCI Controller
------------------------------------------------------
-
-Required properties:
-- compatible : "platform-uhci"
-- reg : Should contain 1 register ranges(address and length)
-- interrupts : UHCI controller interrupt
-
-Example:
-
-       uhci@d8007b00 {
-               compatible = "platform-uhci";
-               reg = <0xd8007b00 0x200>;
-               interrupts = <43>;
-       };
index fa18612f757b8edc6170cd1381dc5d2fc36cceab..ff151ec084c402c75ce0d634dbd9be622cba19bd 100644 (file)
@@ -1,19 +1,20 @@
 USB EHCI controllers
 
 Required properties:
-  - compatible : should be "usb-ehci".
+  - compatible : should be "generic-ehci".
   - reg : should contain at least address and length of the standard EHCI
     register set for the device. Optional platform-dependent registers
     (debug-port or other) can be also specified here, but only after
     definition of standard EHCI registers.
   - interrupts : one EHCI interrupt should be described here.
-If device registers are implemented in big endian mode, the device
-node should have "big-endian-regs" property.
-If controller implementation operates with big endian descriptors,
-"big-endian-desc" property should be specified.
-If both big endian registers and descriptors are used by the controller
-implementation, "big-endian" property can be specified instead of having
-both "big-endian-regs" and "big-endian-desc".
+
+Optional properties:
+ - big-endian-regs : boolean, set this for hcds with big-endian registers
+ - big-endian-desc : boolean, set this for hcds with big-endian descriptors
+ - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
+ - clocks : a list of phandle + clock specifier pairs
+ - phys : phandle + phy specifier pair
+ - phy-names : "usb"
 
 Example (Sequoia 440EPx):
     ehci@e0000300 {
@@ -23,3 +24,13 @@ Example (Sequoia 440EPx):
           reg = <0 e0000300 90 0 e0000390 70>;
           big-endian;
    };
+
+Example (Allwinner sun4i A10 SoC):
+   ehci0: usb@01c14000 {
+          compatible = "allwinner,sun4i-a10-ehci", "generic-ehci";
+          reg = <0x01c14000 0x100>;
+          interrupts = <39>;
+          clocks = <&ahb_gates 1>;
+          phys = <&usbphy 1>;
+          phy-names = "usb";
+   };
diff --git a/Documentation/devicetree/bindings/usb/usb-ohci.txt b/Documentation/devicetree/bindings/usb/usb-ohci.txt
new file mode 100644 (file)
index 0000000..45f67d9
--- /dev/null
@@ -0,0 +1,25 @@
+USB OHCI controllers
+
+Required properties:
+- compatible : "generic-ohci"
+- reg : ohci controller register range (address and length)
+- interrupts : ohci controller interrupt
+
+Optional properties:
+- big-endian-regs : boolean, set this for hcds with big-endian registers
+- big-endian-desc : boolean, set this for hcds with big-endian descriptors
+- big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
+- clocks : a list of phandle + clock specifier pairs
+- phys : phandle + phy specifier pair
+- phy-names : "usb"
+
+Example:
+
+       ohci0: usb@01c14400 {
+               compatible = "allwinner,sun4i-a10-ohci", "generic-ohci";
+               reg = <0x01c14400 0x100>;
+               interrupts = <64>;
+               clocks = <&usb_clk 6>, <&ahb_gates 2>;
+               phys = <&usbphy 1>;
+               phy-names = "usb";
+       };
diff --git a/Documentation/devicetree/bindings/usb/usb-phy.txt b/Documentation/devicetree/bindings/usb/usb-phy.txt
deleted file mode 100644 (file)
index c0245c8..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-USB PHY
-
-OMAP USB2 PHY
-
-Required properties:
- - compatible: Should be "ti,omap-usb2"
- - reg : Address and length of the register set for the device.
- - #phy-cells: determine the number of cells that should be given in the
-   phandle while referencing this phy.
-
-Optional properties:
- - ctrl-module : phandle of the control module used by PHY driver to power on
-   the PHY.
-
-This is usually a subnode of ocp2scp to which it is connected.
-
-usb2phy@4a0ad080 {
-       compatible = "ti,omap-usb2";
-       reg = <0x4a0ad080 0x58>;
-       ctrl-module = <&omap_control_usb>;
-       #phy-cells = <0>;
-};
-
-OMAP USB3 PHY
-
-Required properties:
- - compatible: Should be "ti,omap-usb3"
- - reg : Address and length of the register set for the device.
- - reg-names: The names of the register addresses corresponding to the registers
-   filled in "reg".
- - #phy-cells: determine the number of cells that should be given in the
-   phandle while referencing this phy.
-
-Optional properties:
- - ctrl-module : phandle of the control module used by PHY driver to power on
-   the PHY.
-
-This is usually a subnode of ocp2scp to which it is connected.
-
-usb3phy@4a084400 {
-       compatible = "ti,omap-usb3";
-       reg = <0x4a084400 0x80>,
-             <0x4a084800 0x64>,
-             <0x4a084c00 0x40>;
-       reg-names = "phy_rx", "phy_tx", "pll_ctrl";
-       ctrl-module = <&omap_control_usb>;
-       #phy-cells = <0>;
-};
diff --git a/Documentation/devicetree/bindings/usb/usb-uhci.txt b/Documentation/devicetree/bindings/usb/usb-uhci.txt
new file mode 100644 (file)
index 0000000..2981334
--- /dev/null
@@ -0,0 +1,15 @@
+Generic Platform UHCI Controller
+-----------------------------------------------------
+
+Required properties:
+- compatible : "generic-uhci" (deprecated: "platform-uhci")
+- reg : Should contain 1 register ranges(address and length)
+- interrupts : UHCI controller interrupt
+
+Example:
+
+       uhci@d8007b00 {
+               compatible = "generic-uhci";
+               reg = <0xd8007b00 0x200>;
+               interrupts = <43>;
+       };
index 5752df0e17a2e27790ef6ff7841f5d103fb5bc20..90f8f607d125b5c158012e7b24aa1ae58d564260 100644 (file)
@@ -1,14 +1,14 @@
 USB xHCI controllers
 
 Required properties:
-  - compatible: should be "xhci-platform".
+  - compatible: should be "generic-xhci" (deprecated: "xhci-platform").
   - reg: should contain address and length of the standard XHCI
     register set for the device.
   - interrupts: one XHCI interrupt should be described here.
 
 Example:
        usb@f0931000 {
-               compatible = "xhci-platform";
+               compatible = "generic-xhci";
                reg = <0xf0931000 0x8c8>;
                interrupts = <0x0 0x4e 0x0>;
        };
diff --git a/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/via,vt8500-ehci.txt
deleted file mode 100644 (file)
index 17b3ad1..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-VIA/Wondermedia VT8500 EHCI Controller
------------------------------------------------------
-
-Required properties:
-- compatible : "via,vt8500-ehci"
-- reg : Should contain 1 register ranges(address and length)
-- interrupts : ehci controller interrupt
-
-Example:
-
-       ehci@d8007900 {
-               compatible = "via,vt8500-ehci";
-               reg = <0xd8007900 0x200>;
-               interrupts = <43>;
-       };
diff --git a/Documentation/devicetree/bindings/usb/vt8500-ehci.txt b/Documentation/devicetree/bindings/usb/vt8500-ehci.txt
deleted file mode 100644 (file)
index 5fb8fd6..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-VIA VT8500 and Wondermedia WM8xxx SoC USB controllers.
-
-Required properties:
- - compatible: Should be "via,vt8500-ehci" or "wm,prizm-ehci".
- - reg: Address range of the ehci registers. size should be 0x200
- - interrupts: Should contain the ehci interrupt.
-
-usb: ehci@D8007100 {
-       compatible = "wm,prizm-ehci", "usb-ehci";
-       reg = <0xD8007100 0x200>;
-       interrupts = <1>;
-};
diff --git a/Documentation/phy/samsung-usb2.txt b/Documentation/phy/samsung-usb2.txt
new file mode 100644 (file)
index 0000000..ed12d43
--- /dev/null
@@ -0,0 +1,135 @@
+.------------------------------------------------------------------------------+
+|                      Samsung USB 2.0 PHY adaptation layer                   |
++-----------------------------------------------------------------------------+'
+
+| 1. Description
++----------------
+
+The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
+among many SoCs. In spite of the similarities it proved difficult to
+create a one driver that would fit all these PHY controllers. Often
+the differences were minor and were found in particular bits of the
+registers of the PHY. In some rare cases the order of register writes or
+the PHY powering up process had to be altered. This adaptation layer is
+a compromise between having separate drivers and having a single driver
+with added support for many special cases.
+
+| 2. Files description
++----------------------
+
+- phy-samsung-usb2.c
+   This is the main file of the adaptation layer. This file contains
+   the probe function and provides two callbacks to the Generic PHY
+   Framework. This two callbacks are used to power on and power off the
+   phy. They carry out the common work that has to be done on all version
+   of the PHY module. Depending on which SoC was chosen they execute SoC
+   specific callbacks. The specific SoC version is selected by choosing
+   the appropriate compatible string. In addition, this file contains
+   struct of_device_id definitions for particular SoCs.
+
+- phy-samsung-usb2.h
+   This is the include file. It declares the structures used by this
+   driver. In addition it should contain extern declarations for
+   structures that describe particular SoCs.
+
+| 3. Supporting SoCs
++--------------------
+
+To support a new SoC a new file should be added to the drivers/phy
+directory. Each SoC's configuration is stored in an instance of the
+struct samsung_usb2_phy_config.
+
+struct samsung_usb2_phy_config {
+       const struct samsung_usb2_common_phy *phys;
+       int (*rate_to_clk)(unsigned long, u32 *);
+       unsigned int num_phys;
+       bool has_mode_switch;
+};
+
+The num_phys is the number of phys handled by the driver. *phys is an
+array that contains the configuration for each phy. The has_mode_switch
+property is a boolean flag that determines whether the SoC has USB host
+and device on a single pair of pins. If so, a special register has to
+be modified to change the internal routing of these pins between a USB
+device or host module.
+
+For example the configuration for Exynos 4210 is following:
+
+const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
+       .has_mode_switch        = 0,
+       .num_phys               = EXYNOS4210_NUM_PHYS,
+       .phys                   = exynos4210_phys,
+       .rate_to_clk            = exynos4210_rate_to_clk,
+}
+
+- int (*rate_to_clk)(unsigned long, u32 *)
+       The rate_to_clk callback is to convert the rate of the clock
+       used as the reference clock for the PHY module to the value
+       that should be written in the hardware register.
+
+The exynos4210_phys configuration array is as follows:
+
+static const struct samsung_usb2_common_phy exynos4210_phys[] = {
+       {
+               .label          = "device",
+               .id             = EXYNOS4210_DEVICE,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "host",
+               .id             = EXYNOS4210_HOST,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "hsic0",
+               .id             = EXYNOS4210_HSIC0,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "hsic1",
+               .id             = EXYNOS4210_HSIC1,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {},
+};
+
+- int (*power_on)(struct samsung_usb2_phy_instance *);
+- int (*power_off)(struct samsung_usb2_phy_instance *);
+       These two callbacks are used to power on and power off the phy
+       by modifying appropriate registers.
+
+Final change to the driver is adding appropriate compatible value to the
+phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
+added to the struct of_device_id samsung_usb2_phy_of_match[] array:
+
+#ifdef CONFIG_PHY_EXYNOS4210_USB2
+       {
+               .compatible = "samsung,exynos4210-usb2-phy",
+               .data = &exynos4210_usb2_phy_config,
+       },
+#endif
+
+To add further flexibility to the driver the Kconfig file enables to
+include support for selected SoCs in the compiled driver. The Kconfig
+entry for Exynos 4210 is following:
+
+config PHY_EXYNOS4210_USB2
+       bool "Support for Exynos 4210"
+       depends on PHY_SAMSUNG_USB2
+       depends on CPU_EXYNOS4210
+       help
+         Enable USB PHY support for Exynos 4210. This option requires that
+         Samsung USB 2.0 PHY driver is enabled and means that support for this
+         particular SoC is compiled in the driver. In case of Exynos 4210 four
+         phys are available - device, host, HSCI0 and HSCI1.
+
+The newly created file that supports the new SoC has to be also added to the
+Makefile. In case of Exynos 4210 the added line is following:
+
+obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o
+
+After completing these steps the support for the new SoC should be ready.
index 48a88402c25dd11e3b93a5baa9ee1df505dafe93..7fe5977e80db572b2125519c8b1845bd9486a4a5 100644 (file)
@@ -9130,8 +9130,7 @@ S:        Maintained
 F:     drivers/net/wireless/ath/ar5523/
 
 USB ATTACHED SCSI
-M:     Matthew Wilcox <willy@linux.intel.com>
-M:     Sarah Sharp <sarah.a.sharp@linux.intel.com>
+M:     Hans de Goede <hdegoede@redhat.com>
 M:     Gerd Hoffmann <kraxel@redhat.com>
 L:     linux-usb@vger.kernel.org
 L:     linux-scsi@vger.kernel.org
@@ -9357,7 +9356,7 @@ S:        Maintained
 F:     drivers/net/wireless/rndis_wlan.c
 
 USB XHCI DRIVER
-M:     Sarah Sharp <sarah.a.sharp@linux.intel.com>
+M:     Mathias Nyman <mathias.nyman@intel.com>
 L:     linux-usb@vger.kernel.org
 S:     Supported
 F:     drivers/usb/host/xhci*
index 15949459611f194376dafcdebaf569f8ef141dc5..b76ae4185a3b15577450481e5619dc76118264b5 100644 (file)
@@ -534,7 +534,6 @@ config ARCH_DOVE
        select PINCTRL
        select PINCTRL_DOVE
        select PLAT_ORION_LEGACY
-       select USB_ARCH_HAS_EHCI
        help
          Support for the Marvell Dove SoC 88AP510
 
@@ -633,7 +632,6 @@ config ARCH_LPC32XX
        select GENERIC_CLOCKEVENTS
        select HAVE_IDE
        select HAVE_PWM
-       select USB_ARCH_HAS_OHCI
        select USE_OF
        help
          Support for the NXP LPC32XX family of processors
@@ -770,7 +768,6 @@ config ARCH_S3C64XX
        select SAMSUNG_ATAGS
        select SAMSUNG_WAKEMASK
        select SAMSUNG_WDT_RESET
-       select USB_ARCH_HAS_OHCI
        help
          Samsung S3C64XX series based systems
 
index 4c414af75ef0edf9e09f8f3a3ba91509de2e9ef8..8d197dcdd2c06a9fc6af1ed84d4d966c6847a46c 100644 (file)
@@ -36,7 +36,6 @@ config ARCH_EXYNOS5
        select HAVE_ARM_SCU if SMP
        select HAVE_SMP
        select PINCTRL
-       select USB_ARCH_HAS_XHCI
        help
          Samsung EXYNOS5 (Cortex-A15) SoC based systems
 
index 0af7ca02314d99aa72baefb69959ccfba3380758..6fb91fb604a04961ecc91613f6bb449c2b62d708 100644 (file)
@@ -21,7 +21,6 @@ config ARCH_OMAP3
        select PM_OPP if PM
        select PM_RUNTIME if CPU_IDLE
        select SOC_HAS_OMAP2_SDRC
-       select USB_ARCH_HAS_EHCI if USB_SUPPORT
 
 config ARCH_OMAP4
        bool "TI OMAP4"
@@ -42,7 +41,6 @@ config ARCH_OMAP4
        select PL310_ERRATA_727915
        select PM_OPP if PM
        select PM_RUNTIME if CPU_IDLE
-       select USB_ARCH_HAS_EHCI if USB_SUPPORT
        select ARM_ERRATA_754322
        select ARM_ERRATA_775420
 
index f6db7dcae3f4dd044bc8ffea87cbd7779dd4602b..3b8c87461d6717ca0fc514424de3e443ac3f1990 100644 (file)
@@ -114,8 +114,6 @@ config ARCH_R8A7778
        select CPU_V7
        select SH_CLK_CPG
        select ARM_GIC
-       select USB_ARCH_HAS_EHCI
-       select USB_ARCH_HAS_OHCI
        select SYS_SUPPORTS_SH_TMU
 
 config ARCH_R8A7779
@@ -124,8 +122,6 @@ config ARCH_R8A7779
        select ARM_GIC
        select CPU_V7
        select SH_CLK_CPG
-       select USB_ARCH_HAS_EHCI
-       select USB_ARCH_HAS_OHCI
        select RENESAS_INTC_IRQPIN
        select SYS_SUPPORTS_SH_TMU
 
index b1232d8be6f568d208d92888f1519788f764902d..4926bd11f190fc259c82f0311760baaee3502bc8 100644 (file)
@@ -19,7 +19,6 @@ config ARCH_TEGRA
        select RESET_CONTROLLER
        select SOC_BUS
        select SPARSE_IRQ
-       select USB_ARCH_HAS_EHCI if USB_SUPPORT
        select USB_ULPI if USB_PHY
        select USB_ULPI_VIEWPORT if USB_PHY
        select USE_OF
index 95fa1f1d5c8b4a70d3ee991e7a5bb32b5db22740..cb1da372346c5da1c57fb445b0da597665cd5879 100644 (file)
@@ -67,8 +67,6 @@ config MIPS_ALCHEMY
        select SYS_SUPPORTS_APM_EMULATION
        select ARCH_REQUIRE_GPIOLIB
        select SYS_SUPPORTS_ZBOOT
-       select USB_ARCH_HAS_OHCI
-       select USB_ARCH_HAS_EHCI
 
 config AR7
        bool "Texas Instruments AR7"
@@ -360,7 +358,6 @@ config MIPS_SEAD3
        select SYS_SUPPORTS_LITTLE_ENDIAN
        select SYS_SUPPORTS_SMARTMIPS
        select SYS_SUPPORTS_MICROMIPS
-       select USB_ARCH_HAS_EHCI
        select USB_EHCI_BIG_ENDIAN_DESC
        select USB_EHCI_BIG_ENDIAN_MMIO
        select USE_OF
@@ -718,8 +715,6 @@ config CAVIUM_OCTEON_SOC
        select SWAP_IO_SPACE
        select HW_HAS_PCI
        select ZONE_DMA32
-       select USB_ARCH_HAS_OHCI
-       select USB_ARCH_HAS_EHCI
        select HOLES_IN_ZONE
        select ARCH_REQUIRE_GPIOLIB
        help
@@ -756,8 +751,6 @@ config NLM_XLR_BOARD
        select ZONE_DMA32 if 64BIT
        select SYNC_R4K
        select SYS_HAS_EARLY_PRINTK
-       select USB_ARCH_HAS_OHCI if USB_SUPPORT
-       select USB_ARCH_HAS_EHCI if USB_SUPPORT
        select SYS_SUPPORTS_ZBOOT
        select SYS_SUPPORTS_ZBOOT_UART16550
        help
index 3995e31a73e22a829da8796119031fe9c145fd9c..dfc60209dc63ca231a687175a9e9545c1cd9833a 100644 (file)
@@ -74,34 +74,26 @@ config ATH79_MACH_UBNT_XM
 endmenu
 
 config SOC_AR71XX
-       select USB_ARCH_HAS_EHCI
-       select USB_ARCH_HAS_OHCI
        select HW_HAS_PCI
        def_bool n
 
 config SOC_AR724X
-       select USB_ARCH_HAS_EHCI
-       select USB_ARCH_HAS_OHCI
        select HW_HAS_PCI
        select PCI_AR724X if PCI
        def_bool n
 
 config SOC_AR913X
-       select USB_ARCH_HAS_EHCI
        def_bool n
 
 config SOC_AR933X
-       select USB_ARCH_HAS_EHCI
        def_bool n
 
 config SOC_AR934X
-       select USB_ARCH_HAS_EHCI
        select HW_HAS_PCI
        select PCI_AR724X if PCI
        def_bool n
 
 config SOC_QCA955X
-       select USB_ARCH_HAS_EHCI
        select HW_HAS_PCI
        select PCI_AR724X if PCI
        def_bool n
index 1bfd1c17b3c22377231466f4402707ab0515b6af..4a296655f4469391b17449fe3642c1d142308cc0 100644 (file)
@@ -20,19 +20,13 @@ choice
        config SOC_RT305X
                bool "RT305x"
                select USB_ARCH_HAS_HCD
-               select USB_ARCH_HAS_OHCI
-               select USB_ARCH_HAS_EHCI
 
        config SOC_RT3883
                bool "RT3883"
-               select USB_ARCH_HAS_OHCI
-               select USB_ARCH_HAS_EHCI
                select HW_HAS_PCI
 
        config SOC_MT7620
                bool "MT7620"
-               select USB_ARCH_HAS_OHCI
-               select USB_ARCH_HAS_EHCI
 
 endchoice
 
index d6c7506ec7d9d2f84ad91d92e012229db29f2880..dc1a264ec6e6da888eb45955c8360e2f816a3a90 100644 (file)
@@ -265,7 +265,6 @@ config 440EP
        select PPC_FPU
        select IBM440EP_ERR42
        select IBM_EMAC_ZMII
-       select USB_ARCH_HAS_OHCI
 
 config 440EPX
        bool
index e87c19473973506f39a128252d21c7a162c8ec60..56f274064d6cfd192fb3a6f20b06068c68c5eb36 100644 (file)
@@ -2,10 +2,8 @@ config PPC_PS3
        bool "Sony PS3"
        depends on PPC64 && PPC_BOOK3S
        select PPC_CELL
-       select USB_ARCH_HAS_OHCI
        select USB_OHCI_LITTLE_ENDIAN
        select USB_OHCI_BIG_ENDIAN_MMIO
-       select USB_ARCH_HAS_EHCI
        select USB_EHCI_BIG_ENDIAN_MMIO
        select PPC_PCI_CHOICE
        help
index 364d204298fae1af77191ac5f496b6dc5cdb30d1..1399383315a36bdcaf06a79b0ef85a60cb8d33d7 100644 (file)
@@ -347,7 +347,6 @@ config CPU_SUBTYPE_SH7720
        select CPU_HAS_DSP
        select SYS_SUPPORTS_SH_CMT
        select ARCH_WANT_OPTIONAL_GPIOLIB
-       select USB_ARCH_HAS_OHCI
        select USB_OHCI_SH if USB_OHCI_HCD
        select PINCTRL
        help
@@ -358,7 +357,6 @@ config CPU_SUBTYPE_SH7721
        select CPU_SH3
        select CPU_HAS_DSP
        select SYS_SUPPORTS_SH_CMT
-       select USB_ARCH_HAS_OHCI
        select USB_OHCI_SH if USB_OHCI_HCD
        help
          Select SH7721 if you have a SH3-DSP SH7721 CPU.
@@ -436,8 +434,6 @@ config CPU_SUBTYPE_SH7734
        select CPU_SH4A
        select CPU_SHX2
        select ARCH_WANT_OPTIONAL_GPIOLIB
-       select USB_ARCH_HAS_OHCI
-       select USB_ARCH_HAS_EHCI
        select PINCTRL
        help
          Select SH7734 if you have a SH4A SH7734 CPU.
@@ -447,8 +443,6 @@ config CPU_SUBTYPE_SH7757
        select CPU_SH4A
        select CPU_SHX2
        select ARCH_WANT_OPTIONAL_GPIOLIB
-       select USB_ARCH_HAS_OHCI
-       select USB_ARCH_HAS_EHCI
        select PINCTRL
        help
          Select SH7757 if you have a SH4A SH7757 CPU.
@@ -456,7 +450,6 @@ config CPU_SUBTYPE_SH7757
 config CPU_SUBTYPE_SH7763
        bool "Support SH7763 processor"
        select CPU_SH4A
-       select USB_ARCH_HAS_OHCI
        select USB_OHCI_SH if USB_OHCI_HCD
        help
          Select SH7763 if you have a SH4A SH7763(R5S77631) CPU.
@@ -485,9 +478,7 @@ config CPU_SUBTYPE_SH7786
        select CPU_HAS_PTEAEX
        select GENERIC_CLOCKEVENTS_BROADCAST if SMP
        select ARCH_WANT_OPTIONAL_GPIOLIB
-       select USB_ARCH_HAS_OHCI
        select USB_OHCI_SH if USB_OHCI_HCD
-       select USB_ARCH_HAS_EHCI
        select USB_EHCI_SH if USB_EHCI_HCD
        select PINCTRL
 
index cc32a6f96c64804d8a1fc936f0fd22244eb51e83..2448d757b449f232bedf22dfc15d817d30048f86 100644 (file)
@@ -2278,6 +2278,7 @@ static const struct hid_device_id hid_ignore_list[] = {
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_1_PHIDGETSERVO_20) },
        { HID_USB_DEVICE(USB_VENDOR_ID_WISEGROUP, USB_DEVICE_ID_8_8_4_IF_KIT) },
        { HID_USB_DEVICE(USB_VENDOR_ID_YEALINK, USB_DEVICE_ID_YEALINK_P1K_P4K_B2K) },
+       { HID_USB_DEVICE(USB_VENDOR_ID_RISO_KAGAKU, USB_DEVICE_ID_RI_KA_WEBMAIL) },
        { }
 };
 
index 22f28d6b33a845f0c728b3d4423b6c6fd848ff78..830de69d2df025b2295c049a0728f4fa1804b725 100644 (file)
 #define USB_DEVICE_ID_PRIMAX_KEYBOARD  0x4e05
 
 
+#define USB_VENDOR_ID_RISO_KAGAKU      0x1294  /* Riso Kagaku Corp. */
+#define USB_DEVICE_ID_RI_KA_WEBMAIL    0x1320  /* Webmail Notifier */
+
 #endif
index c7a551c2d5f1b4d39b982d85222537ec3fbfb804..8d3c49cc500fadcd212620f62df6d3cc9df68e4a 100644 (file)
@@ -16,30 +16,56 @@ config GENERIC_PHY
          framework should select this config.
 
 config PHY_EXYNOS_MIPI_VIDEO
-       depends on HAS_IOMEM
        tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
+       depends on HAS_IOMEM
+       depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+       select GENERIC_PHY
+       default y if ARCH_S5PV210 || ARCH_EXYNOS
        help
          Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
          and EXYNOS SoCs.
 
 config PHY_MVEBU_SATA
        def_bool y
-       depends on ARCH_KIRKWOOD || ARCH_DOVE
+       depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
        depends on OF
        select GENERIC_PHY
 
+config OMAP_CONTROL_PHY
+       tristate "OMAP CONTROL PHY Driver"
+       help
+         Enable this to add support for the PHY part present in the control
+         module. This driver has API to power on the USB2 PHY and to write to
+         the mailbox. The mailbox is present only in omap4 and the register to
+         power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
+         additional register to power on USB3 PHY/SATA PHY/PCIE PHY
+         (PIPE3 PHY).
+
 config OMAP_USB2
        tristate "OMAP USB2 PHY Driver"
        depends on ARCH_OMAP2PLUS
        depends on USB_PHY
        select GENERIC_PHY
-       select OMAP_CONTROL_USB
+       select OMAP_CONTROL_PHY
+       depends on OMAP_OCP2SCP
        help
          Enable this to support the transceiver that is part of SOC. This
          driver takes care of all the PHY functionality apart from comparator.
          The USB OTG controller communicates with the comparator using this
          driver.
 
+config TI_PIPE3
+       tristate "TI PIPE3 PHY Driver"
+       depends on ARCH_OMAP2PLUS || COMPILE_TEST
+       select GENERIC_PHY
+       select OMAP_CONTROL_PHY
+       depends on OMAP_OCP2SCP
+       help
+         Enable this to support the PIPE3 PHY that is part of TI SOCs. This
+         driver takes care of all the PHY functionality apart from comparator.
+         This driver interacts with the "OMAP Control PHY Driver" to power
+         on/off the PHY.
+
 config TWL4030_USB
        tristate "TWL4030 USB Transceiver Driver"
        depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
@@ -54,6 +80,8 @@ config TWL4030_USB
 config PHY_EXYNOS_DP_VIDEO
        tristate "EXYNOS SoC series Display Port PHY driver"
        depends on OF
+       depends on ARCH_EXYNOS || COMPILE_TEST
+       default ARCH_EXYNOS
        select GENERIC_PHY
        help
          Support for Display Port PHY found on Samsung EXYNOS SoCs.
@@ -65,4 +93,77 @@ config BCM_KONA_USB2_PHY
        help
          Enable this to support the Broadcom Kona USB 2.0 PHY.
 
+config PHY_EXYNOS5250_SATA
+       tristate "Exynos5250 Sata SerDes/PHY driver"
+       depends on SOC_EXYNOS5250
+       depends on HAS_IOMEM
+       depends on OF
+       select GENERIC_PHY
+       select I2C
+       select I2C_S3C2410
+       select MFD_SYSCON
+       help
+         Enable this to support SATA SerDes/Phy found on Samsung's
+         Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s,
+         SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
+         port to accept one SATA device.
+
+config PHY_SUN4I_USB
+       tristate "Allwinner sunxi SoC USB PHY driver"
+       depends on ARCH_SUNXI && HAS_IOMEM && OF
+       select GENERIC_PHY
+       help
+         Enable this to support the transceiver that is part of Allwinner
+         sunxi SoCs.
+
+         This driver controls the entire USB PHY block, both the USB OTG
+         parts, as well as the 2 regular USB 2 host PHYs.
+
+config PHY_SAMSUNG_USB2
+       tristate "Samsung USB 2.0 PHY driver"
+       select GENERIC_PHY
+       select MFD_SYSCON
+       help
+         Enable this to support the Samsung USB 2.0 PHY driver for Samsung
+         SoCs. This driver provides the interface for USB 2.0 PHY. Support for
+         particular SoCs has to be enabled in addition to this driver. Number
+         and type of supported phys depends on the SoC.
+
+config PHY_EXYNOS4210_USB2
+       bool "Support for Exynos 4210"
+       depends on PHY_SAMSUNG_USB2
+       depends on CPU_EXYNOS4210
+       help
+         Enable USB PHY support for Exynos 4210. This option requires that
+         Samsung USB 2.0 PHY driver is enabled and means that support for this
+         particular SoC is compiled in the driver. In case of Exynos 4210 four
+         phys are available - device, host, HSIC0 and HSIC1.
+
+config PHY_EXYNOS4X12_USB2
+       bool "Support for Exynos 4x12"
+       depends on PHY_SAMSUNG_USB2
+       depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
+       help
+         Enable USB PHY support for Exynos 4x12. This option requires that
+         Samsung USB 2.0 PHY driver is enabled and means that support for this
+         particular SoC is compiled in the driver. In case of Exynos 4x12 four
+         phys are available - device, host, HSIC0 and HSIC1.
+
+config PHY_EXYNOS5250_USB2
+       bool "Support for Exynos 5250"
+       depends on PHY_SAMSUNG_USB2
+       depends on SOC_EXYNOS5250
+       help
+         Enable USB PHY support for Exynos 5250. This option requires that
+         Samsung USB 2.0 PHY driver is enabled and means that support for this
+         particular SoC is compiled in the driver. In case of Exynos 5250 four
+         phys are available - device, host, HSIC0 and HSIC.
+
+config PHY_XGENE
+       tristate "APM X-Gene 15Gbps PHY support"
+       depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
+       select GENERIC_PHY
+       help
+         This option enables support for APM X-Gene SoC multi-purpose PHY.
+
 endmenu
index b57c25371cca48695cf6068f61cfbd85e44c281b..2faf78edc8649f2c5ef79c3093aaa14de04b0eea 100644 (file)
@@ -7,5 +7,14 @@ obj-$(CONFIG_BCM_KONA_USB2_PHY)                += phy-bcm-kona-usb2.o
 obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)      += phy-exynos-dp-video.o
 obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)    += phy-exynos-mipi-video.o
 obj-$(CONFIG_PHY_MVEBU_SATA)           += phy-mvebu-sata.o
+obj-$(CONFIG_OMAP_CONTROL_PHY)         += phy-omap-control.o
 obj-$(CONFIG_OMAP_USB2)                        += phy-omap-usb2.o
+obj-$(CONFIG_TI_PIPE3)                 += phy-ti-pipe3.o
 obj-$(CONFIG_TWL4030_USB)              += phy-twl4030-usb.o
+obj-$(CONFIG_PHY_EXYNOS5250_SATA)      += phy-exynos5250-sata.o
+obj-$(CONFIG_PHY_SUN4I_USB)            += phy-sun4i-usb.o
+obj-$(CONFIG_PHY_SAMSUNG_USB2)         += phy-samsung-usb2.o
+obj-$(CONFIG_PHY_EXYNOS4210_USB2)      += phy-exynos4210-usb2.o
+obj-$(CONFIG_PHY_EXYNOS4X12_USB2)      += phy-exynos4x12-usb2.o
+obj-$(CONFIG_PHY_EXYNOS5250_USB2)      += phy-exynos5250-usb2.o
+obj-$(CONFIG_PHY_XGENE)                        += phy-xgene.o
index efc5c1a13a5d0cd1acb9c03c98820002839cae49..e94f5a6a5645a33acaad36e64da7ac9c2d44b364 100644 (file)
@@ -128,10 +128,8 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
 
        phy_provider = devm_of_phy_provider_register(dev,
                        of_phy_simple_xlate);
-       if (IS_ERR(phy_provider))
-               return PTR_ERR(phy_provider);
 
-       return 0;
+       return PTR_ERR_OR_ZERO(phy_provider);
 }
 
 static const struct of_device_id bcm_kona_usb2_dt_ids[] = {
index 6c738376daff5110a791c3c3f6492ab818971165..623b71c54b3e5f0a865d8563c8557919e9603900 100644 (file)
@@ -274,8 +274,8 @@ int phy_power_off(struct phy *phy)
 EXPORT_SYMBOL_GPL(phy_power_off);
 
 /**
- * of_phy_get() - lookup and obtain a reference to a phy by phandle
- * @dev: device that requests this phy
+ * _of_phy_get() - lookup and obtain a reference to a phy by phandle
+ * @np: device_node for which to get the phy
  * @index: the index of the phy
  *
  * Returns the phy associated with the given phandle value,
@@ -284,20 +284,17 @@ EXPORT_SYMBOL_GPL(phy_power_off);
  * not yet loaded. This function uses of_xlate call back function provided
  * while registering the phy_provider to find the phy instance.
  */
-static struct phy *of_phy_get(struct device *dev, int index)
+static struct phy *_of_phy_get(struct device_node *np, int index)
 {
        int ret;
        struct phy_provider *phy_provider;
        struct phy *phy = NULL;
        struct of_phandle_args args;
 
-       ret = of_parse_phandle_with_args(dev->of_node, "phys", "#phy-cells",
+       ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
                index, &args);
-       if (ret) {
-               dev_dbg(dev, "failed to get phy in %s node\n",
-                       dev->of_node->full_name);
+       if (ret)
                return ERR_PTR(-ENODEV);
-       }
 
        mutex_lock(&phy_provider_mutex);
        phy_provider = of_phy_provider_lookup(args.np);
@@ -316,6 +313,36 @@ err0:
        return phy;
 }
 
+/**
+ * of_phy_get() - lookup and obtain a reference to a phy using a device_node.
+ * @np: device_node for which to get the phy
+ * @con_id: name of the phy from device's point of view
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *of_phy_get(struct device_node *np, const char *con_id)
+{
+       struct phy *phy = NULL;
+       int index = 0;
+
+       if (con_id)
+               index = of_property_match_string(np, "phy-names", con_id);
+
+       phy = _of_phy_get(np, index);
+       if (IS_ERR(phy))
+               return phy;
+
+       if (!try_module_get(phy->ops->owner))
+               return ERR_PTR(-EPROBE_DEFER);
+
+       get_device(&phy->dev);
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(of_phy_get);
+
 /**
  * phy_put() - release the PHY
  * @phy: the phy returned by phy_get()
@@ -407,7 +434,7 @@ struct phy *phy_get(struct device *dev, const char *string)
        if (dev->of_node) {
                index = of_property_match_string(dev->of_node, "phy-names",
                        string);
-               phy = of_phy_get(dev, index);
+               phy = _of_phy_get(dev->of_node, index);
        } else {
                phy = phy_lookup(dev, string);
        }
@@ -498,6 +525,37 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string)
 }
 EXPORT_SYMBOL_GPL(devm_phy_optional_get);
 
+/**
+ * devm_of_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @np: node containing the phy
+ * @con_id: name of the phy from device's point of view
+ *
+ * Gets the phy using of_phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
+                           const char *con_id)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = of_phy_get(np, con_id);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_get);
+
 /**
  * phy_create() - create a new phy
  * @dev: device that is creating the new phy
diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c
new file mode 100644 (file)
index 0000000..236a52a
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4210_UPHYPWR                    0x0
+
+#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND       BIT(0)
+#define EXYNOS_4210_UPHYPWR_PHY0_PWR           BIT(3)
+#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR       BIT(4)
+#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP         BIT(5)
+#define EXYNOS_4210_UPHYPWR_PHY0       ( \
+       EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_PHY0_PWR | \
+       EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
+       EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND       BIT(6)
+#define EXYNOS_4210_UPHYPWR_PHY1_PWR           BIT(7)
+#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP         BIT(8)
+#define EXYNOS_4210_UPHYPWR_PHY1 ( \
+       EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_PHY1_PWR | \
+       EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND      BIT(9)
+#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP                BIT(10)
+#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
+       EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND      BIT(11)
+#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP                BIT(12)
+#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
+       EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4210_UPHYCLK                    0x4
+
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK       (0x3 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET     0
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ      (0x0 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ      (0x3 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ      (0x2 << 0)
+
+#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP     BIT(2)
+#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON     BIT(4)
+#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON     BIT(7)
+
+/* PHY reset control */
+#define EXYNOS_4210_UPHYRST                    0x8
+
+#define EXYNOS_4210_URSTCON_PHY0               BIT(0)
+#define EXYNOS_4210_URSTCON_OTG_HLINK          BIT(1)
+#define EXYNOS_4210_URSTCON_OTG_PHYLINK                BIT(2)
+#define EXYNOS_4210_URSTCON_PHY1_ALL           BIT(3)
+#define EXYNOS_4210_URSTCON_PHY1_P0            BIT(4)
+#define EXYNOS_4210_URSTCON_PHY1_P1P2          BIT(5)
+#define EXYNOS_4210_URSTCON_HOST_LINK_ALL      BIT(6)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P0       BIT(7)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P1       BIT(8)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P2       BIT(9)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET     0x704
+#define EXYNOS_4210_USB_ISOL_DEVICE            BIT(0)
+#define EXYNOS_4210_USB_ISOL_HOST_OFFSET       0x708
+#define EXYNOS_4210_USB_ISOL_HOST              BIT(0)
+
+/* USBYPHY1 Floating prevention */
+#define EXYNOS_4210_UPHY1CON                   0x34
+#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION  0x1
+
+/* Mode switching SUB Device <-> Host */
+#define EXYNOS_4210_MODE_SWITCH_OFFSET         0x21c
+#define EXYNOS_4210_MODE_SWITCH_MASK           1
+#define EXYNOS_4210_MODE_SWITCH_DEVICE         0
+#define EXYNOS_4210_MODE_SWITCH_HOST           1
+
+enum exynos4210_phy_id {
+       EXYNOS4210_DEVICE,
+       EXYNOS4210_HOST,
+       EXYNOS4210_HSIC0,
+       EXYNOS4210_HSIC1,
+       EXYNOS4210_NUM_PHYS,
+};
+
+/*
+ * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       switch (rate) {
+       case 12 * MHZ:
+               *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
+               break;
+       case 48 * MHZ:
+               *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 offset;
+       u32 mask;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4210_DEVICE:
+               offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
+               mask = EXYNOS_4210_USB_ISOL_DEVICE;
+               break;
+       case EXYNOS4210_HOST:
+               offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
+               mask = EXYNOS_4210_USB_ISOL_HOST;
+               break;
+       default:
+               return;
+       };
+
+       regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 rstbits = 0;
+       u32 phypwr = 0;
+       u32 rst;
+       u32 pwr;
+       u32 clk;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4210_DEVICE:
+               phypwr =        EXYNOS_4210_UPHYPWR_PHY0;
+               rstbits =       EXYNOS_4210_URSTCON_PHY0;
+               break;
+       case EXYNOS4210_HOST:
+               phypwr =        EXYNOS_4210_UPHYPWR_PHY1;
+               rstbits =       EXYNOS_4210_URSTCON_PHY1_ALL |
+                               EXYNOS_4210_URSTCON_PHY1_P0 |
+                               EXYNOS_4210_URSTCON_PHY1_P1P2 |
+                               EXYNOS_4210_URSTCON_HOST_LINK_ALL |
+                               EXYNOS_4210_URSTCON_HOST_LINK_P0;
+               writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
+               break;
+       case EXYNOS4210_HSIC0:
+               phypwr =        EXYNOS_4210_UPHYPWR_HSIC0;
+               rstbits =       EXYNOS_4210_URSTCON_PHY1_P1P2 |
+                               EXYNOS_4210_URSTCON_HOST_LINK_P1;
+               break;
+       case EXYNOS4210_HSIC1:
+               phypwr =        EXYNOS_4210_UPHYPWR_HSIC1;
+               rstbits =       EXYNOS_4210_URSTCON_PHY1_P1P2 |
+                               EXYNOS_4210_URSTCON_HOST_LINK_P2;
+               break;
+       };
+
+       if (on) {
+               clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
+               clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
+               clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
+               writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
+
+               pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
+               pwr &= ~phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
+
+               rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
+               rst |= rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
+               udelay(10);
+               rst &= ~rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
+               /* The following delay is necessary for the reset sequence to be
+                * completed */
+               udelay(80);
+       } else {
+               pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
+               pwr |= phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
+       }
+}
+
+static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       /* Order of initialisation is important - first power then isolation */
+       exynos4210_phy_pwr(inst, 1);
+       exynos4210_isol(inst, 0);
+
+       return 0;
+}
+
+static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       exynos4210_isol(inst, 1);
+       exynos4210_phy_pwr(inst, 0);
+
+       return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos4210_phys[] = {
+       {
+               .label          = "device",
+               .id             = EXYNOS4210_DEVICE,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "host",
+               .id             = EXYNOS4210_HOST,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "hsic0",
+               .id             = EXYNOS4210_HSIC0,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "hsic1",
+               .id             = EXYNOS4210_HSIC1,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {},
+};
+
+const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
+       .has_mode_switch        = 0,
+       .num_phys               = EXYNOS4210_NUM_PHYS,
+       .phys                   = exynos4210_phys,
+       .rate_to_clk            = exynos4210_rate_to_clk,
+};
diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c
new file mode 100644 (file)
index 0000000..d92a7cc
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4x12_UPHYPWR                    0x0
+
+#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND       BIT(0)
+#define EXYNOS_4x12_UPHYPWR_PHY0_PWR           BIT(3)
+#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR       BIT(4)
+#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP         BIT(5)
+#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
+       EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
+       EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
+       EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND       BIT(6)
+#define EXYNOS_4x12_UPHYPWR_PHY1_PWR           BIT(7)
+#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP         BIT(8)
+#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
+       EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
+       EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND      BIT(9)
+#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR          BIT(10)
+#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP                BIT(11)
+#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
+       EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
+       EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND      BIT(12)
+#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR          BIT(13)
+#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP                BIT(14)
+#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
+       EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
+       EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4x12_UPHYCLK                    0x4
+
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK       (0x7 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET     0
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6      (0x0 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ      (0x1 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ      (0x2 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2     (0x3 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ      (0x4 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ      (0x5 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ      (0x7 << 0)
+
+#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP     BIT(3)
+#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON     BIT(4)
+#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON     BIT(7)
+
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK   (0x7f << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET  10
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ  (0x24 << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ  (0x1c << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ  (0x1a << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ  (0x14 << 10)
+
+/* PHY reset control */
+#define EXYNOS_4x12_UPHYRST                    0x8
+
+#define EXYNOS_4x12_URSTCON_PHY0               BIT(0)
+#define EXYNOS_4x12_URSTCON_OTG_HLINK          BIT(1)
+#define EXYNOS_4x12_URSTCON_OTG_PHYLINK                BIT(2)
+#define EXYNOS_4x12_URSTCON_HOST_PHY           BIT(3)
+#define EXYNOS_4x12_URSTCON_PHY1               BIT(4)
+#define EXYNOS_4x12_URSTCON_HSIC0              BIT(5)
+#define EXYNOS_4x12_URSTCON_HSIC1              BIT(6)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL      BIT(7)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P0       BIT(8)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P1       BIT(9)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P2       BIT(10)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4x12_USB_ISOL_OFFSET            0x704
+#define EXYNOS_4x12_USB_ISOL_OTG               BIT(0)
+#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET      0x708
+#define EXYNOS_4x12_USB_ISOL_HSIC0             BIT(0)
+#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET      0x70c
+#define EXYNOS_4x12_USB_ISOL_HSIC1             BIT(0)
+
+/* Mode switching SUB Device <-> Host */
+#define EXYNOS_4x12_MODE_SWITCH_OFFSET         0x21c
+#define EXYNOS_4x12_MODE_SWITCH_MASK           1
+#define EXYNOS_4x12_MODE_SWITCH_DEVICE         0
+#define EXYNOS_4x12_MODE_SWITCH_HOST           1
+
+enum exynos4x12_phy_id {
+       EXYNOS4x12_DEVICE,
+       EXYNOS4x12_HOST,
+       EXYNOS4x12_HSIC0,
+       EXYNOS4x12_HSIC1,
+       EXYNOS4x12_NUM_PHYS,
+};
+
+/*
+ * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
+
+       switch (rate) {
+       case 9600 * KHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
+               break;
+       case 10 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
+               break;
+       case 12 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
+               break;
+       case 19200 * KHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
+               break;
+       case 20 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
+               break;
+       case 50 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 offset;
+       u32 mask;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4x12_DEVICE:
+       case EXYNOS4x12_HOST:
+               offset = EXYNOS_4x12_USB_ISOL_OFFSET;
+               mask = EXYNOS_4x12_USB_ISOL_OTG;
+               break;
+       case EXYNOS4x12_HSIC0:
+               offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
+               mask = EXYNOS_4x12_USB_ISOL_HSIC0;
+               break;
+       case EXYNOS4x12_HSIC1:
+               offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
+               mask = EXYNOS_4x12_USB_ISOL_HSIC1;
+               break;
+       default:
+               return;
+       };
+
+       regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 clk;
+
+       clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
+       clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
+       clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
+       writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
+}
+
+static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 rstbits = 0;
+       u32 phypwr = 0;
+       u32 rst;
+       u32 pwr;
+       u32 mode = 0;
+       u32 switch_mode = 0;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4x12_DEVICE:
+               phypwr =        EXYNOS_4x12_UPHYPWR_PHY0;
+               rstbits =       EXYNOS_4x12_URSTCON_PHY0;
+               mode =          EXYNOS_4x12_MODE_SWITCH_DEVICE;
+               switch_mode =   1;
+               break;
+       case EXYNOS4x12_HOST:
+               phypwr =        EXYNOS_4x12_UPHYPWR_PHY1;
+               rstbits =       EXYNOS_4x12_URSTCON_HOST_PHY;
+               mode =          EXYNOS_4x12_MODE_SWITCH_HOST;
+               switch_mode =   1;
+               break;
+       case EXYNOS4x12_HSIC0:
+               phypwr =        EXYNOS_4x12_UPHYPWR_HSIC0;
+               rstbits =       EXYNOS_4x12_URSTCON_HSIC1 |
+                               EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
+                               EXYNOS_4x12_URSTCON_HOST_PHY;
+               break;
+       case EXYNOS4x12_HSIC1:
+               phypwr =        EXYNOS_4x12_UPHYPWR_HSIC1;
+               rstbits =       EXYNOS_4x12_URSTCON_HSIC1 |
+                               EXYNOS_4x12_URSTCON_HOST_LINK_P1;
+               break;
+       };
+
+       if (on) {
+               if (switch_mode)
+                       regmap_update_bits(drv->reg_sys,
+                                          EXYNOS_4x12_MODE_SWITCH_OFFSET,
+                                          EXYNOS_4x12_MODE_SWITCH_MASK, mode);
+
+               pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+               pwr &= ~phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+
+               rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
+               rst |= rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
+               udelay(10);
+               rst &= ~rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
+               /* The following delay is necessary for the reset sequence to be
+                * completed */
+               udelay(80);
+       } else {
+               pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+               pwr |= phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+       }
+}
+
+static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+
+       inst->enabled = 1;
+       exynos4x12_setup_clk(inst);
+       exynos4x12_phy_pwr(inst, 1);
+       exynos4x12_isol(inst, 0);
+
+       /* Power on the device, as it is necessary for HSIC to work */
+       if (inst->cfg->id == EXYNOS4x12_HSIC0) {
+               struct samsung_usb2_phy_instance *device =
+                                       &drv->instances[EXYNOS4x12_DEVICE];
+               exynos4x12_phy_pwr(device, 1);
+               exynos4x12_isol(device, 0);
+       }
+
+       return 0;
+}
+
+static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       struct samsung_usb2_phy_instance *device =
+                                       &drv->instances[EXYNOS4x12_DEVICE];
+
+       inst->enabled = 0;
+       exynos4x12_isol(inst, 1);
+       exynos4x12_phy_pwr(inst, 0);
+
+       if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
+               exynos4x12_isol(device, 1);
+               exynos4x12_phy_pwr(device, 0);
+       }
+
+       return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
+       {
+               .label          = "device",
+               .id             = EXYNOS4x12_DEVICE,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {
+               .label          = "host",
+               .id             = EXYNOS4x12_HOST,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {
+               .label          = "hsic0",
+               .id             = EXYNOS4x12_HSIC0,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {
+               .label          = "hsic1",
+               .id             = EXYNOS4x12_HSIC1,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {},
+};
+
+const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
+       .has_mode_switch        = 1,
+       .num_phys               = EXYNOS4x12_NUM_PHYS,
+       .phys                   = exynos4x12_phys,
+       .rate_to_clk            = exynos4x12_rate_to_clk,
+};
diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c
new file mode 100644 (file)
index 0000000..c9361b7
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * Samsung SATA SerDes(PHY) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Authors: Girish K S <ks.giri@samsung.com>
+ *         Yuvaraj Kumar C D <yuvaraj.cd@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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
+
+#define SATAPHY_CONTROL_OFFSET         0x0724
+#define EXYNOS5_SATAPHY_PMU_ENABLE     BIT(0)
+#define EXYNOS5_SATA_RESET             0x4
+#define RESET_GLOBAL_RST_N             BIT(0)
+#define RESET_CMN_RST_N                        BIT(1)
+#define RESET_CMN_BLOCK_RST_N          BIT(2)
+#define RESET_CMN_I2C_RST_N            BIT(3)
+#define RESET_TX_RX_PIPE_RST_N         BIT(4)
+#define RESET_TX_RX_BLOCK_RST_N                BIT(5)
+#define RESET_TX_RX_I2C_RST_N          (BIT(6) | BIT(7))
+#define LINK_RESET                     0xf0000
+#define EXYNOS5_SATA_MODE0             0x10
+#define SATA_SPD_GEN3                  BIT(1)
+#define EXYNOS5_SATA_CTRL0             0x14
+#define CTRL0_P0_PHY_CALIBRATED_SEL    BIT(9)
+#define CTRL0_P0_PHY_CALIBRATED                BIT(8)
+#define EXYNOS5_SATA_PHSATA_CTRLM      0xe0
+#define PHCTRLM_REF_RATE               BIT(1)
+#define PHCTRLM_HIGH_SPEED             BIT(0)
+#define EXYNOS5_SATA_PHSATA_STATM      0xf0
+#define PHSTATM_PLL_LOCKED             BIT(0)
+
+#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
+
+struct exynos_sata_phy {
+       struct phy *phy;
+       struct clk *phyclk;
+       void __iomem *regs;
+       struct regmap *pmureg;
+       struct i2c_client *client;
+};
+
+static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
+                               u32 status)
+{
+       unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
+
+       while (time_before(jiffies, timeout)) {
+               if ((readl(base + reg) & checkbit) == status)
+                       return 0;
+       }
+
+       return -EFAULT;
+}
+
+static int exynos_sata_phy_power_on(struct phy *phy)
+{
+       struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
+
+       return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
+                       EXYNOS5_SATAPHY_PMU_ENABLE, true);
+
+}
+
+static int exynos_sata_phy_power_off(struct phy *phy)
+{
+       struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
+
+       return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
+                       EXYNOS5_SATAPHY_PMU_ENABLE, false);
+
+}
+
+static int exynos_sata_phy_init(struct phy *phy)
+{
+       u32 val = 0;
+       int ret = 0;
+       u8 buf[] = { 0x3a, 0x0b };
+       struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
+
+       ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
+                       EXYNOS5_SATAPHY_PMU_ENABLE, true);
+       if (ret != 0)
+               dev_err(&sata_phy->phy->dev, "phy init failed\n");
+
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
+               | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
+               | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= LINK_RESET;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= RESET_CMN_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+       val &= ~PHCTRLM_REF_RATE;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+
+       /* High speed enable for Gen3 */
+       val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+       val |= PHCTRLM_HIGH_SPEED;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
+       val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
+       val |= SATA_SPD_GEN3;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
+
+       ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
+       if (ret < 0)
+               return ret;
+
+       /* release cmu reset */
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val &= ~RESET_CMN_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= RESET_CMN_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       ret = wait_for_reg_status(sata_phy->regs,
+                               EXYNOS5_SATA_PHSATA_STATM,
+                               PHSTATM_PLL_LOCKED, 1);
+       if (ret < 0)
+               dev_err(&sata_phy->phy->dev,
+                       "PHY PLL locking failed\n");
+       return ret;
+}
+
+static struct phy_ops exynos_sata_phy_ops = {
+       .init           = exynos_sata_phy_init,
+       .power_on       = exynos_sata_phy_power_on,
+       .power_off      = exynos_sata_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int exynos_sata_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_sata_phy *sata_phy;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct device_node *node;
+       int ret = 0;
+
+       sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
+       if (!sata_phy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       sata_phy->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(sata_phy->regs))
+               return PTR_ERR(sata_phy->regs);
+
+       sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                       "samsung,syscon-phandle");
+       if (IS_ERR(sata_phy->pmureg)) {
+               dev_err(dev, "syscon regmap lookup failed.\n");
+               return PTR_ERR(sata_phy->pmureg);
+       }
+
+       node = of_parse_phandle(dev->of_node,
+                       "samsung,exynos-sataphy-i2c-phandle", 0);
+       if (!node)
+               return -EINVAL;
+
+       sata_phy->client = of_find_i2c_device_by_node(node);
+       if (!sata_phy->client)
+               return -EPROBE_DEFER;
+
+       dev_set_drvdata(dev, sata_phy);
+
+       sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
+       if (IS_ERR(sata_phy->phyclk)) {
+               dev_err(dev, "failed to get clk for PHY\n");
+               return PTR_ERR(sata_phy->phyclk);
+       }
+
+       ret = clk_prepare_enable(sata_phy->phyclk);
+       if (ret < 0) {
+               dev_err(dev, "failed to enable source clk\n");
+               return ret;
+       }
+
+       sata_phy->phy = devm_phy_create(dev, &exynos_sata_phy_ops, NULL);
+       if (IS_ERR(sata_phy->phy)) {
+               clk_disable_unprepare(sata_phy->phyclk);
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(sata_phy->phy);
+       }
+
+       phy_set_drvdata(sata_phy->phy, sata_phy);
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                       of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               clk_disable_unprepare(sata_phy->phyclk);
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static const struct of_device_id exynos_sata_phy_of_match[] = {
+       { .compatible = "samsung,exynos5250-sata-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
+
+static struct platform_driver exynos_sata_phy_driver = {
+       .probe  = exynos_sata_phy_probe,
+       .driver = {
+               .of_match_table = exynos_sata_phy_of_match,
+               .name  = "samsung,sata-phy",
+               .owner = THIS_MODULE,
+       }
+};
+module_platform_driver(exynos_sata_phy_driver);
+
+MODULE_DESCRIPTION("Samsung SerDes PHY driver");
+MODULE_LICENSE("GPL V2");
+MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
+MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c
new file mode 100644 (file)
index 0000000..94179af
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+#define EXYNOS_5250_REFCLKSEL_CRYSTAL  0x0
+#define EXYNOS_5250_REFCLKSEL_XO       0x1
+#define EXYNOS_5250_REFCLKSEL_CLKCORE  0x2
+
+#define EXYNOS_5250_FSEL_9MHZ6         0x0
+#define EXYNOS_5250_FSEL_10MHZ         0x1
+#define EXYNOS_5250_FSEL_12MHZ         0x2
+#define EXYNOS_5250_FSEL_19MHZ2                0x3
+#define EXYNOS_5250_FSEL_20MHZ         0x4
+#define EXYNOS_5250_FSEL_24MHZ         0x5
+#define EXYNOS_5250_FSEL_50MHZ         0x7
+
+/* Normal host */
+#define EXYNOS_5250_HOSTPHYCTRL0                       0x0
+
+#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL           BIT(31)
+#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT       19
+#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK        \
+               (0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT)
+#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT            16
+#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \
+               (0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT)
+#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN            BIT(11)
+#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE             BIT(10)
+#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N           BIT(9)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK                (0x3 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL                (0x0 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0         (0x1 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST  (0x2 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ                 BIT(6)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP            BIT(5)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND          BIT(4)
+#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE         BIT(3)
+#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST             BIT(2)
+#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST             BIT(1)
+#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST              BIT(0)
+
+/* HSIC0 & HSIC1 */
+#define EXYNOS_5250_HSICPHYCTRL1                       0x10
+#define EXYNOS_5250_HSICPHYCTRL2                       0x20
+
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK                (0x3 << 23)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT     (0x2 << 23)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK                (0x7f << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12          (0x24 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15          (0x1c << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16          (0x1a << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2                (0x15 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20          (0x14 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ                 BIT(6)
+#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP            BIT(5)
+#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND          BIT(4)
+#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE         BIT(3)
+#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST             BIT(2)
+#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST              BIT(0)
+
+/* EHCI control */
+#define EXYNOS_5250_HOSTEHCICTRL                       0x30
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN         BIT(29)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4              BIT(28)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8              BIT(27)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16             BIT(26)
+#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN     BIT(25)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT       19
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK        \
+               (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT       13
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK        \
+               (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT       7
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK        \
+               (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT    1
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \
+               (0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE                BIT(0)
+
+/* OHCI control */
+#define EXYNOS_5250_HOSTOHCICTRL                        0x34
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT     1
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \
+               (0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT)
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN         BIT(0)
+
+/* USBOTG */
+#define EXYNOS_5250_USBOTGSYS                          0x38
+#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET         BIT(14)
+#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG         BIT(13)
+#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST               BIT(12)
+#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT          9
+#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \
+               (0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT)
+#define EXYNOS_5250_USBOTGSYS_ID_PULLUP                        BIT(8)
+#define EXYNOS_5250_USBOTGSYS_COMMON_ON                        BIT(7)
+#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT               4
+#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \
+               (0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT)
+#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP              BIT(3)
+#define EXYNOS_5250_USBOTGSYS_OTGDISABLE               BIT(2)
+#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG               BIT(1)
+#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND            BIT(0)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_5250_USB_ISOL_OTG_OFFSET                0x704
+#define EXYNOS_5250_USB_ISOL_OTG               BIT(0)
+#define EXYNOS_5250_USB_ISOL_HOST_OFFSET       0x708
+#define EXYNOS_5250_USB_ISOL_HOST              BIT(0)
+
+/* Mode swtich register */
+#define EXYNOS_5250_MODE_SWITCH_OFFSET         0x230
+#define EXYNOS_5250_MODE_SWITCH_MASK           1
+#define EXYNOS_5250_MODE_SWITCH_DEVICE         0
+#define EXYNOS_5250_MODE_SWITCH_HOST           1
+
+enum exynos4x12_phy_id {
+       EXYNOS5250_DEVICE,
+       EXYNOS5250_HOST,
+       EXYNOS5250_HSIC0,
+       EXYNOS5250_HSIC1,
+       EXYNOS5250_NUM_PHYS,
+};
+
+/*
+ * exynos5250_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       /* EXYNOS_5250_FSEL_MASK */
+
+       switch (rate) {
+       case 9600 * KHZ:
+               *reg = EXYNOS_5250_FSEL_9MHZ6;
+               break;
+       case 10 * MHZ:
+               *reg = EXYNOS_5250_FSEL_10MHZ;
+               break;
+       case 12 * MHZ:
+               *reg = EXYNOS_5250_FSEL_12MHZ;
+               break;
+       case 19200 * KHZ:
+               *reg = EXYNOS_5250_FSEL_19MHZ2;
+               break;
+       case 20 * MHZ:
+               *reg = EXYNOS_5250_FSEL_20MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = EXYNOS_5250_FSEL_24MHZ;
+               break;
+       case 50 * MHZ:
+               *reg = EXYNOS_5250_FSEL_50MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 offset;
+       u32 mask;
+
+       switch (inst->cfg->id) {
+       case EXYNOS5250_DEVICE:
+               offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET;
+               mask = EXYNOS_5250_USB_ISOL_OTG;
+               break;
+       case EXYNOS5250_HOST:
+               offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET;
+               mask = EXYNOS_5250_USB_ISOL_HOST;
+               break;
+       default:
+               return;
+       };
+
+       regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 ctrl0;
+       u32 otg;
+       u32 ehci;
+       u32 ohci;
+       u32 hsic;
+
+       switch (inst->cfg->id) {
+       case EXYNOS5250_DEVICE:
+               regmap_update_bits(drv->reg_sys,
+                                  EXYNOS_5250_MODE_SWITCH_OFFSET,
+                                  EXYNOS_5250_MODE_SWITCH_MASK,
+                                  EXYNOS_5250_MODE_SWITCH_DEVICE);
+
+               /* OTG configuration */
+               otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               /* The clock */
+               otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
+               otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+               /* Reset */
+               otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+                       EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+                       EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+               otg |=  EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_OTGDISABLE;
+               /* Ref clock */
+               otg &=  ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
+               otg |=  EXYNOS_5250_REFCLKSEL_CLKCORE <<
+                                       EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               udelay(100);
+               otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+                       EXYNOS_5250_USBOTGSYS_OTGDISABLE);
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+
+
+               break;
+       case EXYNOS5250_HOST:
+       case EXYNOS5250_HSIC0:
+       case EXYNOS5250_HSIC1:
+               /* Host registers configuration */
+               ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               /* The clock */
+               ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
+               ctrl0 |= drv->ref_reg_val <<
+                                       EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT;
+
+               /* Reset */
+               ctrl0 &=        ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL |
+                               EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP);
+               ctrl0 |=        EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N;
+               writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               udelay(10);
+               ctrl0 &=        ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST);
+               writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+
+               /* OTG configuration */
+               otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               /* The clock */
+               otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
+               otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+               /* Reset */
+               otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+                       EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+                       EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+               otg |=  EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_OTGDISABLE;
+               /* Ref clock */
+               otg &=  ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
+               otg |=  EXYNOS_5250_REFCLKSEL_CLKCORE <<
+                                       EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               udelay(10);
+               otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET);
+
+               /* HSIC phy configuration */
+               hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
+                               EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
+                               EXYNOS_5250_HSICPHYCTRLX_PHYSWRST);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+               udelay(10);
+               hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST;
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+               /* The following delay is necessary for the reset sequence to be
+                * completed */
+               udelay(80);
+
+               /* Enable EHCI DMA burst */
+               ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
+               ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN |
+                       EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 |
+                       EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 |
+                       EXYNOS_5250_HOSTEHCICTRL_ENAINCR16;
+               writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
+
+               /* OHCI settings */
+               ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
+               /* Following code is based on the old driver */
+               ohci |= 0x1 << 3;
+               writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
+
+               break;
+       }
+       inst->enabled = 1;
+       exynos5250_isol(inst, 0);
+
+       return 0;
+}
+
+static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 ctrl0;
+       u32 otg;
+       u32 hsic;
+
+       inst->enabled = 0;
+       exynos5250_isol(inst, 1);
+
+       switch (inst->cfg->id) {
+       case EXYNOS5250_DEVICE:
+               otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+                       EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG |
+                       EXYNOS_5250_USBOTGSYS_FORCE_SLEEP);
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               break;
+       case EXYNOS5250_HOST:
+               ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP |
+                               EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL);
+               writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               break;
+       case EXYNOS5250_HSIC0:
+       case EXYNOS5250_HSIC1:
+               hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
+                               EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
+                               EXYNOS_5250_HSICPHYCTRLX_SIDDQ |
+                               EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP |
+                               EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND
+                               );
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+               break;
+       }
+
+       return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos5250_phys[] = {
+       {
+               .label          = "device",
+               .id             = EXYNOS5250_DEVICE,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {
+               .label          = "host",
+               .id             = EXYNOS5250_HOST,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {
+               .label          = "hsic0",
+               .id             = EXYNOS5250_HSIC0,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {
+               .label          = "hsic1",
+               .id             = EXYNOS5250_HSIC1,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {},
+};
+
+const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = {
+       .has_mode_switch        = 1,
+       .num_phys               = EXYNOS5250_NUM_PHYS,
+       .phys                   = exynos5250_phys,
+       .rate_to_clk            = exynos5250_rate_to_clk,
+};
diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c
new file mode 100644 (file)
index 0000000..311b4f9
--- /dev/null
@@ -0,0 +1,320 @@
+/*
+ * omap-control-phy.c - The PHY part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/phy/omap_control_phy.h>
+
+/**
+ * omap_control_phy_power - power on/off the phy using control module reg
+ * @dev: the control module device
+ * @on: 0 or 1, based on powering on or off the PHY
+ */
+void omap_control_phy_power(struct device *dev, int on)
+{
+       u32 val;
+       unsigned long rate;
+       struct omap_control_phy *control_phy;
+
+       if (IS_ERR(dev) || !dev) {
+               pr_err("%s: invalid device\n", __func__);
+               return;
+       }
+
+       control_phy = dev_get_drvdata(dev);
+       if (!control_phy) {
+               dev_err(dev, "%s: invalid control phy device\n", __func__);
+               return;
+       }
+
+       if (control_phy->type == OMAP_CTRL_TYPE_OTGHS)
+               return;
+
+       val = readl(control_phy->power);
+
+       switch (control_phy->type) {
+       case OMAP_CTRL_TYPE_USB2:
+               if (on)
+                       val &= ~OMAP_CTRL_DEV_PHY_PD;
+               else
+                       val |= OMAP_CTRL_DEV_PHY_PD;
+               break;
+
+       case OMAP_CTRL_TYPE_PIPE3:
+               rate = clk_get_rate(control_phy->sys_clk);
+               rate = rate/1000000;
+
+               if (on) {
+                       val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
+                       val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+                       val |= rate <<
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
+               } else {
+                       val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
+                       val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+               }
+               break;
+
+       case OMAP_CTRL_TYPE_DRA7USB2:
+               if (on)
+                       val &= ~OMAP_CTRL_USB2_PHY_PD;
+               else
+                       val |= OMAP_CTRL_USB2_PHY_PD;
+               break;
+
+       case OMAP_CTRL_TYPE_AM437USB2:
+               if (on) {
+                       val &= ~(AM437X_CTRL_USB2_PHY_PD |
+                                       AM437X_CTRL_USB2_OTG_PD);
+                       val |= (AM437X_CTRL_USB2_OTGVDET_EN |
+                                       AM437X_CTRL_USB2_OTGSESSEND_EN);
+               } else {
+                       val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |
+                                       AM437X_CTRL_USB2_OTGSESSEND_EN);
+                       val |= (AM437X_CTRL_USB2_PHY_PD |
+                                        AM437X_CTRL_USB2_OTG_PD);
+               }
+               break;
+       default:
+               dev_err(dev, "%s: type %d not recognized\n",
+                       __func__, control_phy->type);
+               break;
+       }
+
+       writel(val, control_phy->power);
+}
+EXPORT_SYMBOL_GPL(omap_control_phy_power);
+
+/**
+ * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core that a usb
+ * device has been connected.
+ */
+static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy)
+{
+       u32 val;
+
+       val = readl(ctrl_phy->otghs_control);
+       val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
+       val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
+       writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
+ * impedance
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core that it has been
+ * connected to a usb host.
+ */
+static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy)
+{
+       u32 val;
+
+       val = readl(ctrl_phy->otghs_control);
+       val &= ~OMAP_CTRL_DEV_SESSEND;
+       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
+               OMAP_CTRL_DEV_VBUSVALID;
+       writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
+ * impedance
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core it's now in
+ * disconnected state.
+ */
+static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy)
+{
+       u32 val;
+
+       val = readl(ctrl_phy->otghs_control);
+       val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
+       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
+       writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode
+ * or device mode or to denote disconnected state
+ * @dev: the control module device
+ * @mode: The mode to which usb should be configured
+ *
+ * This is an API to write to the mailbox register to notify the usb core that
+ * a usb device has been connected.
+ */
+void omap_control_usb_set_mode(struct device *dev,
+       enum omap_control_usb_mode mode)
+{
+       struct omap_control_phy *ctrl_phy;
+
+       if (IS_ERR(dev) || !dev)
+               return;
+
+       ctrl_phy = dev_get_drvdata(dev);
+
+       if (!ctrl_phy) {
+               dev_err(dev, "Invalid control phy device\n");
+               return;
+       }
+
+       if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS)
+               return;
+
+       switch (mode) {
+       case USB_MODE_HOST:
+               omap_control_usb_host_mode(ctrl_phy);
+               break;
+       case USB_MODE_DEVICE:
+               omap_control_usb_device_mode(ctrl_phy);
+               break;
+       case USB_MODE_DISCONNECT:
+               omap_control_usb_set_sessionend(ctrl_phy);
+               break;
+       default:
+               dev_vdbg(dev, "invalid omap control usb mode\n");
+       }
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
+
+#ifdef CONFIG_OF
+
+static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
+static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
+static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
+static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
+static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
+
+static const struct of_device_id omap_control_phy_id_table[] = {
+       {
+               .compatible = "ti,control-phy-otghs",
+               .data = &otghs_data,
+       },
+       {
+               .compatible = "ti,control-phy-usb2",
+               .data = &usb2_data,
+       },
+       {
+               .compatible = "ti,control-phy-pipe3",
+               .data = &pipe3_data,
+       },
+       {
+               .compatible = "ti,control-phy-usb2-dra7",
+               .data = &dra7usb2_data,
+       },
+       {
+               .compatible = "ti,control-phy-usb2-am437",
+               .data = &am437usb2_data,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
+#endif
+
+
+static int omap_control_phy_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       const struct of_device_id *of_id;
+       struct omap_control_phy *control_phy;
+
+       of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
+                               &pdev->dev);
+       if (!of_id)
+               return -EINVAL;
+
+       control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy),
+               GFP_KERNEL);
+       if (!control_phy) {
+               dev_err(&pdev->dev, "unable to alloc memory for control phy\n");
+               return -ENOMEM;
+       }
+
+       control_phy->dev = &pdev->dev;
+       control_phy->type = *(enum omap_control_phy_type *)of_id->data;
+
+       if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                       "otghs_control");
+               control_phy->otghs_control = devm_ioremap_resource(
+                       &pdev->dev, res);
+               if (IS_ERR(control_phy->otghs_control))
+                       return PTR_ERR(control_phy->otghs_control);
+       } else {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                               "power");
+               control_phy->power = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(control_phy->power)) {
+                       dev_err(&pdev->dev, "Couldn't get power register\n");
+                       return PTR_ERR(control_phy->power);
+               }
+       }
+
+       if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
+               control_phy->sys_clk = devm_clk_get(control_phy->dev,
+                       "sys_clkin");
+               if (IS_ERR(control_phy->sys_clk)) {
+                       pr_err("%s: unable to get sys_clkin\n", __func__);
+                       return -EINVAL;
+               }
+       }
+
+       dev_set_drvdata(control_phy->dev, control_phy);
+
+       return 0;
+}
+
+static struct platform_driver omap_control_phy_driver = {
+       .probe          = omap_control_phy_probe,
+       .driver         = {
+               .name   = "omap-control-phy",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(omap_control_phy_id_table),
+       },
+};
+
+static int __init omap_control_phy_init(void)
+{
+       return platform_driver_register(&omap_control_phy_driver);
+}
+subsys_initcall(omap_control_phy_init);
+
+static void __exit omap_control_phy_exit(void)
+{
+       platform_driver_unregister(&omap_control_phy_driver);
+}
+module_exit(omap_control_phy_exit);
+
+MODULE_ALIAS("platform: omap_control_phy");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
+MODULE_LICENSE("GPL v2");
index 7699752fba11bfbfa8acff589d794262d2882cc4..a2205a841e5e37266594692f2bb12f35f6321bf0 100644 (file)
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/io.h>
-#include <linux/usb/omap_usb.h>
+#include <linux/phy/omap_usb.h>
 #include <linux/usb/phy_companion.h>
 #include <linux/clk.h>
 #include <linux/err.h>
 #include <linux/pm_runtime.h>
 #include <linux/delay.h>
-#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/omap_control_phy.h>
 #include <linux/phy/phy.h>
 #include <linux/of_platform.h>
 
+#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
+#define USB2PHY_ANA_CONFIG1 0x4c
+
 /**
  * omap_usb2_set_comparator - links the comparator present in the sytem with
  *     this phy
@@ -98,65 +101,116 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
        return 0;
 }
 
-static int omap_usb2_suspend(struct usb_phy *x, int suspend)
+static int omap_usb_power_off(struct phy *x)
 {
-       struct omap_usb *phy = phy_to_omapusb(x);
-       int ret;
+       struct omap_usb *phy = phy_get_drvdata(x);
 
-       if (suspend && !phy->is_suspended) {
-               omap_control_usb_phy_power(phy->control_dev, 0);
-               pm_runtime_put_sync(phy->dev);
-               phy->is_suspended = 1;
-       } else if (!suspend && phy->is_suspended) {
-               ret = pm_runtime_get_sync(phy->dev);
-               if (ret < 0) {
-                       dev_err(phy->dev, "get_sync failed with err %d\n", ret);
-                       return ret;
-               }
-               omap_control_usb_phy_power(phy->control_dev, 1);
-               phy->is_suspended = 0;
-       }
+       omap_control_phy_power(phy->control_dev, 0);
 
        return 0;
 }
 
-static int omap_usb_power_off(struct phy *x)
+static int omap_usb_power_on(struct phy *x)
 {
        struct omap_usb *phy = phy_get_drvdata(x);
 
-       omap_control_usb_phy_power(phy->control_dev, 0);
+       omap_control_phy_power(phy->control_dev, 1);
 
        return 0;
 }
 
-static int omap_usb_power_on(struct phy *x)
+static int omap_usb_init(struct phy *x)
 {
        struct omap_usb *phy = phy_get_drvdata(x);
-
-       omap_control_usb_phy_power(phy->control_dev, 1);
+       u32 val;
+
+       if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
+               /*
+                *
+                * Reduce the sensitivity of internal PHY by enabling the
+                * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
+                * resolves issues with certain devices which can otherwise
+                * be prone to false disconnects.
+                *
+                */
+               val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
+               val |= USB2PHY_DISCON_BYP_LATCH;
+               omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
+       }
 
        return 0;
 }
 
 static struct phy_ops ops = {
+       .init           = omap_usb_init,
        .power_on       = omap_usb_power_on,
        .power_off      = omap_usb_power_off,
        .owner          = THIS_MODULE,
 };
 
+#ifdef CONFIG_OF
+static const struct usb_phy_data omap_usb2_data = {
+       .label = "omap_usb2",
+       .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
+};
+
+static const struct usb_phy_data omap5_usb2_data = {
+       .label = "omap5_usb2",
+       .flags = 0,
+};
+
+static const struct usb_phy_data dra7x_usb2_data = {
+       .label = "dra7x_usb2",
+       .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
+};
+
+static const struct usb_phy_data am437x_usb2_data = {
+       .label = "am437x_usb2",
+       .flags =  0,
+};
+
+static const struct of_device_id omap_usb2_id_table[] = {
+       {
+               .compatible = "ti,omap-usb2",
+               .data = &omap_usb2_data,
+       },
+       {
+               .compatible = "ti,omap5-usb2",
+               .data = &omap5_usb2_data,
+       },
+       {
+               .compatible = "ti,dra7x-usb2",
+               .data = &dra7x_usb2_data,
+       },
+       {
+               .compatible = "ti,am437x-usb2",
+               .data = &am437x_usb2_data,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
+#endif
+
 static int omap_usb2_probe(struct platform_device *pdev)
 {
        struct omap_usb *phy;
        struct phy *generic_phy;
+       struct resource *res;
        struct phy_provider *phy_provider;
        struct usb_otg *otg;
        struct device_node *node = pdev->dev.of_node;
        struct device_node *control_node;
        struct platform_device *control_pdev;
+       const struct of_device_id *of_id;
+       struct usb_phy_data *phy_data;
+
+       of_id = of_match_device(of_match_ptr(omap_usb2_id_table), &pdev->dev);
 
-       if (!node)
+       if (!of_id)
                return -EINVAL;
 
+       phy_data = (struct usb_phy_data *)of_id->data;
+
        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
        if (!phy) {
                dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
@@ -172,11 +226,18 @@ static int omap_usb2_probe(struct platform_device *pdev)
        phy->dev                = &pdev->dev;
 
        phy->phy.dev            = phy->dev;
-       phy->phy.label          = "omap-usb2";
-       phy->phy.set_suspend    = omap_usb2_suspend;
+       phy->phy.label          = phy_data->label;
        phy->phy.otg            = otg;
        phy->phy.type           = USB_PHY_TYPE_USB2;
 
+       if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
+               if (!phy->phy_base)
+                       return -ENOMEM;
+               phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
+       }
+
        control_node = of_parse_phandle(node, "ctrl-module", 0);
        if (!control_node) {
                dev_err(&pdev->dev, "Failed to get control device phandle\n");
@@ -190,14 +251,14 @@ static int omap_usb2_probe(struct platform_device *pdev)
        }
 
        phy->control_dev = &control_pdev->dev;
-
-       phy->is_suspended       = 1;
-       omap_control_usb_phy_power(phy->control_dev, 0);
+       omap_control_phy_power(phy->control_dev, 0);
 
        otg->set_host           = omap_usb_set_host;
        otg->set_peripheral     = omap_usb_set_peripheral;
-       otg->set_vbus           = omap_usb_set_vbus;
-       otg->start_srp          = omap_usb_start_srp;
+       if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
+               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;
 
        platform_set_drvdata(pdev, phy);
@@ -297,14 +358,6 @@ static const struct dev_pm_ops omap_usb2_pm_ops = {
 #define DEV_PM_OPS     NULL
 #endif
 
-#ifdef CONFIG_OF
-static const struct of_device_id omap_usb2_id_table[] = {
-       { .compatible = "ti,omap-usb2" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
-#endif
-
 static struct platform_driver omap_usb2_driver = {
        .probe          = omap_usb2_probe,
        .remove         = omap_usb2_remove,
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
new file mode 100644 (file)
index 0000000..8a8c6bc
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@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.
+ */
+
+#include <linux/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include "phy-samsung-usb2.h"
+
+static int samsung_usb2_phy_power_on(struct phy *phy)
+{
+       struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       int ret;
+
+       dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
+               inst->cfg->label);
+       ret = clk_prepare_enable(drv->clk);
+       if (ret)
+               goto err_main_clk;
+       ret = clk_prepare_enable(drv->ref_clk);
+       if (ret)
+               goto err_instance_clk;
+       if (inst->cfg->power_on) {
+               spin_lock(&drv->lock);
+               ret = inst->cfg->power_on(inst);
+               spin_unlock(&drv->lock);
+       }
+
+       return 0;
+
+err_instance_clk:
+       clk_disable_unprepare(drv->clk);
+err_main_clk:
+       return ret;
+}
+
+static int samsung_usb2_phy_power_off(struct phy *phy)
+{
+       struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       int ret = 0;
+
+       dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
+               inst->cfg->label);
+       if (inst->cfg->power_off) {
+               spin_lock(&drv->lock);
+               ret = inst->cfg->power_off(inst);
+               spin_unlock(&drv->lock);
+       }
+       clk_disable_unprepare(drv->ref_clk);
+       clk_disable_unprepare(drv->clk);
+       return ret;
+}
+
+static struct phy_ops samsung_usb2_phy_ops = {
+       .power_on       = samsung_usb2_phy_power_on,
+       .power_off      = samsung_usb2_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *samsung_usb2_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct samsung_usb2_phy_driver *drv;
+
+       drv = dev_get_drvdata(dev);
+       if (!drv)
+               return ERR_PTR(-EINVAL);
+
+       if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
+               return ERR_PTR(-ENODEV);
+
+       return drv->instances[args->args[0]].phy;
+}
+
+static const struct of_device_id samsung_usb2_phy_of_match[] = {
+#ifdef CONFIG_PHY_EXYNOS4210_USB2
+       {
+               .compatible = "samsung,exynos4210-usb2-phy",
+               .data = &exynos4210_usb2_phy_config,
+       },
+#endif
+#ifdef CONFIG_PHY_EXYNOS4X12_USB2
+       {
+               .compatible = "samsung,exynos4x12-usb2-phy",
+               .data = &exynos4x12_usb2_phy_config,
+       },
+#endif
+#ifdef CONFIG_PHY_EXYNOS5250_USB2
+       {
+               .compatible = "samsung,exynos5250-usb2-phy",
+               .data = &exynos5250_usb2_phy_config,
+       },
+#endif
+       { },
+};
+
+static int samsung_usb2_phy_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       const struct samsung_usb2_phy_config *cfg;
+       struct device *dev = &pdev->dev;
+       struct phy_provider *phy_provider;
+       struct resource *mem;
+       struct samsung_usb2_phy_driver *drv;
+       int i, ret;
+
+       if (!pdev->dev.of_node) {
+               dev_err(dev, "This driver is required to be instantiated from device tree\n");
+               return -EINVAL;
+       }
+
+       match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
+       if (!match) {
+               dev_err(dev, "of_match_node() failed\n");
+               return -EINVAL;
+       }
+       cfg = match->data;
+
+       drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
+               cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
+                                                               GFP_KERNEL);
+       if (!drv)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, drv);
+       spin_lock_init(&drv->lock);
+
+       drv->cfg = cfg;
+       drv->dev = dev;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       drv->reg_phy = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(drv->reg_phy)) {
+               dev_err(dev, "Failed to map register memory (phy)\n");
+               return PTR_ERR(drv->reg_phy);
+       }
+
+       drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+               "samsung,pmureg-phandle");
+       if (IS_ERR(drv->reg_pmu)) {
+               dev_err(dev, "Failed to map PMU registers (via syscon)\n");
+               return PTR_ERR(drv->reg_pmu);
+       }
+
+       if (drv->cfg->has_mode_switch) {
+               drv->reg_sys = syscon_regmap_lookup_by_phandle(
+                               pdev->dev.of_node, "samsung,sysreg-phandle");
+               if (IS_ERR(drv->reg_sys)) {
+                       dev_err(dev, "Failed to map system registers (via syscon)\n");
+                       return PTR_ERR(drv->reg_sys);
+               }
+       }
+
+       drv->clk = devm_clk_get(dev, "phy");
+       if (IS_ERR(drv->clk)) {
+               dev_err(dev, "Failed to get clock of phy controller\n");
+               return PTR_ERR(drv->clk);
+       }
+
+       drv->ref_clk = devm_clk_get(dev, "ref");
+       if (IS_ERR(drv->ref_clk)) {
+               dev_err(dev, "Failed to get reference clock for the phy controller\n");
+               return PTR_ERR(drv->ref_clk);
+       }
+
+       drv->ref_rate = clk_get_rate(drv->ref_clk);
+       if (drv->cfg->rate_to_clk) {
+               ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val);
+               if (ret)
+                       return ret;
+       }
+
+       for (i = 0; i < drv->cfg->num_phys; i++) {
+               char *label = drv->cfg->phys[i].label;
+               struct samsung_usb2_phy_instance *p = &drv->instances[i];
+
+               dev_dbg(dev, "Creating phy \"%s\"\n", label);
+               p->phy = devm_phy_create(dev, &samsung_usb2_phy_ops, NULL);
+               if (IS_ERR(p->phy)) {
+                       dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
+                               label);
+                       return PTR_ERR(p->phy);
+               }
+
+               p->cfg = &drv->cfg->phys[i];
+               p->drv = drv;
+               phy_set_bus_width(p->phy, 8);
+               phy_set_drvdata(p->phy, p);
+       }
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                                       samsung_usb2_phy_xlate);
+       if (IS_ERR(phy_provider)) {
+               dev_err(drv->dev, "Failed to register phy provider\n");
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static struct platform_driver samsung_usb2_phy_driver = {
+       .probe  = samsung_usb2_phy_probe,
+       .driver = {
+               .of_match_table = samsung_usb2_phy_of_match,
+               .name           = "samsung-usb2-phy",
+               .owner          = THIS_MODULE,
+       }
+};
+
+module_platform_driver(samsung_usb2_phy_driver);
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:samsung-usb2-phy");
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
new file mode 100644 (file)
index 0000000..45b3170
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@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 _PHY_EXYNOS_USB2_H
+#define _PHY_EXYNOS_USB2_H
+
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#define KHZ 1000
+#define MHZ (KHZ * KHZ)
+
+struct samsung_usb2_phy_driver;
+struct samsung_usb2_phy_instance;
+struct samsung_usb2_phy_config;
+
+struct samsung_usb2_phy_instance {
+       const struct samsung_usb2_common_phy *cfg;
+       struct phy *phy;
+       struct samsung_usb2_phy_driver *drv;
+       bool enabled;
+};
+
+struct samsung_usb2_phy_driver {
+       const struct samsung_usb2_phy_config *cfg;
+       struct clk *clk;
+       struct clk *ref_clk;
+       unsigned long ref_rate;
+       u32 ref_reg_val;
+       struct device *dev;
+       void __iomem *reg_phy;
+       struct regmap *reg_pmu;
+       struct regmap *reg_sys;
+       spinlock_t lock;
+       struct samsung_usb2_phy_instance instances[0];
+};
+
+struct samsung_usb2_common_phy {
+       int (*power_on)(struct samsung_usb2_phy_instance *);
+       int (*power_off)(struct samsung_usb2_phy_instance *);
+       unsigned int id;
+       char *label;
+};
+
+
+struct samsung_usb2_phy_config {
+       const struct samsung_usb2_common_phy *phys;
+       int (*rate_to_clk)(unsigned long, u32 *);
+       unsigned int num_phys;
+       bool has_mode_switch;
+};
+
+extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
+#endif
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
new file mode 100644 (file)
index 0000000..e6e6c4b
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * Allwinner sun4i USB phy driver
+ *
+ * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#define REG_ISCR                       0x00
+#define REG_PHYCTL                     0x04
+#define REG_PHYBIST                    0x08
+#define REG_PHYTUNE                    0x0c
+
+#define PHYCTL_DATA                    BIT(7)
+
+#define SUNXI_AHB_ICHR8_EN             BIT(10)
+#define SUNXI_AHB_INCR4_BURST_EN       BIT(9)
+#define SUNXI_AHB_INCRX_ALIGN_EN       BIT(8)
+#define SUNXI_ULPI_BYPASS_EN           BIT(0)
+
+/* Common Control Bits for Both PHYs */
+#define PHY_PLL_BW                     0x03
+#define PHY_RES45_CAL_EN               0x0c
+
+/* Private Control Bits for Each PHY */
+#define PHY_TX_AMPLITUDE_TUNE          0x20
+#define PHY_TX_SLEWRATE_TUNE           0x22
+#define PHY_VBUSVALID_TH_SEL           0x25
+#define PHY_PULLUP_RES_SEL             0x27
+#define PHY_OTG_FUNC_EN                        0x28
+#define PHY_VBUS_DET_EN                        0x29
+#define PHY_DISCON_TH_SEL              0x2a
+
+#define MAX_PHYS                       3
+
+struct sun4i_usb_phy_data {
+       struct clk *clk;
+       void __iomem *base;
+       struct mutex mutex;
+       int num_phys;
+       u32 disc_thresh;
+       struct sun4i_usb_phy {
+               struct phy *phy;
+               void __iomem *pmu;
+               struct regulator *vbus;
+               struct reset_control *reset;
+               int index;
+       } phys[MAX_PHYS];
+};
+
+#define to_sun4i_usb_phy_data(phy) \
+       container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
+
+static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
+                               int len)
+{
+       struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
+       u32 temp, usbc_bit = BIT(phy->index * 2);
+       int i;
+
+       mutex_lock(&phy_data->mutex);
+
+       for (i = 0; i < len; i++) {
+               temp = readl(phy_data->base + REG_PHYCTL);
+
+               /* clear the address portion */
+               temp &= ~(0xff << 8);
+
+               /* set the address */
+               temp |= ((addr + i) << 8);
+               writel(temp, phy_data->base + REG_PHYCTL);
+
+               /* set the data bit and clear usbc bit*/
+               temp = readb(phy_data->base + REG_PHYCTL);
+               if (data & 0x1)
+                       temp |= PHYCTL_DATA;
+               else
+                       temp &= ~PHYCTL_DATA;
+               temp &= ~usbc_bit;
+               writeb(temp, phy_data->base + REG_PHYCTL);
+
+               /* pulse usbc_bit */
+               temp = readb(phy_data->base + REG_PHYCTL);
+               temp |= usbc_bit;
+               writeb(temp, phy_data->base + REG_PHYCTL);
+
+               temp = readb(phy_data->base + REG_PHYCTL);
+               temp &= ~usbc_bit;
+               writeb(temp, phy_data->base + REG_PHYCTL);
+
+               data >>= 1;
+       }
+       mutex_unlock(&phy_data->mutex);
+}
+
+static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
+{
+       u32 bits, reg_value;
+
+       if (!phy->pmu)
+               return;
+
+       bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
+               SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
+
+       reg_value = readl(phy->pmu);
+
+       if (enable)
+               reg_value |= bits;
+       else
+               reg_value &= ~bits;
+
+       writel(reg_value, phy->pmu);
+}
+
+static int sun4i_usb_phy_init(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+       int ret;
+
+       ret = clk_prepare_enable(data->clk);
+       if (ret)
+               return ret;
+
+       ret = reset_control_deassert(phy->reset);
+       if (ret) {
+               clk_disable_unprepare(data->clk);
+               return ret;
+       }
+
+       /* Adjust PHY's magnitude and rate */
+       sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+
+       /* Disconnect threshold adjustment */
+       sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
+
+       sun4i_usb_phy_passby(phy, 1);
+
+       return 0;
+}
+
+static int sun4i_usb_phy_exit(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+       sun4i_usb_phy_passby(phy, 0);
+       reset_control_assert(phy->reset);
+       clk_disable_unprepare(data->clk);
+
+       return 0;
+}
+
+static int sun4i_usb_phy_power_on(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       int ret = 0;
+
+       if (phy->vbus)
+               ret = regulator_enable(phy->vbus);
+
+       return ret;
+}
+
+static int sun4i_usb_phy_power_off(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+
+       if (phy->vbus)
+               regulator_disable(phy->vbus);
+
+       return 0;
+}
+
+static struct phy_ops sun4i_usb_phy_ops = {
+       .init           = sun4i_usb_phy_init,
+       .exit           = sun4i_usb_phy_exit,
+       .power_on       = sun4i_usb_phy_power_on,
+       .power_off      = sun4i_usb_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *sun4i_usb_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+       if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
+               return ERR_PTR(-ENODEV);
+
+       return data->phys[args->args[0]].phy;
+}
+
+static int sun4i_usb_phy_probe(struct platform_device *pdev)
+{
+       struct sun4i_usb_phy_data *data;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       void __iomem *pmu = NULL;
+       struct phy_provider *phy_provider;
+       struct reset_control *reset;
+       struct regulator *vbus;
+       struct resource *res;
+       struct phy *phy;
+       char name[16];
+       int i;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       mutex_init(&data->mutex);
+
+       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
+               data->num_phys = 2;
+       else
+               data->num_phys = 3;
+
+       if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
+               data->disc_thresh = 3;
+       else
+               data->disc_thresh = 2;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
+       data->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(data->base))
+               return PTR_ERR(data->base);
+
+       data->clk = devm_clk_get(dev, "usb_phy");
+       if (IS_ERR(data->clk)) {
+               dev_err(dev, "could not get usb_phy clock\n");
+               return PTR_ERR(data->clk);
+       }
+
+       /* Skip 0, 0 is the phy for otg which is not yet supported. */
+       for (i = 1; i < data->num_phys; i++) {
+               snprintf(name, sizeof(name), "usb%d_vbus", i);
+               vbus = devm_regulator_get_optional(dev, name);
+               if (IS_ERR(vbus)) {
+                       if (PTR_ERR(vbus) == -EPROBE_DEFER)
+                               return -EPROBE_DEFER;
+                       vbus = NULL;
+               }
+
+               snprintf(name, sizeof(name), "usb%d_reset", i);
+               reset = devm_reset_control_get(dev, name);
+               if (IS_ERR(reset)) {
+                       dev_err(dev, "failed to get reset %s\n", name);
+                       return PTR_ERR(reset);
+               }
+
+               if (i) { /* No pmu for usbc0 */
+                       snprintf(name, sizeof(name), "pmu%d", i);
+                       res = platform_get_resource_byname(pdev,
+                                                       IORESOURCE_MEM, name);
+                       pmu = devm_ioremap_resource(dev, res);
+                       if (IS_ERR(pmu))
+                               return PTR_ERR(pmu);
+               }
+
+               phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", i);
+                       return PTR_ERR(phy);
+               }
+
+               data->phys[i].phy = phy;
+               data->phys[i].pmu = pmu;
+               data->phys[i].vbus = vbus;
+               data->phys[i].reset = reset;
+               data->phys[i].index = i;
+               phy_set_drvdata(phy, &data->phys[i]);
+       }
+
+       dev_set_drvdata(dev, data);
+       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       return 0;
+}
+
+static const struct of_device_id sun4i_usb_phy_of_match[] = {
+       { .compatible = "allwinner,sun4i-a10-usb-phy" },
+       { .compatible = "allwinner,sun5i-a13-usb-phy" },
+       { .compatible = "allwinner,sun7i-a20-usb-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
+
+static struct platform_driver sun4i_usb_phy_driver = {
+       .probe  = sun4i_usb_phy_probe,
+       .driver = {
+               .of_match_table = sun4i_usb_phy_of_match,
+               .name  = "sun4i-usb-phy",
+               .owner = THIS_MODULE,
+       }
+};
+module_platform_driver(sun4i_usb_phy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
new file mode 100644 (file)
index 0000000..5913676
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * phy-ti-pipe3 - PIPE3 PHY driver.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/phy/phy.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/phy/omap_control_phy.h>
+#include <linux/of_platform.h>
+
+#define        PLL_STATUS              0x00000004
+#define        PLL_GO                  0x00000008
+#define        PLL_CONFIGURATION1      0x0000000C
+#define        PLL_CONFIGURATION2      0x00000010
+#define        PLL_CONFIGURATION3      0x00000014
+#define        PLL_CONFIGURATION4      0x00000020
+
+#define        PLL_REGM_MASK           0x001FFE00
+#define        PLL_REGM_SHIFT          0x9
+#define        PLL_REGM_F_MASK         0x0003FFFF
+#define        PLL_REGM_F_SHIFT        0x0
+#define        PLL_REGN_MASK           0x000001FE
+#define        PLL_REGN_SHIFT          0x1
+#define        PLL_SELFREQDCO_MASK     0x0000000E
+#define        PLL_SELFREQDCO_SHIFT    0x1
+#define        PLL_SD_MASK             0x0003FC00
+#define        PLL_SD_SHIFT            10
+#define        SET_PLL_GO              0x1
+#define PLL_LDOPWDN            BIT(15)
+#define PLL_TICOPWDN           BIT(16)
+#define        PLL_LOCK                0x2
+#define        PLL_IDLE                0x1
+
+/*
+ * This is an Empirical value that works, need to confirm the actual
+ * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
+ * to be correctly reflected in the PIPE3PHY_PLL_STATUS register.
+ */
+#define PLL_IDLE_TIME  100     /* in milliseconds */
+#define PLL_LOCK_TIME  100     /* in milliseconds */
+
+struct pipe3_dpll_params {
+       u16     m;
+       u8      n;
+       u8      freq:3;
+       u8      sd;
+       u32     mf;
+};
+
+struct pipe3_dpll_map {
+       unsigned long rate;
+       struct pipe3_dpll_params params;
+};
+
+struct ti_pipe3 {
+       void __iomem            *pll_ctrl_base;
+       struct device           *dev;
+       struct device           *control_dev;
+       struct clk              *wkupclk;
+       struct clk              *sys_clk;
+       struct clk              *refclk;
+       struct pipe3_dpll_map   *dpll_map;
+};
+
+static struct pipe3_dpll_map dpll_map_usb[] = {
+       {12000000, {1250, 5, 4, 20, 0} },       /* 12 MHz */
+       {16800000, {3125, 20, 4, 20, 0} },      /* 16.8 MHz */
+       {19200000, {1172, 8, 4, 20, 65537} },   /* 19.2 MHz */
+       {20000000, {1000, 7, 4, 10, 0} },       /* 20 MHz */
+       {26000000, {1250, 12, 4, 20, 0} },      /* 26 MHz */
+       {38400000, {3125, 47, 4, 20, 92843} },  /* 38.4 MHz */
+       { },                                    /* Terminator */
+};
+
+static struct pipe3_dpll_map dpll_map_sata[] = {
+       {12000000, {1000, 7, 4, 6, 0} },        /* 12 MHz */
+       {16800000, {714, 7, 4, 6, 0} },         /* 16.8 MHz */
+       {19200000, {625, 7, 4, 6, 0} },         /* 19.2 MHz */
+       {20000000, {600, 7, 4, 6, 0} },         /* 20 MHz */
+       {26000000, {461, 7, 4, 6, 0} },         /* 26 MHz */
+       {38400000, {312, 7, 4, 6, 0} },         /* 38.4 MHz */
+       { },                                    /* Terminator */
+};
+
+static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset)
+{
+       return __raw_readl(addr + offset);
+}
+
+static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset,
+       u32 data)
+{
+       __raw_writel(data, addr + offset);
+}
+
+static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy)
+{
+       unsigned long rate;
+       struct pipe3_dpll_map *dpll_map = phy->dpll_map;
+
+       rate = clk_get_rate(phy->sys_clk);
+
+       for (; dpll_map->rate; dpll_map++) {
+               if (rate == dpll_map->rate)
+                       return &dpll_map->params;
+       }
+
+       dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate);
+
+       return NULL;
+}
+
+static int ti_pipe3_power_off(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+
+       omap_control_phy_power(phy->control_dev, 0);
+
+       return 0;
+}
+
+static int ti_pipe3_power_on(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+
+       omap_control_phy_power(phy->control_dev, 1);
+
+       return 0;
+}
+
+static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
+{
+       u32             val;
+       unsigned long   timeout;
+
+       timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME);
+       do {
+               cpu_relax();
+               val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
+               if (val & PLL_LOCK)
+                       break;
+       } while (!time_after(jiffies, timeout));
+
+       if (!(val & PLL_LOCK)) {
+               dev_err(phy->dev, "DPLL failed to lock\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
+{
+       u32                     val;
+       struct pipe3_dpll_params *dpll_params;
+
+       dpll_params = ti_pipe3_get_dpll_params(phy);
+       if (!dpll_params)
+               return -EINVAL;
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
+       val &= ~PLL_REGN_MASK;
+       val |= dpll_params->n << PLL_REGN_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+       val &= ~PLL_SELFREQDCO_MASK;
+       val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
+       val &= ~PLL_REGM_MASK;
+       val |= dpll_params->m << PLL_REGM_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
+       val &= ~PLL_REGM_F_MASK;
+       val |= dpll_params->mf << PLL_REGM_F_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
+       val &= ~PLL_SD_MASK;
+       val |= dpll_params->sd << PLL_SD_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
+
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
+
+       return ti_pipe3_dpll_wait_lock(phy);
+}
+
+static int ti_pipe3_init(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+       u32 val;
+       int ret = 0;
+
+       /* Bring it out of IDLE if it is IDLE */
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+       if (val & PLL_IDLE) {
+               val &= ~PLL_IDLE;
+               ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+               ret = ti_pipe3_dpll_wait_lock(phy);
+       }
+
+       /* Program the DPLL only if not locked */
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
+       if (!(val & PLL_LOCK))
+               if (ti_pipe3_dpll_program(phy))
+                       return -EINVAL;
+
+       return ret;
+}
+
+static int ti_pipe3_exit(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+       u32 val;
+       unsigned long timeout;
+
+       /* SATA DPLL can't be powered down due to Errata i783 */
+       if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
+               return 0;
+
+       /* Put DPLL in IDLE mode */
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+       val |= PLL_IDLE;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+       /* wait for LDO and Oscillator to power down */
+       timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME);
+       do {
+               cpu_relax();
+               val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
+               if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
+                       break;
+       } while (!time_after(jiffies, timeout));
+
+       if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
+               dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n",
+                       val);
+               return -EBUSY;
+       }
+
+       return 0;
+}
+static struct phy_ops ops = {
+       .init           = ti_pipe3_init,
+       .exit           = ti_pipe3_exit,
+       .power_on       = ti_pipe3_power_on,
+       .power_off      = ti_pipe3_power_off,
+       .owner          = THIS_MODULE,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id ti_pipe3_id_table[];
+#endif
+
+static int ti_pipe3_probe(struct platform_device *pdev)
+{
+       struct ti_pipe3 *phy;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       struct resource *res;
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *control_node;
+       struct platform_device *control_pdev;
+       const struct of_device_id *match;
+
+       match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev);
+       if (!match)
+               return -EINVAL;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
+               return -ENOMEM;
+       }
+
+       phy->dpll_map = (struct pipe3_dpll_map *)match->data;
+       if (!phy->dpll_map) {
+               dev_err(&pdev->dev, "no DPLL data\n");
+               return -EINVAL;
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
+       phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(phy->pll_ctrl_base))
+               return PTR_ERR(phy->pll_ctrl_base);
+
+       phy->dev                = &pdev->dev;
+
+       if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
+
+               phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
+               if (IS_ERR(phy->wkupclk)) {
+                       dev_err(&pdev->dev, "unable to get wkupclk\n");
+                       return PTR_ERR(phy->wkupclk);
+               }
+
+               phy->refclk = devm_clk_get(phy->dev, "refclk");
+               if (IS_ERR(phy->refclk)) {
+                       dev_err(&pdev->dev, "unable to get refclk\n");
+                       return PTR_ERR(phy->refclk);
+               }
+       } else {
+               phy->wkupclk = ERR_PTR(-ENODEV);
+               phy->refclk = ERR_PTR(-ENODEV);
+       }
+
+       phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
+       if (IS_ERR(phy->sys_clk)) {
+               dev_err(&pdev->dev, "unable to get sysclk\n");
+               return -EINVAL;
+       }
+
+       control_node = of_parse_phandle(node, "ctrl-module", 0);
+       if (!control_node) {
+               dev_err(&pdev->dev, "Failed to get control device phandle\n");
+               return -EINVAL;
+       }
+
+       control_pdev = of_find_device_by_node(control_node);
+       if (!control_pdev) {
+               dev_err(&pdev->dev, "Failed to get control device\n");
+               return -EINVAL;
+       }
+
+       phy->control_dev = &control_pdev->dev;
+
+       omap_control_phy_power(phy->control_dev, 0);
+
+       platform_set_drvdata(pdev, phy);
+       pm_runtime_enable(phy->dev);
+
+       generic_phy = devm_phy_create(phy->dev, &ops, NULL);
+       if (IS_ERR(generic_phy))
+               return PTR_ERR(generic_phy);
+
+       phy_set_drvdata(generic_phy, phy);
+       phy_provider = devm_of_phy_provider_register(phy->dev,
+                       of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       pm_runtime_get(&pdev->dev);
+
+       return 0;
+}
+
+static int ti_pipe3_remove(struct platform_device *pdev)
+{
+       if (!pm_runtime_suspended(&pdev->dev))
+               pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int ti_pipe3_runtime_suspend(struct device *dev)
+{
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+       if (!IS_ERR(phy->wkupclk))
+               clk_disable_unprepare(phy->wkupclk);
+       if (!IS_ERR(phy->refclk))
+               clk_disable_unprepare(phy->refclk);
+
+       return 0;
+}
+
+static int ti_pipe3_runtime_resume(struct device *dev)
+{
+       u32 ret = 0;
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+       if (!IS_ERR(phy->refclk)) {
+               ret = clk_prepare_enable(phy->refclk);
+               if (ret) {
+                       dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
+                       goto err1;
+               }
+       }
+
+       if (!IS_ERR(phy->wkupclk)) {
+               ret = clk_prepare_enable(phy->wkupclk);
+               if (ret) {
+                       dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+                       goto err2;
+               }
+       }
+
+       return 0;
+
+err2:
+       if (!IS_ERR(phy->refclk))
+               clk_disable_unprepare(phy->refclk);
+
+err1:
+       return ret;
+}
+
+static const struct dev_pm_ops ti_pipe3_pm_ops = {
+       SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
+                          ti_pipe3_runtime_resume, NULL)
+};
+
+#define DEV_PM_OPS     (&ti_pipe3_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id ti_pipe3_id_table[] = {
+       {
+               .compatible = "ti,phy-usb3",
+               .data = dpll_map_usb,
+       },
+       {
+               .compatible = "ti,omap-usb3",
+               .data = dpll_map_usb,
+       },
+       {
+               .compatible = "ti,phy-pipe3-sata",
+               .data = dpll_map_sata,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
+#endif
+
+static struct platform_driver ti_pipe3_driver = {
+       .probe          = ti_pipe3_probe,
+       .remove         = ti_pipe3_remove,
+       .driver         = {
+               .name   = "ti-pipe3",
+               .owner  = THIS_MODULE,
+               .pm     = DEV_PM_OPS,
+               .of_match_table = of_match_ptr(ti_pipe3_id_table),
+       },
+};
+
+module_platform_driver(ti_pipe3_driver);
+
+MODULE_ALIAS("platform: ti_pipe3");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TI PIPE3 phy driver");
+MODULE_LICENSE("GPL v2");
index c3ace1db8136eedef379691bc6fa532b4ff5d67c..2e0e9b3774c88d7501462466dd858d3a3bf7edd0 100644 (file)
@@ -338,7 +338,7 @@ static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
                dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
                                mode);
                break;
-       };
+       }
 }
 
 static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
@@ -661,7 +661,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        struct phy_provider     *phy_provider;
        struct phy_init_data    *init_data = NULL;
 
-       twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
+       twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
        if (!twl)
                return -ENOMEM;
 
@@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
+       otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
        if (!otg)
                return -ENOMEM;
 
diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
new file mode 100644 (file)
index 0000000..4aa1ccd
--- /dev/null
@@ -0,0 +1,1750 @@
+/*
+ * AppliedMicro X-Gene Multi-purpose PHY driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Loc Ho <lho@apm.com>
+ *         Tuan Phan <tphan@apm.com>
+ *         Suman Tripathi <stripathi@apm.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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The APM X-Gene PHY consists of two PLL clock macro's (CMU) and lanes.
+ * The first PLL clock macro is used for internal reference clock. The second
+ * PLL clock macro is used to generate the clock for the PHY. This driver
+ * configures the first PLL CMU, the second PLL CMU, and programs the PHY to
+ * operate according to the mode of operation. The first PLL CMU is only
+ * required if internal clock is enabled.
+ *
+ * Logical Layer Out Of HW module units:
+ *
+ * -----------------
+ * | Internal      |    |------|
+ * | Ref PLL CMU   |----|      |     -------------    ---------
+ * ------------ ----    | MUX  |-----|PHY PLL CMU|----| Serdes|
+ *                      |      |     |           |    ---------
+ * External Clock ------|      |     -------------
+ *                      |------|
+ *
+ * The Ref PLL CMU CSR (Configuration System Registers) is accessed
+ * indirectly from the SDS offset at 0x2000. It is only required for
+ * internal reference clock.
+ * The PHY PLL CMU CSR is accessed indirectly from the SDS offset at 0x0000.
+ * The Serdes CSR is accessed indirectly from the SDS offset at 0x0400.
+ *
+ * The Ref PLL CMU can be located within the same PHY IP or outside the PHY IP
+ * due to shared Ref PLL CMU. For PHY with Ref PLL CMU shared with another IP,
+ * it is located outside the PHY IP. This is the case for the PHY located
+ * at 0x1f23a000 (SATA Port 4/5). For such PHY, another resource is required
+ * to located the SDS/Ref PLL CMU module and its clock for that IP enabled.
+ *
+ * Currently, this driver only supports Gen3 SATA mode with external clock.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/phy/phy.h>
+#include <linux/clk.h>
+
+/* Max 2 lanes per a PHY unit */
+#define MAX_LANE                       2
+
+/* Register offset inside the PHY */
+#define SERDES_PLL_INDIRECT_OFFSET     0x0000
+#define SERDES_PLL_REF_INDIRECT_OFFSET 0x2000
+#define SERDES_INDIRECT_OFFSET         0x0400
+#define SERDES_LANE_STRIDE             0x0200
+
+/* Some default Serdes parameters */
+#define DEFAULT_SATA_TXBOOST_GAIN      { 0x1e, 0x1e, 0x1e }
+#define DEFAULT_SATA_TXEYEDIRECTION    { 0x0, 0x0, 0x0 }
+#define DEFAULT_SATA_TXEYETUNING       { 0xa, 0xa, 0xa }
+#define DEFAULT_SATA_SPD_SEL           { 0x1, 0x3, 0x7 }
+#define DEFAULT_SATA_TXAMP             { 0x8, 0x8, 0x8 }
+#define DEFAULT_SATA_TXCN1             { 0x2, 0x2, 0x2 }
+#define DEFAULT_SATA_TXCN2             { 0x0, 0x0, 0x0 }
+#define DEFAULT_SATA_TXCP1             { 0xa, 0xa, 0xa }
+
+#define SATA_SPD_SEL_GEN3              0x7
+#define SATA_SPD_SEL_GEN2              0x3
+#define SATA_SPD_SEL_GEN1              0x1
+
+#define SSC_DISABLE                    0
+#define SSC_ENABLE                     1
+
+#define FBDIV_VAL_50M                  0x77
+#define REFDIV_VAL_50M                 0x1
+#define FBDIV_VAL_100M                 0x3B
+#define REFDIV_VAL_100M                        0x0
+
+/* SATA Clock/Reset CSR */
+#define SATACLKENREG                   0x00000000
+#define  SATA0_CORE_CLKEN              0x00000002
+#define  SATA1_CORE_CLKEN              0x00000004
+#define SATASRESETREG                  0x00000004
+#define  SATA_MEM_RESET_MASK           0x00000020
+#define  SATA_MEM_RESET_RD(src)                (((src) & 0x00000020) >> 5)
+#define  SATA_SDS_RESET_MASK           0x00000004
+#define  SATA_CSR_RESET_MASK           0x00000001
+#define  SATA_CORE_RESET_MASK          0x00000002
+#define  SATA_PMCLK_RESET_MASK         0x00000010
+#define  SATA_PCLK_RESET_MASK          0x00000008
+
+/* SDS CSR used for PHY Indirect access */
+#define SATA_ENET_SDS_PCS_CTL0         0x00000000
+#define  REGSPEC_CFG_I_TX_WORDMODE0_SET(dst, src) \
+               (((dst) & ~0x00070000) | (((u32) (src) << 16) & 0x00070000))
+#define  REGSPEC_CFG_I_RX_WORDMODE0_SET(dst, src) \
+               (((dst) & ~0x00e00000) | (((u32) (src) << 21) & 0x00e00000))
+#define SATA_ENET_SDS_CTL0             0x0000000c
+#define  REGSPEC_CFG_I_CUSTOMER_PIN_MODE0_SET(dst, src) \
+               (((dst) & ~0x00007fff) | (((u32) (src)) & 0x00007fff))
+#define SATA_ENET_SDS_CTL1             0x00000010
+#define  CFG_I_SPD_SEL_CDR_OVR1_SET(dst, src) \
+               (((dst) & ~0x0000000f) | (((u32) (src)) & 0x0000000f))
+#define SATA_ENET_SDS_RST_CTL          0x00000024
+#define SATA_ENET_SDS_IND_CMD_REG      0x0000003c
+#define  CFG_IND_WR_CMD_MASK           0x00000001
+#define  CFG_IND_RD_CMD_MASK           0x00000002
+#define  CFG_IND_CMD_DONE_MASK         0x00000004
+#define  CFG_IND_ADDR_SET(dst, src) \
+               (((dst) & ~0x003ffff0) | (((u32) (src) << 4) & 0x003ffff0))
+#define SATA_ENET_SDS_IND_RDATA_REG    0x00000040
+#define SATA_ENET_SDS_IND_WDATA_REG    0x00000044
+#define SATA_ENET_CLK_MACRO_REG                0x0000004c
+#define  I_RESET_B_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src)) & 0x00000001))
+#define  I_PLL_FBDIV_SET(dst, src) \
+               (((dst) & ~0x001ff000) | (((u32) (src) << 12) & 0x001ff000))
+#define  I_CUSTOMEROV_SET(dst, src) \
+               (((dst) & ~0x00000f80) | (((u32) (src) << 7) & 0x00000f80))
+#define  O_PLL_LOCK_RD(src)            (((src) & 0x40000000) >> 30)
+#define  O_PLL_READY_RD(src)           (((src) & 0x80000000) >> 31)
+
+/* PLL Clock Macro Unit (CMU) CSR accessing from SDS indirectly */
+#define CMU_REG0                       0x00000
+#define  CMU_REG0_PLL_REF_SEL_MASK     0x00002000
+#define  CMU_REG0_PLL_REF_SEL_SET(dst, src)    \
+               (((dst) & ~0x00002000) | (((u32) (src) << 13) & 0x00002000))
+#define  CMU_REG0_PDOWN_MASK           0x00004000
+#define  CMU_REG0_CAL_COUNT_RESOL_SET(dst, src) \
+               (((dst) & ~0x000000e0) | (((u32) (src) << 5) & 0x000000e0))
+#define CMU_REG1                       0x00002
+#define  CMU_REG1_PLL_CP_SET(dst, src) \
+               (((dst) & ~0x00003c00) | (((u32) (src) << 10) & 0x00003c00))
+#define  CMU_REG1_PLL_MANUALCAL_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  CMU_REG1_PLL_CP_SEL_SET(dst, src) \
+               (((dst) & ~0x000003e0) | (((u32) (src) << 5) & 0x000003e0))
+#define  CMU_REG1_REFCLK_CMOS_SEL_MASK 0x00000001
+#define  CMU_REG1_REFCLK_CMOS_SEL_SET(dst, src)        \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG2                       0x00004
+#define  CMU_REG2_PLL_REFDIV_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  CMU_REG2_PLL_LFRES_SET(dst, src) \
+               (((dst) & ~0x0000001e) | (((u32) (src) << 1) & 0x0000001e))
+#define  CMU_REG2_PLL_FBDIV_SET(dst, src) \
+               (((dst) & ~0x00003fe0) | (((u32) (src) << 5) & 0x00003fe0))
+#define CMU_REG3                       0x00006
+#define  CMU_REG3_VCOVARSEL_SET(dst, src) \
+               (((dst) & ~0x0000000f) | (((u32) (src) << 0) & 0x0000000f))
+#define  CMU_REG3_VCO_MOMSEL_INIT_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define  CMU_REG3_VCO_MANMOMSEL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define CMU_REG4                       0x00008
+#define CMU_REG5                       0x0000a
+#define  CMU_REG5_PLL_LFSMCAP_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  CMU_REG5_PLL_LOCK_RESOLUTION_SET(dst, src) \
+               (((dst) & ~0x0000000e) | (((u32) (src) << 1) & 0x0000000e))
+#define  CMU_REG5_PLL_LFCAP_SET(dst, src) \
+               (((dst) & ~0x00003000) | (((u32) (src) << 12) & 0x00003000))
+#define  CMU_REG5_PLL_RESETB_MASK      0x00000001
+#define CMU_REG6                       0x0000c
+#define  CMU_REG6_PLL_VREGTRIM_SET(dst, src) \
+               (((dst) & ~0x00000600) | (((u32) (src) << 9) & 0x00000600))
+#define  CMU_REG6_MAN_PVT_CAL_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define CMU_REG7                       0x0000e
+#define  CMU_REG7_PLL_CALIB_DONE_RD(src) ((0x00004000 & (u32) (src)) >> 14)
+#define  CMU_REG7_VCO_CAL_FAIL_RD(src) ((0x00000c00 & (u32) (src)) >> 10)
+#define CMU_REG8                       0x00010
+#define CMU_REG9                       0x00012
+#define  CMU_REG9_WORD_LEN_8BIT                0x000
+#define  CMU_REG9_WORD_LEN_10BIT       0x001
+#define  CMU_REG9_WORD_LEN_16BIT       0x002
+#define  CMU_REG9_WORD_LEN_20BIT       0x003
+#define  CMU_REG9_WORD_LEN_32BIT       0x004
+#define  CMU_REG9_WORD_LEN_40BIT       0x005
+#define  CMU_REG9_WORD_LEN_64BIT       0x006
+#define  CMU_REG9_WORD_LEN_66BIT       0x007
+#define  CMU_REG9_TX_WORD_MODE_CH1_SET(dst, src) \
+               (((dst) & ~0x00000380) | (((u32) (src) << 7) & 0x00000380))
+#define  CMU_REG9_TX_WORD_MODE_CH0_SET(dst, src) \
+               (((dst) & ~0x00000070) | (((u32) (src) << 4) & 0x00000070))
+#define  CMU_REG9_PLL_POST_DIVBY2_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  CMU_REG9_VBG_BYPASSB_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define  CMU_REG9_IGEN_BYPASS_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define CMU_REG10                      0x00014
+#define  CMU_REG10_VREG_REFSEL_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG11                      0x00016
+#define CMU_REG12                      0x00018
+#define  CMU_REG12_STATE_DELAY9_SET(dst, src) \
+               (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define CMU_REG13                      0x0001a
+#define CMU_REG14                      0x0001c
+#define CMU_REG15                      0x0001e
+#define CMU_REG16                      0x00020
+#define  CMU_REG16_PVT_DN_MAN_ENA_MASK 0x00000001
+#define  CMU_REG16_PVT_UP_MAN_ENA_MASK 0x00000002
+#define  CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(dst, src) \
+               (((dst) & ~0x0000001c) | (((u32) (src) << 2) & 0x0000001c))
+#define  CMU_REG16_CALIBRATION_DONE_OVERRIDE_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define  CMU_REG16_BYPASS_PLL_LOCK_SET(dst, src) \
+               (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define CMU_REG17                      0x00022
+#define  CMU_REG17_PVT_CODE_R2A_SET(dst, src) \
+               (((dst) & ~0x00007f00) | (((u32) (src) << 8) & 0x00007f00))
+#define  CMU_REG17_RESERVED_7_SET(dst, src) \
+               (((dst) & ~0x000000e0) | (((u32) (src) << 5) & 0x000000e0))
+#define  CMU_REG17_PVT_TERM_MAN_ENA_MASK       0x00008000
+#define CMU_REG18                      0x00024
+#define CMU_REG19                      0x00026
+#define CMU_REG20                      0x00028
+#define CMU_REG21                      0x0002a
+#define CMU_REG22                      0x0002c
+#define CMU_REG23                      0x0002e
+#define CMU_REG24                      0x00030
+#define CMU_REG25                      0x00032
+#define CMU_REG26                      0x00034
+#define  CMU_REG26_FORCE_PLL_LOCK_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG27                      0x00036
+#define CMU_REG28                      0x00038
+#define CMU_REG29                      0x0003a
+#define CMU_REG30                      0x0003c
+#define  CMU_REG30_LOCK_COUNT_SET(dst, src) \
+               (((dst) & ~0x00000006) | (((u32) (src) << 1) & 0x00000006))
+#define  CMU_REG30_PCIE_MODE_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define CMU_REG31                      0x0003e
+#define CMU_REG32                      0x00040
+#define  CMU_REG32_FORCE_VCOCAL_START_MASK     0x00004000
+#define  CMU_REG32_PVT_CAL_WAIT_SEL_SET(dst, src) \
+               (((dst) & ~0x00000006) | (((u32) (src) << 1) & 0x00000006))
+#define  CMU_REG32_IREF_ADJ_SET(dst, src) \
+               (((dst) & ~0x00000180) | (((u32) (src) << 7) & 0x00000180))
+#define CMU_REG33                      0x00042
+#define CMU_REG34                      0x00044
+#define  CMU_REG34_VCO_CAL_VTH_LO_MAX_SET(dst, src) \
+               (((dst) & ~0x0000000f) | (((u32) (src) << 0) & 0x0000000f))
+#define  CMU_REG34_VCO_CAL_VTH_HI_MAX_SET(dst, src) \
+               (((dst) & ~0x00000f00) | (((u32) (src) << 8) & 0x00000f00))
+#define  CMU_REG34_VCO_CAL_VTH_LO_MIN_SET(dst, src) \
+               (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define  CMU_REG34_VCO_CAL_VTH_HI_MIN_SET(dst, src) \
+               (((dst) & ~0x0000f000) | (((u32) (src) << 12) & 0x0000f000))
+#define CMU_REG35                      0x00046
+#define  CMU_REG35_PLL_SSC_MOD_SET(dst, src) \
+               (((dst) & ~0x0000fe00) | (((u32) (src) << 9) & 0x0000fe00))
+#define CMU_REG36                              0x00048
+#define  CMU_REG36_PLL_SSC_EN_SET(dst, src) \
+               (((dst) & ~0x00000010) | (((u32) (src) << 4) & 0x00000010))
+#define  CMU_REG36_PLL_SSC_VSTEP_SET(dst, src) \
+               (((dst) & ~0x0000ffc0) | (((u32) (src) << 6) & 0x0000ffc0))
+#define  CMU_REG36_PLL_SSC_DSMSEL_SET(dst, src) \
+               (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define CMU_REG37                      0x0004a
+#define CMU_REG38                      0x0004c
+#define CMU_REG39                      0x0004e
+
+/* PHY lane CSR accessing from SDS indirectly */
+#define RXTX_REG0                      0x000
+#define  RXTX_REG0_CTLE_EQ_HR_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG0_CTLE_EQ_QR_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG0_CTLE_EQ_FR_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG1                      0x002
+#define  RXTX_REG1_RXACVCM_SET(dst, src) \
+               (((dst) & ~0x0000f000) | (((u32) (src) << 12) & 0x0000f000))
+#define  RXTX_REG1_CTLE_EQ_SET(dst, src) \
+               (((dst) & ~0x00000f80) | (((u32) (src) << 7) & 0x00000f80))
+#define  RXTX_REG1_RXVREG1_SET(dst, src) \
+               (((dst) & ~0x00000060) | (((u32) (src) << 5) & 0x00000060))
+#define  RXTX_REG1_RXIREF_ADJ_SET(dst, src) \
+               (((dst) & ~0x00000006) | (((u32) (src) << 1) &  0x00000006))
+#define RXTX_REG2                      0x004
+#define  RXTX_REG2_VTT_ENA_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define  RXTX_REG2_TX_FIFO_ENA_SET(dst, src) \
+               (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define  RXTX_REG2_VTT_SEL_SET(dst, src) \
+               (((dst) & ~0x000000c0) | (((u32) (src) << 6) & 0x000000c0))
+#define RXTX_REG4                      0x008
+#define  RXTX_REG4_TX_LOOPBACK_BUF_EN_MASK     0x00000040
+#define  RXTX_REG4_TX_DATA_RATE_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  RXTX_REG4_TX_WORD_MODE_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG5                      0x00a
+#define  RXTX_REG5_TX_CN1_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG5_TX_CP1_SET(dst, src) \
+               (((dst) & ~0x000007e0) | (((u32) (src) << 5) & 0x000007e0))
+#define  RXTX_REG5_TX_CN2_SET(dst, src) \
+               (((dst) & ~0x0000001f) | (((u32) (src) << 0) & 0x0000001f))
+#define RXTX_REG6                      0x00c
+#define  RXTX_REG6_TXAMP_CNTL_SET(dst, src) \
+               (((dst) & ~0x00000780) | (((u32) (src) << 7) & 0x00000780))
+#define  RXTX_REG6_TXAMP_ENA_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define  RXTX_REG6_RX_BIST_ERRCNT_RD_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define  RXTX_REG6_TX_IDLE_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  RXTX_REG6_RX_BIST_RESYNC_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG7                      0x00e
+#define  RXTX_REG7_RESETB_RXD_MASK     0x00000100
+#define  RXTX_REG7_RESETB_RXA_MASK     0x00000080
+#define  RXTX_REG7_BIST_ENA_RX_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define  RXTX_REG7_RX_WORD_MODE_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG8                      0x010
+#define  RXTX_REG8_CDR_LOOP_ENA_SET(dst, src) \
+               (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
+#define  RXTX_REG8_CDR_BYPASS_RXLOS_SET(dst, src) \
+               (((dst) & ~0x00000800) | (((u32) (src) << 11) & 0x00000800))
+#define  RXTX_REG8_SSC_ENABLE_SET(dst, src) \
+               (((dst) & ~0x00000200) | (((u32) (src) << 9) & 0x00000200))
+#define  RXTX_REG8_SD_VREF_SET(dst, src) \
+               (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define  RXTX_REG8_SD_DISABLE_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define RXTX_REG7                      0x00e
+#define  RXTX_REG7_RESETB_RXD_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define  RXTX_REG7_RESETB_RXA_SET(dst, src) \
+               (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
+#define  RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK     0x00004000
+#define  RXTX_REG7_LOOP_BACK_ENA_CTLE_SET(dst, src) \
+               (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
+#define RXTX_REG11                     0x016
+#define  RXTX_REG11_PHASE_ADJUST_LIMIT_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG12                     0x018
+#define  RXTX_REG12_LATCH_OFF_ENA_SET(dst, src) \
+               (((dst) & ~0x00002000) | (((u32) (src) << 13) & 0x00002000))
+#define  RXTX_REG12_SUMOS_ENABLE_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define  RXTX_REG12_RX_DET_TERM_ENABLE_MASK    0x00000002
+#define  RXTX_REG12_RX_DET_TERM_ENABLE_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG13                     0x01a
+#define RXTX_REG14                     0x01c
+#define  RXTX_REG14_CLTE_LATCAL_MAN_PROG_SET(dst, src) \
+               (((dst) & ~0x0000003f) | (((u32) (src) << 0) & 0x0000003f))
+#define  RXTX_REG14_CTLE_LATCAL_MAN_ENA_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define RXTX_REG26                     0x034
+#define  RXTX_REG26_PERIOD_ERROR_LATCH_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define  RXTX_REG26_BLWC_ENA_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define RXTX_REG21                     0x02a
+#define  RXTX_REG21_DO_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG21_XO_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define  RXTX_REG21_LATCH_CAL_FAIL_ODD_RD(src) ((0x0000000f & (u32)(src)))
+#define RXTX_REG22                     0x02c
+#define  RXTX_REG22_SO_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define  RXTX_REG22_EO_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG22_LATCH_CAL_FAIL_EVEN_RD(src)        ((0x0000000f & (u32)(src)))
+#define RXTX_REG23                     0x02e
+#define  RXTX_REG23_DE_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG23_XE_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG24                     0x030
+#define  RXTX_REG24_EE_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG24_SE_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG27                     0x036
+#define RXTX_REG28                     0x038
+#define RXTX_REG31                     0x03e
+#define RXTX_REG38                     0x04c
+#define  RXTX_REG38_CUSTOMER_PINMODE_INV_SET(dst, src) \
+               (((dst) & 0x0000fffe) | (((u32) (src) << 1) & 0x0000fffe))
+#define RXTX_REG39                     0x04e
+#define RXTX_REG40                     0x050
+#define RXTX_REG41                     0x052
+#define RXTX_REG42                     0x054
+#define RXTX_REG43                     0x056
+#define RXTX_REG44                     0x058
+#define RXTX_REG45                     0x05a
+#define RXTX_REG46                     0x05c
+#define RXTX_REG47                     0x05e
+#define RXTX_REG48                     0x060
+#define RXTX_REG49                     0x062
+#define RXTX_REG50                     0x064
+#define RXTX_REG51                     0x066
+#define RXTX_REG52                     0x068
+#define RXTX_REG53                     0x06a
+#define RXTX_REG54                     0x06c
+#define RXTX_REG55                     0x06e
+#define RXTX_REG61                     0x07a
+#define  RXTX_REG61_ISCAN_INBERT_SET(dst, src) \
+               (((dst) & ~0x00000010) | (((u32) (src) << 4) & 0x00000010))
+#define  RXTX_REG61_LOADFREQ_SHIFT_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  RXTX_REG61_EYE_COUNT_WIDTH_SEL_SET(dst, src) \
+               (((dst) & ~0x000000c0) | (((u32) (src) << 6) & 0x000000c0))
+#define  RXTX_REG61_SPD_SEL_CDR_SET(dst, src) \
+               (((dst) & ~0x00003c00) | (((u32) (src) << 10) & 0x00003c00))
+#define RXTX_REG62                     0x07c
+#define  RXTX_REG62_PERIOD_H1_QLATCH_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG81                     0x0a2
+#define  RXTX_REG89_MU_TH7_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG89_MU_TH8_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG89_MU_TH9_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG96                     0x0c0
+#define  RXTX_REG96_MU_FREQ1_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG96_MU_FREQ2_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG96_MU_FREQ3_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG99                     0x0c6
+#define  RXTX_REG99_MU_PHASE1_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG99_MU_PHASE2_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG99_MU_PHASE3_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG102                    0x0cc
+#define  RXTX_REG102_FREQLOOP_LIMIT_SET(dst, src) \
+               (((dst) & ~0x00000060) | (((u32) (src) << 5) & 0x00000060))
+#define RXTX_REG114                    0x0e4
+#define RXTX_REG121                    0x0f2
+#define  RXTX_REG121_SUMOS_CAL_CODE_RD(src) ((0x0000003e & (u32)(src)) >> 0x1)
+#define RXTX_REG125                    0x0fa
+#define  RXTX_REG125_PQ_REG_SET(dst, src) \
+               (((dst) & ~0x0000fe00) | (((u32) (src) << 9) & 0x0000fe00))
+#define  RXTX_REG125_SIGN_PQ_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define  RXTX_REG125_SIGN_PQ_2C_SET(dst, src) \
+               (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
+#define  RXTX_REG125_PHZ_MANUALCODE_SET(dst, src) \
+               (((dst) & ~0x0000007c) | (((u32) (src) << 2) & 0x0000007c))
+#define  RXTX_REG125_PHZ_MANUAL_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG127                    0x0fe
+#define  RXTX_REG127_FORCE_SUM_CAL_START_MASK  0x00000002
+#define  RXTX_REG127_FORCE_LAT_CAL_START_MASK  0x00000004
+#define  RXTX_REG127_FORCE_SUM_CAL_START_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define  RXTX_REG127_FORCE_LAT_CAL_START_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define  RXTX_REG127_LATCH_MAN_CAL_ENA_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  RXTX_REG127_DO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG127_XO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG128                    0x100
+#define  RXTX_REG128_LATCH_CAL_WAIT_SEL_SET(dst, src) \
+               (((dst) & ~0x0000000c) | (((u32) (src) << 2) & 0x0000000c))
+#define  RXTX_REG128_EO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG128_SO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG129                    0x102
+#define  RXTX_REG129_DE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG129_XE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG130                    0x104
+#define  RXTX_REG130_EE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG130_SE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG145                    0x122
+#define  RXTX_REG145_TX_IDLE_SATA_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define  RXTX_REG145_RXES_ENA_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define  RXTX_REG145_RXDFE_CONFIG_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  RXTX_REG145_RXVWES_LATENA_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define RXTX_REG147                    0x126
+#define RXTX_REG148                    0x128
+
+/* Clock macro type */
+enum cmu_type_t {
+       REF_CMU = 0,    /* Clock macro is the internal reference clock */
+       PHY_CMU = 1,    /* Clock macro is the PLL for the Serdes */
+};
+
+enum mux_type_t {
+       MUX_SELECT_ATA = 0,     /* Switch the MUX to ATA */
+       MUX_SELECT_SGMMII = 0,  /* Switch the MUX to SGMII */
+};
+
+enum clk_type_t {
+       CLK_EXT_DIFF = 0,       /* External differential */
+       CLK_INT_DIFF = 1,       /* Internal differential */
+       CLK_INT_SING = 2,       /* Internal single ended */
+};
+
+enum phy_mode {
+       MODE_SATA       = 0,    /* List them for simple reference */
+       MODE_SGMII      = 1,
+       MODE_PCIE       = 2,
+       MODE_USB        = 3,
+       MODE_XFI        = 4,
+       MODE_MAX
+};
+
+struct xgene_sata_override_param {
+       u32 speed[MAX_LANE]; /* Index for override parameter per lane */
+       u32 txspeed[3];                 /* Tx speed */
+       u32 txboostgain[MAX_LANE*3];    /* Tx freq boost and gain control */
+       u32 txeyetuning[MAX_LANE*3];    /* Tx eye tuning */
+       u32 txeyedirection[MAX_LANE*3]; /* Tx eye tuning direction */
+       u32 txamplitude[MAX_LANE*3];    /* Tx amplitude control */
+       u32 txprecursor_cn1[MAX_LANE*3]; /* Tx emphasis taps 1st pre-cursor */
+       u32 txprecursor_cn2[MAX_LANE*3]; /* Tx emphasis taps 2nd pre-cursor */
+       u32 txpostcursor_cp1[MAX_LANE*3]; /* Tx emphasis taps post-cursor */
+};
+
+struct xgene_phy_ctx {
+       struct device *dev;
+       struct phy *phy;
+       enum phy_mode mode;             /* Mode of operation */
+       enum clk_type_t clk_type;       /* Input clock selection */
+       void __iomem *sds_base;         /* PHY CSR base addr */
+       struct clk *clk;                /* Optional clock */
+
+       /* Override Serdes parameters */
+       struct xgene_sata_override_param sata_param;
+};
+
+/*
+ * For chip earlier than A3 version, enable this flag.
+ * To enable, pass boot argument phy_xgene.preA3Chip=1
+ */
+static int preA3Chip;
+MODULE_PARM_DESC(preA3Chip, "Enable pre-A3 chip support (1=enable 0=disable)");
+module_param_named(preA3Chip, preA3Chip, int, 0444);
+
+static void sds_wr(void __iomem *csr_base, u32 indirect_cmd_reg,
+                  u32 indirect_data_reg, u32 addr, u32 data)
+{
+       unsigned long deadline = jiffies + HZ;
+       u32 val;
+       u32 cmd;
+
+       cmd = CFG_IND_WR_CMD_MASK | CFG_IND_CMD_DONE_MASK;
+       cmd = CFG_IND_ADDR_SET(cmd, addr);
+       writel(data, csr_base + indirect_data_reg);
+       readl(csr_base + indirect_data_reg); /* Force a barrier */
+       writel(cmd, csr_base + indirect_cmd_reg);
+       readl(csr_base + indirect_cmd_reg); /* Force a barrier */
+       do {
+               val = readl(csr_base + indirect_cmd_reg);
+       } while (!(val & CFG_IND_CMD_DONE_MASK) &&
+                time_before(jiffies, deadline));
+       if (!(val & CFG_IND_CMD_DONE_MASK))
+               pr_err("SDS WR timeout at 0x%p offset 0x%08X value 0x%08X\n",
+                      csr_base + indirect_cmd_reg, addr, data);
+}
+
+static void sds_rd(void __iomem *csr_base, u32 indirect_cmd_reg,
+                  u32 indirect_data_reg, u32 addr, u32 *data)
+{
+       unsigned long deadline = jiffies + HZ;
+       u32 val;
+       u32 cmd;
+
+       cmd = CFG_IND_RD_CMD_MASK | CFG_IND_CMD_DONE_MASK;
+       cmd = CFG_IND_ADDR_SET(cmd, addr);
+       writel(cmd, csr_base + indirect_cmd_reg);
+       readl(csr_base + indirect_cmd_reg); /* Force a barrier */
+       do {
+               val = readl(csr_base + indirect_cmd_reg);
+       } while (!(val & CFG_IND_CMD_DONE_MASK) &&
+                time_before(jiffies, deadline));
+       *data = readl(csr_base + indirect_data_reg);
+       if (!(val & CFG_IND_CMD_DONE_MASK))
+               pr_err("SDS WR timeout at 0x%p offset 0x%08X value 0x%08X\n",
+                      csr_base + indirect_cmd_reg, addr, *data);
+}
+
+static void cmu_wr(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                  u32 reg, u32 data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+       u32 val;
+
+       if (cmu_type == REF_CMU)
+               reg += SERDES_PLL_REF_INDIRECT_OFFSET;
+       else
+               reg += SERDES_PLL_INDIRECT_OFFSET;
+       sds_wr(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+               SATA_ENET_SDS_IND_WDATA_REG, reg, data);
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+               SATA_ENET_SDS_IND_RDATA_REG, reg, &val);
+       pr_debug("CMU WR addr 0x%X value 0x%08X <-> 0x%08X\n", reg, data, val);
+}
+
+static void cmu_rd(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                  u32 reg, u32 *data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+
+       if (cmu_type == REF_CMU)
+               reg += SERDES_PLL_REF_INDIRECT_OFFSET;
+       else
+               reg += SERDES_PLL_INDIRECT_OFFSET;
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+               SATA_ENET_SDS_IND_RDATA_REG, reg, data);
+       pr_debug("CMU RD addr 0x%X value 0x%08X\n", reg, *data);
+}
+
+static void cmu_toggle1to0(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                          u32 reg, u32 bits)
+{
+       u32 val;
+
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val |= bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val &= ~bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void cmu_clrbits(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                       u32 reg, u32 bits)
+{
+       u32 val;
+
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val &= ~bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void cmu_setbits(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                       u32 reg, u32 bits)
+{
+       u32 val;
+
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val |= bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void serdes_wr(struct xgene_phy_ctx *ctx, int lane, u32 reg, u32 data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+       u32 val;
+
+       reg += SERDES_INDIRECT_OFFSET;
+       reg += lane * SERDES_LANE_STRIDE;
+       sds_wr(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+              SATA_ENET_SDS_IND_WDATA_REG, reg, data);
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+              SATA_ENET_SDS_IND_RDATA_REG, reg, &val);
+       pr_debug("SERDES WR addr 0x%X value 0x%08X <-> 0x%08X\n", reg, data,
+                val);
+}
+
+static void serdes_rd(struct xgene_phy_ctx *ctx, int lane, u32 reg, u32 *data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+
+       reg += SERDES_INDIRECT_OFFSET;
+       reg += lane * SERDES_LANE_STRIDE;
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+              SATA_ENET_SDS_IND_RDATA_REG, reg, data);
+       pr_debug("SERDES RD addr 0x%X value 0x%08X\n", reg, *data);
+}
+
+static void serdes_clrbits(struct xgene_phy_ctx *ctx, int lane, u32 reg,
+                          u32 bits)
+{
+       u32 val;
+
+       serdes_rd(ctx, lane, reg, &val);
+       val &= ~bits;
+       serdes_wr(ctx, lane, reg, val);
+}
+
+static void serdes_setbits(struct xgene_phy_ctx *ctx, int lane, u32 reg,
+                          u32 bits)
+{
+       u32 val;
+
+       serdes_rd(ctx, lane, reg, &val);
+       val |= bits;
+       serdes_wr(ctx, lane, reg, val);
+}
+
+static void xgene_phy_cfg_cmu_clk_type(struct xgene_phy_ctx *ctx,
+                                      enum cmu_type_t cmu_type,
+                                      enum clk_type_t clk_type)
+{
+       u32 val;
+
+       /* Set the reset sequence delay for TX ready assertion */
+       cmu_rd(ctx, cmu_type, CMU_REG12, &val);
+       val = CMU_REG12_STATE_DELAY9_SET(val, 0x1);
+       cmu_wr(ctx, cmu_type, CMU_REG12, val);
+       /* Set the programmable stage delays between various enable stages */
+       cmu_wr(ctx, cmu_type, CMU_REG13, 0x0222);
+       cmu_wr(ctx, cmu_type, CMU_REG14, 0x2225);
+
+       /* Configure clock type */
+       if (clk_type == CLK_EXT_DIFF) {
+               /* Select external clock mux */
+               cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+               val = CMU_REG0_PLL_REF_SEL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG0, val);
+               /* Select CMOS as reference clock  */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               dev_dbg(ctx->dev, "Set external reference clock\n");
+       } else if (clk_type == CLK_INT_DIFF) {
+               /* Select internal clock mux */
+               cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+               val = CMU_REG0_PLL_REF_SEL_SET(val, 0x1);
+               cmu_wr(ctx, cmu_type, CMU_REG0, val);
+               /* Select CMOS as reference clock  */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x1);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               dev_dbg(ctx->dev, "Set internal reference clock\n");
+       } else if (clk_type == CLK_INT_SING) {
+               /*
+                * NOTE: This clock type is NOT support for controller
+                *       whose internal clock shared in the PCIe controller
+                *
+                * Select internal clock mux
+                */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x1);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               /* Select CML as reference clock */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               dev_dbg(ctx->dev,
+                       "Set internal single ended reference clock\n");
+       }
+}
+
+static void xgene_phy_sata_cfg_cmu_core(struct xgene_phy_ctx *ctx,
+                                       enum cmu_type_t cmu_type,
+                                       enum clk_type_t clk_type)
+{
+       u32 val;
+       int ref_100MHz;
+
+       if (cmu_type == REF_CMU) {
+               /* Set VCO calibration voltage threshold */
+               cmu_rd(ctx, cmu_type, CMU_REG34, &val);
+               val = CMU_REG34_VCO_CAL_VTH_LO_MAX_SET(val, 0x7);
+               val = CMU_REG34_VCO_CAL_VTH_HI_MAX_SET(val, 0xc);
+               val = CMU_REG34_VCO_CAL_VTH_LO_MIN_SET(val, 0x3);
+               val = CMU_REG34_VCO_CAL_VTH_HI_MIN_SET(val, 0x8);
+               cmu_wr(ctx, cmu_type, CMU_REG34, val);
+       }
+
+       /* Set the VCO calibration counter */
+       cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG0_CAL_COUNT_RESOL_SET(val, 0x4);
+       else
+               val = CMU_REG0_CAL_COUNT_RESOL_SET(val, 0x7);
+       cmu_wr(ctx, cmu_type, CMU_REG0, val);
+
+       /* Configure PLL for calibration */
+       cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+       val = CMU_REG1_PLL_CP_SET(val, 0x1);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG1_PLL_CP_SEL_SET(val, 0x5);
+       else
+               val = CMU_REG1_PLL_CP_SEL_SET(val, 0x3);
+       if (cmu_type == REF_CMU)
+               val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x0);
+       else
+               val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x1);
+       cmu_wr(ctx, cmu_type, CMU_REG1, val);
+
+       if (cmu_type != REF_CMU)
+               cmu_clrbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+
+       /* Configure the PLL for either 100MHz or 50MHz */
+       cmu_rd(ctx, cmu_type, CMU_REG2, &val);
+       if (cmu_type == REF_CMU) {
+               val = CMU_REG2_PLL_LFRES_SET(val, 0xa);
+               ref_100MHz = 1;
+       } else {
+               val = CMU_REG2_PLL_LFRES_SET(val, 0x3);
+               if (clk_type == CLK_EXT_DIFF)
+                       ref_100MHz = 0;
+               else
+                       ref_100MHz = 1;
+       }
+       if (ref_100MHz) {
+               val = CMU_REG2_PLL_FBDIV_SET(val, FBDIV_VAL_100M);
+               val = CMU_REG2_PLL_REFDIV_SET(val, REFDIV_VAL_100M);
+       } else {
+               val = CMU_REG2_PLL_FBDIV_SET(val, FBDIV_VAL_50M);
+               val = CMU_REG2_PLL_REFDIV_SET(val, REFDIV_VAL_50M);
+       }
+       cmu_wr(ctx, cmu_type, CMU_REG2, val);
+
+       /* Configure the VCO */
+       cmu_rd(ctx, cmu_type, CMU_REG3, &val);
+       if (cmu_type == REF_CMU) {
+               val = CMU_REG3_VCOVARSEL_SET(val, 0x3);
+               val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x10);
+       } else {
+               val = CMU_REG3_VCOVARSEL_SET(val, 0xF);
+               if (preA3Chip)
+                       val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x15);
+               else
+                       val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x1a);
+               val = CMU_REG3_VCO_MANMOMSEL_SET(val, 0x15);
+       }
+       cmu_wr(ctx, cmu_type, CMU_REG3, val);
+
+       /* Disable force PLL lock */
+       cmu_rd(ctx, cmu_type, CMU_REG26, &val);
+       val = CMU_REG26_FORCE_PLL_LOCK_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG26, val);
+
+       /* Setup PLL loop filter */
+       cmu_rd(ctx, cmu_type, CMU_REG5, &val);
+       val = CMU_REG5_PLL_LFSMCAP_SET(val, 0x3);
+       val = CMU_REG5_PLL_LFCAP_SET(val, 0x3);
+       if (cmu_type == REF_CMU || !preA3Chip)
+               val = CMU_REG5_PLL_LOCK_RESOLUTION_SET(val, 0x7);
+       else
+               val = CMU_REG5_PLL_LOCK_RESOLUTION_SET(val, 0x4);
+       cmu_wr(ctx, cmu_type, CMU_REG5, val);
+
+       /* Enable or disable manual calibration */
+       cmu_rd(ctx, cmu_type, CMU_REG6, &val);
+       val = CMU_REG6_PLL_VREGTRIM_SET(val, preA3Chip ? 0x0 : 0x2);
+       val = CMU_REG6_MAN_PVT_CAL_SET(val, preA3Chip ? 0x1 : 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG6, val);
+
+       /* Configure lane for 20-bits */
+       if (cmu_type == PHY_CMU) {
+               cmu_rd(ctx, cmu_type, CMU_REG9, &val);
+               val = CMU_REG9_TX_WORD_MODE_CH1_SET(val,
+                                                   CMU_REG9_WORD_LEN_20BIT);
+               val = CMU_REG9_TX_WORD_MODE_CH0_SET(val,
+                                                   CMU_REG9_WORD_LEN_20BIT);
+               val = CMU_REG9_PLL_POST_DIVBY2_SET(val, 0x1);
+               if (!preA3Chip) {
+                       val = CMU_REG9_VBG_BYPASSB_SET(val, 0x0);
+                       val = CMU_REG9_IGEN_BYPASS_SET(val , 0x0);
+               }
+               cmu_wr(ctx, cmu_type, CMU_REG9, val);
+
+               if (!preA3Chip) {
+                       cmu_rd(ctx, cmu_type, CMU_REG10, &val);
+                       val = CMU_REG10_VREG_REFSEL_SET(val, 0x1);
+                       cmu_wr(ctx, cmu_type, CMU_REG10, val);
+               }
+       }
+
+       cmu_rd(ctx, cmu_type, CMU_REG16, &val);
+       val = CMU_REG16_CALIBRATION_DONE_OVERRIDE_SET(val, 0x1);
+       val = CMU_REG16_BYPASS_PLL_LOCK_SET(val, 0x1);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x4);
+       else
+               val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x7);
+       cmu_wr(ctx, cmu_type, CMU_REG16, val);
+
+       /* Configure for SATA */
+       cmu_rd(ctx, cmu_type, CMU_REG30, &val);
+       val = CMU_REG30_PCIE_MODE_SET(val, 0x0);
+       val = CMU_REG30_LOCK_COUNT_SET(val, 0x3);
+       cmu_wr(ctx, cmu_type, CMU_REG30, val);
+
+       /* Disable state machine bypass */
+       cmu_wr(ctx, cmu_type, CMU_REG31, 0xF);
+
+       cmu_rd(ctx, cmu_type, CMU_REG32, &val);
+       val = CMU_REG32_PVT_CAL_WAIT_SEL_SET(val, 0x3);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG32_IREF_ADJ_SET(val, 0x3);
+       else
+               val = CMU_REG32_IREF_ADJ_SET(val, 0x1);
+       cmu_wr(ctx, cmu_type, CMU_REG32, val);
+
+       /* Set VCO calibration threshold */
+       if (cmu_type != REF_CMU && preA3Chip)
+               cmu_wr(ctx, cmu_type, CMU_REG34, 0x8d27);
+       else
+               cmu_wr(ctx, cmu_type, CMU_REG34, 0x873c);
+
+       /* Set CTLE Override and override waiting from state machine */
+       cmu_wr(ctx, cmu_type, CMU_REG37, 0xF00F);
+}
+
+static void xgene_phy_ssc_enable(struct xgene_phy_ctx *ctx,
+                                enum cmu_type_t cmu_type)
+{
+       u32 val;
+
+       /* Set SSC modulation value */
+       cmu_rd(ctx, cmu_type, CMU_REG35, &val);
+       val = CMU_REG35_PLL_SSC_MOD_SET(val, 98);
+       cmu_wr(ctx, cmu_type, CMU_REG35, val);
+
+       /* Enable SSC, set vertical step and DSM value */
+       cmu_rd(ctx, cmu_type, CMU_REG36, &val);
+       val = CMU_REG36_PLL_SSC_VSTEP_SET(val, 30);
+       val = CMU_REG36_PLL_SSC_EN_SET(val, 1);
+       val = CMU_REG36_PLL_SSC_DSMSEL_SET(val, 1);
+       cmu_wr(ctx, cmu_type, CMU_REG36, val);
+
+       /* Reset the PLL */
+       cmu_clrbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+       cmu_setbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+
+       /* Force VCO calibration to restart */
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+                      CMU_REG32_FORCE_VCOCAL_START_MASK);
+}
+
+static void xgene_phy_sata_cfg_lanes(struct xgene_phy_ctx *ctx)
+{
+       u32 val;
+       u32 reg;
+       int i;
+       int lane;
+
+       for (lane = 0; lane < MAX_LANE; lane++) {
+               serdes_wr(ctx, lane, RXTX_REG147, 0x6);
+
+               /* Set boost control for quarter, half, and full rate */
+               serdes_rd(ctx, lane, RXTX_REG0, &val);
+               val = RXTX_REG0_CTLE_EQ_HR_SET(val, 0x10);
+               val = RXTX_REG0_CTLE_EQ_QR_SET(val, 0x10);
+               val = RXTX_REG0_CTLE_EQ_FR_SET(val, 0x10);
+               serdes_wr(ctx, lane, RXTX_REG0, val);
+
+               /* Set boost control value */
+               serdes_rd(ctx, lane, RXTX_REG1, &val);
+               val = RXTX_REG1_RXACVCM_SET(val, 0x7);
+               val = RXTX_REG1_CTLE_EQ_SET(val,
+                       ctx->sata_param.txboostgain[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               serdes_wr(ctx, lane, RXTX_REG1, val);
+
+               /* Latch VTT value based on the termination to ground and
+                  enable TX FIFO */
+               serdes_rd(ctx, lane, RXTX_REG2, &val);
+               val = RXTX_REG2_VTT_ENA_SET(val, 0x1);
+               val = RXTX_REG2_VTT_SEL_SET(val, 0x1);
+               val = RXTX_REG2_TX_FIFO_ENA_SET(val, 0x1);
+               serdes_wr(ctx, lane, RXTX_REG2, val);
+
+               /* Configure Tx for 20-bits */
+               serdes_rd(ctx, lane, RXTX_REG4, &val);
+               val = RXTX_REG4_TX_WORD_MODE_SET(val, CMU_REG9_WORD_LEN_20BIT);
+               serdes_wr(ctx, lane, RXTX_REG4, val);
+
+               if (!preA3Chip) {
+                       serdes_rd(ctx, lane, RXTX_REG1, &val);
+                       val = RXTX_REG1_RXVREG1_SET(val, 0x2);
+                       val = RXTX_REG1_RXIREF_ADJ_SET(val, 0x2);
+                       serdes_wr(ctx, lane, RXTX_REG1, val);
+               }
+
+               /* Set pre-emphasis first 1 and 2, and post-emphasis values */
+               serdes_rd(ctx, lane, RXTX_REG5, &val);
+               val = RXTX_REG5_TX_CN1_SET(val,
+                       ctx->sata_param.txprecursor_cn1[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG5_TX_CP1_SET(val,
+                       ctx->sata_param.txpostcursor_cp1[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG5_TX_CN2_SET(val,
+                       ctx->sata_param.txprecursor_cn2[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               serdes_wr(ctx, lane, RXTX_REG5, val);
+
+               /* Set TX amplitude value */
+               serdes_rd(ctx, lane, RXTX_REG6, &val);
+               val = RXTX_REG6_TXAMP_CNTL_SET(val,
+                       ctx->sata_param.txamplitude[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG6_TXAMP_ENA_SET(val, 0x1);
+               val = RXTX_REG6_TX_IDLE_SET(val, 0x0);
+               val = RXTX_REG6_RX_BIST_RESYNC_SET(val, 0x0);
+               val = RXTX_REG6_RX_BIST_ERRCNT_RD_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG6, val);
+
+               /* Configure Rx for 20-bits */
+               serdes_rd(ctx, lane, RXTX_REG7, &val);
+               val = RXTX_REG7_BIST_ENA_RX_SET(val, 0x0);
+               val = RXTX_REG7_RX_WORD_MODE_SET(val, CMU_REG9_WORD_LEN_20BIT);
+               serdes_wr(ctx, lane, RXTX_REG7, val);
+
+               /* Set CDR and LOS values and enable Rx SSC */
+               serdes_rd(ctx, lane, RXTX_REG8, &val);
+               val = RXTX_REG8_CDR_LOOP_ENA_SET(val, 0x1);
+               val = RXTX_REG8_CDR_BYPASS_RXLOS_SET(val, 0x0);
+               val = RXTX_REG8_SSC_ENABLE_SET(val, 0x1);
+               val = RXTX_REG8_SD_DISABLE_SET(val, 0x0);
+               val = RXTX_REG8_SD_VREF_SET(val, 0x4);
+               serdes_wr(ctx, lane, RXTX_REG8, val);
+
+               /* Set phase adjust upper/lower limits */
+               serdes_rd(ctx, lane, RXTX_REG11, &val);
+               val = RXTX_REG11_PHASE_ADJUST_LIMIT_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG11, val);
+
+               /* Enable Latch Off; disable SUMOS and Tx termination */
+               serdes_rd(ctx, lane, RXTX_REG12, &val);
+               val = RXTX_REG12_LATCH_OFF_ENA_SET(val, 0x1);
+               val = RXTX_REG12_SUMOS_ENABLE_SET(val, 0x0);
+               val = RXTX_REG12_RX_DET_TERM_ENABLE_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG12, val);
+
+               /* Set period error latch to 512T and enable BWL */
+               serdes_rd(ctx, lane, RXTX_REG26, &val);
+               val = RXTX_REG26_PERIOD_ERROR_LATCH_SET(val, 0x0);
+               val = RXTX_REG26_BLWC_ENA_SET(val, 0x1);
+               serdes_wr(ctx, lane, RXTX_REG26, val);
+
+               serdes_wr(ctx, lane, RXTX_REG28, 0x0);
+
+               /* Set DFE loop preset value */
+               serdes_wr(ctx, lane, RXTX_REG31, 0x0);
+
+               /* Set Eye Monitor counter width to 12-bit */
+               serdes_rd(ctx, lane, RXTX_REG61, &val);
+               val = RXTX_REG61_ISCAN_INBERT_SET(val, 0x1);
+               val = RXTX_REG61_LOADFREQ_SHIFT_SET(val, 0x0);
+               val = RXTX_REG61_EYE_COUNT_WIDTH_SEL_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG61, val);
+
+               serdes_rd(ctx, lane, RXTX_REG62, &val);
+               val = RXTX_REG62_PERIOD_H1_QLATCH_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG62, val);
+
+               /* Set BW select tap X for DFE loop */
+               for (i = 0; i < 9; i++) {
+                       reg = RXTX_REG81 + i * 2;
+                       serdes_rd(ctx, lane, reg, &val);
+                       val = RXTX_REG89_MU_TH7_SET(val, 0xe);
+                       val = RXTX_REG89_MU_TH8_SET(val, 0xe);
+                       val = RXTX_REG89_MU_TH9_SET(val, 0xe);
+                       serdes_wr(ctx, lane, reg, val);
+               }
+
+               /* Set BW select tap X for frequency adjust loop */
+               for (i = 0; i < 3; i++) {
+                       reg = RXTX_REG96 + i * 2;
+                       serdes_rd(ctx, lane, reg, &val);
+                       val = RXTX_REG96_MU_FREQ1_SET(val, 0x10);
+                       val = RXTX_REG96_MU_FREQ2_SET(val, 0x10);
+                       val = RXTX_REG96_MU_FREQ3_SET(val, 0x10);
+                       serdes_wr(ctx, lane, reg, val);
+               }
+
+               /* Set BW select tap X for phase adjust loop */
+               for (i = 0; i < 3; i++) {
+                       reg = RXTX_REG99 + i * 2;
+                       serdes_rd(ctx, lane, reg, &val);
+                       val = RXTX_REG99_MU_PHASE1_SET(val, 0x7);
+                       val = RXTX_REG99_MU_PHASE2_SET(val, 0x7);
+                       val = RXTX_REG99_MU_PHASE3_SET(val, 0x7);
+                       serdes_wr(ctx, lane, reg, val);
+               }
+
+               serdes_rd(ctx, lane, RXTX_REG102, &val);
+               val = RXTX_REG102_FREQLOOP_LIMIT_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG102, val);
+
+               serdes_wr(ctx, lane, RXTX_REG114, 0xffe0);
+
+               serdes_rd(ctx, lane, RXTX_REG125, &val);
+               val = RXTX_REG125_SIGN_PQ_SET(val,
+                       ctx->sata_param.txeyedirection[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG125_PQ_REG_SET(val,
+                       ctx->sata_param.txeyetuning[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG125_PHZ_MANUAL_SET(val, 0x1);
+               serdes_wr(ctx, lane, RXTX_REG125, val);
+
+               serdes_rd(ctx, lane, RXTX_REG127, &val);
+               val = RXTX_REG127_LATCH_MAN_CAL_ENA_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG127, val);
+
+               serdes_rd(ctx, lane, RXTX_REG128, &val);
+               val = RXTX_REG128_LATCH_CAL_WAIT_SEL_SET(val, 0x3);
+               serdes_wr(ctx, lane, RXTX_REG128, val);
+
+               serdes_rd(ctx, lane, RXTX_REG145, &val);
+               val = RXTX_REG145_RXDFE_CONFIG_SET(val, 0x3);
+               val = RXTX_REG145_TX_IDLE_SATA_SET(val, 0x0);
+               if (preA3Chip) {
+                       val = RXTX_REG145_RXES_ENA_SET(val, 0x1);
+                       val = RXTX_REG145_RXVWES_LATENA_SET(val, 0x1);
+               } else {
+                       val = RXTX_REG145_RXES_ENA_SET(val, 0x0);
+                       val = RXTX_REG145_RXVWES_LATENA_SET(val, 0x0);
+               }
+               serdes_wr(ctx, lane, RXTX_REG145, val);
+
+               /*
+                * Set Rx LOS filter clock rate, sample rate, and threshold
+                * windows
+                */
+               for (i = 0; i < 4; i++) {
+                       reg = RXTX_REG148 + i * 2;
+                       serdes_wr(ctx, lane, reg, 0xFFFF);
+               }
+       }
+}
+
+static int xgene_phy_cal_rdy_chk(struct xgene_phy_ctx *ctx,
+                                enum cmu_type_t cmu_type,
+                                enum clk_type_t clk_type)
+{
+       void __iomem *csr_serdes = ctx->sds_base;
+       int loop;
+       u32 val;
+
+       /* Release PHY main reset */
+       writel(0xdf, csr_serdes + SATA_ENET_SDS_RST_CTL);
+       readl(csr_serdes + SATA_ENET_SDS_RST_CTL); /* Force a barrier */
+
+       if (cmu_type != REF_CMU) {
+               cmu_setbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+               /*
+                * As per PHY design spec, the PLL reset requires a minimum
+                * of 800us.
+                */
+               usleep_range(800, 1000);
+
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               /*
+                * As per PHY design spec, the PLL auto calibration requires
+                * a minimum of 800us.
+                */
+               usleep_range(800, 1000);
+
+               cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+                              CMU_REG32_FORCE_VCOCAL_START_MASK);
+               /*
+                * As per PHY design spec, the PLL requires a minimum of
+                * 800us to settle.
+                */
+               usleep_range(800, 1000);
+       }
+
+       if (!preA3Chip)
+               goto skip_manual_cal;
+
+       /*
+        * Configure the termination resister calibration
+        * The serial receive pins, RXP/RXN, have TERMination resistor
+        * that is required to be calibrated.
+        */
+       cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+       val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x12);
+       val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG17, val);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG17,
+                      CMU_REG17_PVT_TERM_MAN_ENA_MASK);
+       /*
+        * The serial transmit pins, TXP/TXN, have Pull-UP and Pull-DOWN
+        * resistors that are required to the calibrated.
+        * Configure the pull DOWN calibration
+        */
+       cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+       val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x29);
+       val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG17, val);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG16,
+                      CMU_REG16_PVT_DN_MAN_ENA_MASK);
+       /* Configure the pull UP calibration */
+       cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+       val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x28);
+       val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG17, val);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG16,
+                      CMU_REG16_PVT_UP_MAN_ENA_MASK);
+
+skip_manual_cal:
+       /* Poll the PLL calibration completion status for at least 1 ms */
+       loop = 100;
+       do {
+               cmu_rd(ctx, cmu_type, CMU_REG7, &val);
+               if (CMU_REG7_PLL_CALIB_DONE_RD(val))
+                       break;
+               /*
+                * As per PHY design spec, PLL calibration status requires
+                * a minimum of 10us to be updated.
+                */
+               usleep_range(10, 100);
+       } while (--loop > 0);
+
+       cmu_rd(ctx, cmu_type, CMU_REG7, &val);
+       dev_dbg(ctx->dev, "PLL calibration %s\n",
+               CMU_REG7_PLL_CALIB_DONE_RD(val) ? "done" : "failed");
+       if (CMU_REG7_VCO_CAL_FAIL_RD(val)) {
+               dev_err(ctx->dev,
+                       "PLL calibration failed due to VCO failure\n");
+               return -1;
+       }
+       dev_dbg(ctx->dev, "PLL calibration successful\n");
+
+       cmu_rd(ctx, cmu_type, CMU_REG15, &val);
+       dev_dbg(ctx->dev, "PHY Tx is %sready\n", val & 0x300 ? "" : "not ");
+       return 0;
+}
+
+static void xgene_phy_pdwn_force_vco(struct xgene_phy_ctx *ctx,
+                                    enum cmu_type_t cmu_type,
+                                    enum clk_type_t clk_type)
+{
+       u32 val;
+
+       dev_dbg(ctx->dev, "Reset VCO and re-start again\n");
+       if (cmu_type == PHY_CMU) {
+               cmu_rd(ctx, cmu_type, CMU_REG16, &val);
+               val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x7);
+               cmu_wr(ctx, cmu_type, CMU_REG16, val);
+       }
+
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG0, CMU_REG0_PDOWN_MASK);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+                      CMU_REG32_FORCE_VCOCAL_START_MASK);
+}
+
+static int xgene_phy_hw_init_sata(struct xgene_phy_ctx *ctx,
+                                 enum clk_type_t clk_type, int ssc_enable)
+{
+       void __iomem *sds_base = ctx->sds_base;
+       u32 val;
+       int i;
+
+       /* Configure the PHY for operation */
+       dev_dbg(ctx->dev, "Reset PHY\n");
+       /* Place PHY into reset */
+       writel(0x0, sds_base + SATA_ENET_SDS_RST_CTL);
+       val = readl(sds_base + SATA_ENET_SDS_RST_CTL);  /* Force a barrier */
+       /* Release PHY lane from reset (active high) */
+       writel(0x20, sds_base + SATA_ENET_SDS_RST_CTL);
+       readl(sds_base + SATA_ENET_SDS_RST_CTL);        /* Force a barrier */
+       /* Release all PHY module out of reset except PHY main reset */
+       writel(0xde, sds_base + SATA_ENET_SDS_RST_CTL);
+       readl(sds_base + SATA_ENET_SDS_RST_CTL);        /* Force a barrier */
+
+       /* Set the operation speed */
+       val = readl(sds_base + SATA_ENET_SDS_CTL1);
+       val = CFG_I_SPD_SEL_CDR_OVR1_SET(val,
+               ctx->sata_param.txspeed[ctx->sata_param.speed[0]]);
+       writel(val, sds_base + SATA_ENET_SDS_CTL1);
+
+       dev_dbg(ctx->dev, "Set the customer pin mode to SATA\n");
+       val = readl(sds_base + SATA_ENET_SDS_CTL0);
+       val = REGSPEC_CFG_I_CUSTOMER_PIN_MODE0_SET(val, 0x4421);
+       writel(val, sds_base + SATA_ENET_SDS_CTL0);
+
+       /* Configure the clock macro unit (CMU) clock type */
+       xgene_phy_cfg_cmu_clk_type(ctx, PHY_CMU, clk_type);
+
+       /* Configure the clock macro */
+       xgene_phy_sata_cfg_cmu_core(ctx, PHY_CMU, clk_type);
+
+       /* Enable SSC if enabled */
+       if (ssc_enable)
+               xgene_phy_ssc_enable(ctx, PHY_CMU);
+
+       /* Configure PHY lanes */
+       xgene_phy_sata_cfg_lanes(ctx);
+
+       /* Set Rx/Tx 20-bit */
+       val = readl(sds_base + SATA_ENET_SDS_PCS_CTL0);
+       val = REGSPEC_CFG_I_RX_WORDMODE0_SET(val, 0x3);
+       val = REGSPEC_CFG_I_TX_WORDMODE0_SET(val, 0x3);
+       writel(val, sds_base + SATA_ENET_SDS_PCS_CTL0);
+
+       /* Start PLL calibration and try for three times */
+       i = 10;
+       do {
+               if (!xgene_phy_cal_rdy_chk(ctx, PHY_CMU, clk_type))
+                       break;
+               /* If failed, toggle the VCO power signal and start again */
+               xgene_phy_pdwn_force_vco(ctx, PHY_CMU, clk_type);
+       } while (--i > 0);
+       /* Even on failure, allow to continue any way */
+       if (i <= 0)
+               dev_err(ctx->dev, "PLL calibration failed\n");
+
+       return 0;
+}
+
+static int xgene_phy_hw_initialize(struct xgene_phy_ctx *ctx,
+                                  enum clk_type_t clk_type,
+                                  int ssc_enable)
+{
+       int rc;
+
+       dev_dbg(ctx->dev, "PHY init clk type %d\n", clk_type);
+
+       if (ctx->mode == MODE_SATA) {
+               rc = xgene_phy_hw_init_sata(ctx, clk_type, ssc_enable);
+               if (rc)
+                       return rc;
+       } else {
+               dev_err(ctx->dev, "Un-supported customer pin mode %d\n",
+                       ctx->mode);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/*
+ * Receiver Offset Calibration:
+ *
+ * Calibrate the receiver signal path offset in two steps - summar and
+ * latch calibrations
+ */
+static void xgene_phy_force_lat_summer_cal(struct xgene_phy_ctx *ctx, int lane)
+{
+       int i;
+       struct {
+               u32 reg;
+               u32 val;
+       } serdes_reg[] = {
+               {RXTX_REG38, 0x0},
+               {RXTX_REG39, 0xff00},
+               {RXTX_REG40, 0xffff},
+               {RXTX_REG41, 0xffff},
+               {RXTX_REG42, 0xffff},
+               {RXTX_REG43, 0xffff},
+               {RXTX_REG44, 0xffff},
+               {RXTX_REG45, 0xffff},
+               {RXTX_REG46, 0xffff},
+               {RXTX_REG47, 0xfffc},
+               {RXTX_REG48, 0x0},
+               {RXTX_REG49, 0x0},
+               {RXTX_REG50, 0x0},
+               {RXTX_REG51, 0x0},
+               {RXTX_REG52, 0x0},
+               {RXTX_REG53, 0x0},
+               {RXTX_REG54, 0x0},
+               {RXTX_REG55, 0x0},
+       };
+
+       /* Start SUMMER calibration */
+       serdes_setbits(ctx, lane, RXTX_REG127,
+                      RXTX_REG127_FORCE_SUM_CAL_START_MASK);
+       /*
+        * As per PHY design spec, the Summer calibration requires a minimum
+        * of 100us to complete.
+        */
+       usleep_range(100, 500);
+       serdes_clrbits(ctx, lane, RXTX_REG127,
+                       RXTX_REG127_FORCE_SUM_CAL_START_MASK);
+       /*
+        * As per PHY design spec, the auto calibration requires a minimum
+        * of 100us to complete.
+        */
+       usleep_range(100, 500);
+
+       /* Start latch calibration */
+       serdes_setbits(ctx, lane, RXTX_REG127,
+                      RXTX_REG127_FORCE_LAT_CAL_START_MASK);
+       /*
+        * As per PHY design spec, the latch calibration requires a minimum
+        * of 100us to complete.
+        */
+       usleep_range(100, 500);
+       serdes_clrbits(ctx, lane, RXTX_REG127,
+                      RXTX_REG127_FORCE_LAT_CAL_START_MASK);
+
+       /* Configure the PHY lane for calibration */
+       serdes_wr(ctx, lane, RXTX_REG28, 0x7);
+       serdes_wr(ctx, lane, RXTX_REG31, 0x7e00);
+       serdes_clrbits(ctx, lane, RXTX_REG4,
+                      RXTX_REG4_TX_LOOPBACK_BUF_EN_MASK);
+       serdes_clrbits(ctx, lane, RXTX_REG7,
+                      RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK);
+       for (i = 0; i < ARRAY_SIZE(serdes_reg); i++)
+               serdes_wr(ctx, lane, serdes_reg[i].reg,
+                         serdes_reg[i].val);
+}
+
+static void xgene_phy_reset_rxd(struct xgene_phy_ctx *ctx, int lane)
+{
+       /* Reset digital Rx */
+       serdes_clrbits(ctx, lane, RXTX_REG7, RXTX_REG7_RESETB_RXD_MASK);
+       /* As per PHY design spec, the reset requires a minimum of 100us. */
+       usleep_range(100, 150);
+       serdes_setbits(ctx, lane, RXTX_REG7, RXTX_REG7_RESETB_RXD_MASK);
+}
+
+static int xgene_phy_get_avg(int accum, int samples)
+{
+       return (accum + (samples / 2)) / samples;
+}
+
+static void xgene_phy_gen_avg_val(struct xgene_phy_ctx *ctx, int lane)
+{
+       int max_loop = 10;
+       int avg_loop = 0;
+       int lat_do = 0, lat_xo = 0, lat_eo = 0, lat_so = 0;
+       int lat_de = 0, lat_xe = 0, lat_ee = 0, lat_se = 0;
+       int sum_cal = 0;
+       int lat_do_itr, lat_xo_itr, lat_eo_itr, lat_so_itr;
+       int lat_de_itr, lat_xe_itr, lat_ee_itr, lat_se_itr;
+       int sum_cal_itr;
+       int fail_even;
+       int fail_odd;
+       u32 val;
+
+       dev_dbg(ctx->dev, "Generating avg calibration value for lane %d\n",
+               lane);
+
+       /* Enable RX Hi-Z termination */
+       serdes_setbits(ctx, lane, RXTX_REG12,
+                       RXTX_REG12_RX_DET_TERM_ENABLE_MASK);
+       /* Turn off DFE */
+       serdes_wr(ctx, lane, RXTX_REG28, 0x0000);
+       /* DFE Presets to zero */
+       serdes_wr(ctx, lane, RXTX_REG31, 0x0000);
+
+       /*
+        * Receiver Offset Calibration:
+        * Calibrate the receiver signal path offset in two steps - summar
+        * and latch calibration.
+        * Runs the "Receiver Offset Calibration multiple times to determine
+        * the average value to use.
+        */
+       while (avg_loop < max_loop) {
+               /* Start the calibration */
+               xgene_phy_force_lat_summer_cal(ctx, lane);
+
+               serdes_rd(ctx, lane, RXTX_REG21, &val);
+               lat_do_itr = RXTX_REG21_DO_LATCH_CALOUT_RD(val);
+               lat_xo_itr = RXTX_REG21_XO_LATCH_CALOUT_RD(val);
+               fail_odd = RXTX_REG21_LATCH_CAL_FAIL_ODD_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG22, &val);
+               lat_eo_itr = RXTX_REG22_EO_LATCH_CALOUT_RD(val);
+               lat_so_itr = RXTX_REG22_SO_LATCH_CALOUT_RD(val);
+               fail_even = RXTX_REG22_LATCH_CAL_FAIL_EVEN_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG23, &val);
+               lat_de_itr = RXTX_REG23_DE_LATCH_CALOUT_RD(val);
+               lat_xe_itr = RXTX_REG23_XE_LATCH_CALOUT_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG24, &val);
+               lat_ee_itr = RXTX_REG24_EE_LATCH_CALOUT_RD(val);
+               lat_se_itr = RXTX_REG24_SE_LATCH_CALOUT_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG121, &val);
+               sum_cal_itr = RXTX_REG121_SUMOS_CAL_CODE_RD(val);
+
+               /* Check for failure. If passed, sum them for averaging */
+               if ((fail_even == 0 || fail_even == 1) &&
+                   (fail_odd == 0 || fail_odd == 1)) {
+                       lat_do += lat_do_itr;
+                       lat_xo += lat_xo_itr;
+                       lat_eo += lat_eo_itr;
+                       lat_so += lat_so_itr;
+                       lat_de += lat_de_itr;
+                       lat_xe += lat_xe_itr;
+                       lat_ee += lat_ee_itr;
+                       lat_se += lat_se_itr;
+                       sum_cal += sum_cal_itr;
+
+                       dev_dbg(ctx->dev, "Iteration %d:\n", avg_loop);
+                       dev_dbg(ctx->dev, "DO 0x%x XO 0x%x EO 0x%x SO 0x%x\n",
+                               lat_do_itr, lat_xo_itr, lat_eo_itr,
+                               lat_so_itr);
+                       dev_dbg(ctx->dev, "DE 0x%x XE 0x%x EE 0x%x SE 0x%x\n",
+                               lat_de_itr, lat_xe_itr, lat_ee_itr,
+                               lat_se_itr);
+                       dev_dbg(ctx->dev, "SUM 0x%x\n", sum_cal_itr);
+                       ++avg_loop;
+               } else {
+                       dev_err(ctx->dev,
+                               "Receiver calibration failed at %d loop\n",
+                               avg_loop);
+               }
+               xgene_phy_reset_rxd(ctx, lane);
+       }
+
+       /* Update latch manual calibration with average value */
+       serdes_rd(ctx, lane, RXTX_REG127, &val);
+       val = RXTX_REG127_DO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_do, max_loop));
+       val = RXTX_REG127_XO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_xo, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG127, val);
+
+       serdes_rd(ctx, lane, RXTX_REG128, &val);
+       val = RXTX_REG128_EO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_eo, max_loop));
+       val = RXTX_REG128_SO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_so, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG128, val);
+
+       serdes_rd(ctx, lane, RXTX_REG129, &val);
+       val = RXTX_REG129_DE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_de, max_loop));
+       val = RXTX_REG129_XE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_xe, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG129, val);
+
+       serdes_rd(ctx, lane, RXTX_REG130, &val);
+       val = RXTX_REG130_EE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_ee, max_loop));
+       val = RXTX_REG130_SE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_se, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG130, val);
+
+       /* Update SUMMER calibration with average value */
+       serdes_rd(ctx, lane, RXTX_REG14, &val);
+       val = RXTX_REG14_CLTE_LATCAL_MAN_PROG_SET(val,
+               xgene_phy_get_avg(sum_cal, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG14, val);
+
+       dev_dbg(ctx->dev, "Average Value:\n");
+       dev_dbg(ctx->dev, "DO 0x%x XO 0x%x EO 0x%x SO 0x%x\n",
+                xgene_phy_get_avg(lat_do, max_loop),
+                xgene_phy_get_avg(lat_xo, max_loop),
+                xgene_phy_get_avg(lat_eo, max_loop),
+                xgene_phy_get_avg(lat_so, max_loop));
+       dev_dbg(ctx->dev, "DE 0x%x XE 0x%x EE 0x%x SE 0x%x\n",
+                xgene_phy_get_avg(lat_de, max_loop),
+                xgene_phy_get_avg(lat_xe, max_loop),
+                xgene_phy_get_avg(lat_ee, max_loop),
+                xgene_phy_get_avg(lat_se, max_loop));
+       dev_dbg(ctx->dev, "SUM 0x%x\n",
+               xgene_phy_get_avg(sum_cal, max_loop));
+
+       serdes_rd(ctx, lane, RXTX_REG14, &val);
+       val = RXTX_REG14_CTLE_LATCAL_MAN_ENA_SET(val, 0x1);
+       serdes_wr(ctx, lane, RXTX_REG14, val);
+       dev_dbg(ctx->dev, "Enable Manual Summer calibration\n");
+
+       serdes_rd(ctx, lane, RXTX_REG127, &val);
+       val = RXTX_REG127_LATCH_MAN_CAL_ENA_SET(val, 0x1);
+       dev_dbg(ctx->dev, "Enable Manual Latch calibration\n");
+       serdes_wr(ctx, lane, RXTX_REG127, val);
+
+       /* Disable RX Hi-Z termination */
+       serdes_rd(ctx, lane, RXTX_REG12, &val);
+       val = RXTX_REG12_RX_DET_TERM_ENABLE_SET(val, 0);
+       serdes_wr(ctx, lane, RXTX_REG12, val);
+       /* Turn on DFE */
+       serdes_wr(ctx, lane, RXTX_REG28, 0x0007);
+       /* Set DFE preset */
+       serdes_wr(ctx, lane, RXTX_REG31, 0x7e00);
+}
+
+static int xgene_phy_hw_init(struct phy *phy)
+{
+       struct xgene_phy_ctx *ctx = phy_get_drvdata(phy);
+       int rc;
+       int i;
+
+       rc = xgene_phy_hw_initialize(ctx, CLK_EXT_DIFF, SSC_DISABLE);
+       if (rc) {
+               dev_err(ctx->dev, "PHY initialize failed %d\n", rc);
+               return rc;
+       }
+
+       /* Setup clock properly after PHY configuration */
+       if (!IS_ERR(ctx->clk)) {
+               /* HW requires an toggle of the clock */
+               clk_prepare_enable(ctx->clk);
+               clk_disable_unprepare(ctx->clk);
+               clk_prepare_enable(ctx->clk);
+       }
+
+       /* Compute average value */
+       for (i = 0; i < MAX_LANE; i++)
+               xgene_phy_gen_avg_val(ctx, i);
+
+       dev_dbg(ctx->dev, "PHY initialized\n");
+       return 0;
+}
+
+static const struct phy_ops xgene_phy_ops = {
+       .init           = xgene_phy_hw_init,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *xgene_phy_xlate(struct device *dev,
+                                  struct of_phandle_args *args)
+{
+       struct xgene_phy_ctx *ctx = dev_get_drvdata(dev);
+
+       if (args->args_count <= 0)
+               return ERR_PTR(-EINVAL);
+       if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX)
+               return ERR_PTR(-EINVAL);
+
+       ctx->mode = args->args[0];
+       return ctx->phy;
+}
+
+static void xgene_phy_get_param(struct platform_device *pdev,
+                               const char *name, u32 *buffer,
+                               int count, u32 *default_val,
+                               u32 conv_factor)
+{
+       int i;
+
+       if (!of_property_read_u32_array(pdev->dev.of_node, name, buffer,
+                                       count)) {
+               for (i = 0; i < count; i++)
+                       buffer[i] /= conv_factor;
+               return;
+       }
+       /* Does not exist, load default */
+       for (i = 0; i < count; i++)
+               buffer[i] = default_val[i % 3];
+}
+
+static int xgene_phy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct xgene_phy_ctx *ctx;
+       struct resource *res;
+       int rc = 0;
+       u32 default_spd[] = DEFAULT_SATA_SPD_SEL;
+       u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN;
+       u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION;
+       u32 default_txeye_tuning[] = DEFAULT_SATA_TXEYETUNING;
+       u32 default_txamp[] = DEFAULT_SATA_TXAMP;
+       u32 default_txcn1[] = DEFAULT_SATA_TXCN1;
+       u32 default_txcn2[] = DEFAULT_SATA_TXCN2;
+       u32 default_txcp1[] = DEFAULT_SATA_TXCP1;
+       int i;
+
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ctx->sds_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ctx->sds_base)) {
+               rc = PTR_ERR(ctx->sds_base);
+               goto error;
+       }
+
+       /* Retrieve optional clock */
+       ctx->clk = clk_get(&pdev->dev, NULL);
+
+       /* Load override paramaters */
+       xgene_phy_get_param(pdev, "apm,tx-eye-tuning",
+               ctx->sata_param.txeyetuning, 6, default_txeye_tuning, 1);
+       xgene_phy_get_param(pdev, "apm,tx-eye-direction",
+               ctx->sata_param.txeyedirection, 6, default_txeye_direction, 1);
+       xgene_phy_get_param(pdev, "apm,tx-boost-gain",
+               ctx->sata_param.txboostgain, 6, default_txboost_gain, 1);
+       xgene_phy_get_param(pdev, "apm,tx-amplitude",
+               ctx->sata_param.txamplitude, 6, default_txamp, 13300);
+       xgene_phy_get_param(pdev, "apm,tx-pre-cursor1",
+               ctx->sata_param.txprecursor_cn1, 6, default_txcn1, 18200);
+       xgene_phy_get_param(pdev, "apm,tx-pre-cursor2",
+               ctx->sata_param.txprecursor_cn2, 6, default_txcn2, 18200);
+       xgene_phy_get_param(pdev, "apm,tx-post-cursor",
+               ctx->sata_param.txpostcursor_cp1, 6, default_txcp1, 18200);
+       xgene_phy_get_param(pdev, "apm,tx-speed",
+               ctx->sata_param.txspeed, 3, default_spd, 1);
+       for (i = 0; i < MAX_LANE; i++)
+               ctx->sata_param.speed[i] = 2; /* Default to Gen3 */
+
+       ctx->dev = &pdev->dev;
+       platform_set_drvdata(pdev, ctx);
+
+       ctx->phy = devm_phy_create(ctx->dev, &xgene_phy_ops, NULL);
+       if (IS_ERR(ctx->phy)) {
+               dev_dbg(&pdev->dev, "Failed to create PHY\n");
+               rc = PTR_ERR(ctx->phy);
+               goto error;
+       }
+       phy_set_drvdata(ctx->phy, ctx);
+
+       phy_provider = devm_of_phy_provider_register(ctx->dev,
+                                                    xgene_phy_xlate);
+       if (IS_ERR(phy_provider)) {
+               rc = PTR_ERR(phy_provider);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       return rc;
+}
+
+static const struct of_device_id xgene_phy_of_match[] = {
+       {.compatible = "apm,xgene-phy",},
+       {},
+};
+MODULE_DEVICE_TABLE(of, xgene_phy_of_match);
+
+static struct platform_driver xgene_phy_driver = {
+       .probe = xgene_phy_probe,
+       .driver = {
+                  .name = "xgene-phy",
+                  .owner = THIS_MODULE,
+                  .of_match_table = xgene_phy_of_match,
+       },
+};
+module_platform_driver(xgene_phy_driver);
+
+MODULE_DESCRIPTION("APM X-Gene Multi-Purpose PHY driver");
+MODULE_AUTHOR("Loc Ho <lho@apm.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
index 2e6b832e004ba4926aaa1d870d7a1a01345dc8ac..e0cad4418085c5c136c1e8db3852803d74d3b822 100644 (file)
@@ -2,10 +2,6 @@
 # USB device configuration
 #
 
-# These are unused now, remove them once they are no longer selected
-config USB_ARCH_HAS_OHCI
-       bool
-
 config USB_OHCI_BIG_ENDIAN_DESC
        bool
 
@@ -17,18 +13,12 @@ config USB_OHCI_LITTLE_ENDIAN
        default n if STB03xxx || PPC_MPC52xx
        default y
 
-config USB_ARCH_HAS_EHCI
-       bool
-
 config USB_EHCI_BIG_ENDIAN_MMIO
        bool
 
 config USB_EHCI_BIG_ENDIAN_DESC
        bool
 
-config USB_ARCH_HAS_XHCI
-       bool
-
 menuconfig USB_SUPPORT
        bool "USB support"
        depends on HAS_IOMEM
index 7345d2115af2b20b512a4e4eb79eef435b23da6f..480bd4d5710ae5dcc8a751680e7b750c46d9f093 100644 (file)
@@ -10,6 +10,7 @@ ci_hdrc-$(CONFIG_USB_CHIPIDEA_DEBUG)  += debug.o
 # Glue/Bridge layers go here
 
 obj-$(CONFIG_USB_CHIPIDEA)     += ci_hdrc_msm.o
+obj-$(CONFIG_USB_CHIPIDEA)     += ci_hdrc_zevio.o
 
 # PCI doesn't provide stubs, need to check
 ifneq ($(CONFIG_PCI),)
index a857131656889fa2a2634dc33ed398a5143c373a..83d06c1455b7bd93875b3562dcbbe30f7b4f7f9c 100644 (file)
 #define PORTSC_PTC            (0x0FUL << 16)
 #define PORTSC_PHCD(d)       ((d) ? BIT(22) : BIT(23))
 /* PTS and PTW for non lpm version only */
+#define PORTSC_PFSC           BIT(24)
 #define PORTSC_PTS(d)                                          \
        (u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
 #define PORTSC_PTW            BIT(28)
 #define PORTSC_STS            BIT(29)
 
 /* DEVLC */
+#define DEVLC_PFSC            BIT(23)
 #define DEVLC_PSPD            (0x03UL << 25)
 #define DEVLC_PSPD_HS         (0x02UL << 25)
 #define DEVLC_PTW             BIT(27)
index 88b80f7728e42b30b60819acd5dfb0fd613347e0..e206406ae1d9558f8776a6a5e2974a2bd87e2ee7 100644 (file)
@@ -196,8 +196,6 @@ struct ci_hdrc {
 
        struct ci_hdrc_platform_data    *platdata;
        int                             vbus_active;
-       /* FIXME: some day, we'll not use global phy */
-       bool                            global_phy;
        struct usb_phy                  *transceiver;
        struct usb_hcd                  *hcd;
        struct dentry                   *debugfs;
index c00f77257d3651ff3e99858b23e1396e2df921b3..2e58f8dfd3112eb40dc398c883552a69b867d567 100644 (file)
@@ -96,7 +96,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
 {
        struct ci_hdrc_imx_data *data;
        struct ci_hdrc_platform_data pdata = {
-               .name           = "ci_hdrc_imx",
+               .name           = dev_name(&pdev->dev),
                .capoffset      = DEF_CAPOFFSET,
                .flags          = CI_HDRC_REQUIRE_TRANSCEIVER |
                                  CI_HDRC_DISABLE_STREAMING,
diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c
new file mode 100644 (file)
index 0000000..3bf6489
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ *     Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
+ *
+ * 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.
+ *
+ * Based off drivers/usb/chipidea/ci_hdrc_msm.c
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/chipidea.h>
+
+#include "ci.h"
+
+static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
+       .name                   = "ci_hdrc_zevio",
+       .flags                  = CI_HDRC_REGS_SHARED,
+       .capoffset              = DEF_CAPOFFSET,
+};
+
+static int ci_hdrc_zevio_probe(struct platform_device *pdev)
+{
+       struct platform_device *ci_pdev;
+
+       dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n");
+
+       ci_pdev = ci_hdrc_add_device(&pdev->dev,
+                               pdev->resource, pdev->num_resources,
+                               &ci_hdrc_zevio_platdata);
+
+       if (IS_ERR(ci_pdev)) {
+               dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
+               return PTR_ERR(ci_pdev);
+       }
+
+       platform_set_drvdata(pdev, ci_pdev);
+
+       return 0;
+}
+
+static int ci_hdrc_zevio_remove(struct platform_device *pdev)
+{
+       struct platform_device *ci_pdev = platform_get_drvdata(pdev);
+
+       ci_hdrc_remove_device(ci_pdev);
+
+       return 0;
+}
+
+static const struct of_device_id ci_hdrc_zevio_dt_ids[] = {
+       { .compatible = "lsi,zevio-usb", },
+       { /* sentinel */ }
+};
+
+static struct platform_driver ci_hdrc_zevio_driver = {
+       .probe = ci_hdrc_zevio_probe,
+       .remove = ci_hdrc_zevio_remove,
+       .driver = {
+               .name = "zevio_usb",
+               .owner = THIS_MODULE,
+               .of_match_table = ci_hdrc_zevio_dt_ids,
+       },
+};
+
+MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids);
+module_platform_driver(ci_hdrc_zevio_driver);
+
+MODULE_LICENSE("GPL v2");
index 33f22bc6ad7f3063a2299921d29948dc3a73b216..ca6831c5b763053d146d8c7bbae2f10d85e3c3d1 100644 (file)
@@ -64,6 +64,7 @@
 #include <linux/usb/otg.h>
 #include <linux/usb/chipidea.h>
 #include <linux/usb/of.h>
+#include <linux/of.h>
 #include <linux/phy.h>
 #include <linux/regulator/consumer.h>
 
@@ -298,6 +299,13 @@ int hw_device_reset(struct ci_hdrc *ci, u32 mode)
        if (ci->platdata->flags & CI_HDRC_DISABLE_STREAMING)
                hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS, USBMODE_CI_SDIS);
 
+       if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
+               if (ci->hw_bank.lpm)
+                       hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC);
+               else
+                       hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
+       }
+
        /* USBMODE should be configured step by step */
        hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
        hw_write(ci, OP_USBMODE, USBMODE_CM, mode);
@@ -412,6 +420,9 @@ static int ci_get_platdata(struct device *dev,
                }
        }
 
+       if (of_usb_get_maximum_speed(dev->of_node) == USB_SPEED_FULL)
+               platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
+
        return 0;
 }
 
@@ -496,33 +507,6 @@ static void ci_get_otg_capable(struct ci_hdrc *ci)
        }
 }
 
-static int ci_usb_phy_init(struct ci_hdrc *ci)
-{
-       if (ci->platdata->phy) {
-               ci->transceiver = ci->platdata->phy;
-               return usb_phy_init(ci->transceiver);
-       } else {
-               ci->global_phy = true;
-               ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
-               if (IS_ERR(ci->transceiver))
-                       ci->transceiver = NULL;
-
-               return 0;
-       }
-}
-
-static void ci_usb_phy_destroy(struct ci_hdrc *ci)
-{
-       if (!ci->transceiver)
-               return;
-
-       otg_set_peripheral(ci->transceiver->otg, NULL);
-       if (ci->global_phy)
-               usb_put_phy(ci->transceiver);
-       else
-               usb_phy_shutdown(ci->transceiver);
-}
-
 static int ci_hdrc_probe(struct platform_device *pdev)
 {
        struct device   *dev = &pdev->dev;
@@ -532,7 +516,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        int             ret;
        enum usb_dr_mode dr_mode;
 
-       if (!dev->platform_data) {
+       if (!dev_get_platdata(dev)) {
                dev_err(dev, "platform data missing\n");
                return -ENODEV;
        }
@@ -549,7 +533,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        }
 
        ci->dev = dev;
-       ci->platdata = dev->platform_data;
+       ci->platdata = dev_get_platdata(dev);
        ci->imx28_write_fix = !!(ci->platdata->flags &
                CI_HDRC_IMX28_WRITE_FIX);
 
@@ -561,7 +545,26 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
        hw_phymode_configure(ci);
 
-       ret = ci_usb_phy_init(ci);
+       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;
+
+               dev_err(dev, "no usb2 phy configured\n");
+               return -EPROBE_DEFER;
+       }
+
+       ret = usb_phy_init(ci->transceiver);
        if (ret) {
                dev_err(dev, "unable to init phy: %d\n", ret);
                return ret;
@@ -572,8 +575,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        ci->irq = platform_get_irq(pdev, 0);
        if (ci->irq < 0) {
                dev_err(dev, "missing IRQ\n");
-               ret = -ENODEV;
-               goto destroy_phy;
+               ret = ci->irq;
+               goto deinit_phy;
        }
 
        ci_get_otg_capable(ci);
@@ -590,23 +593,12 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                ret = ci_hdrc_gadget_init(ci);
                if (ret)
                        dev_info(dev, "doesn't support gadget\n");
-               if (!ret && ci->transceiver) {
-                       ret = otg_set_peripheral(ci->transceiver->otg,
-                                                       &ci->gadget);
-                       /*
-                        * If we implement all USB functions using chipidea drivers,
-                        * it doesn't need to call above API, meanwhile, if we only
-                        * use gadget function, calling above API is useless.
-                        */
-                       if (ret && ret != -ENOTSUPP)
-                               goto destroy_phy;
-               }
        }
 
        if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
                dev_err(dev, "no supported roles\n");
                ret = -ENODEV;
-               goto destroy_phy;
+               goto deinit_phy;
        }
 
        if (ci->is_otg) {
@@ -663,8 +655,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        free_irq(ci->irq, ci);
 stop:
        ci_role_destroy(ci);
-destroy_phy:
-       ci_usb_phy_destroy(ci);
+deinit_phy:
+       usb_phy_shutdown(ci->transceiver);
 
        return ret;
 }
@@ -677,7 +669,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
        free_irq(ci->irq, ci);
        ci_role_destroy(ci);
        ci_hdrc_enter_lpm(ci, true);
-       ci_usb_phy_destroy(ci);
+       usb_phy_shutdown(ci->transceiver);
+       kfree(ci->hw_bank.regmap);
 
        return 0;
 }
index 4ab2cb62dfce4c1b3dfe140ef83f20d4633b9ff0..7739c64ef2590a1dbdd0daf8e631b90cf99af628 100644 (file)
@@ -177,19 +177,6 @@ static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir)
        return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0;
 }
 
-/**
- * hw_test_and_clear_setup_status: test & clear setup status (execute without
- *                                 interruption)
- * @n: endpoint number
- *
- * This function returns setup status
- */
-static int hw_test_and_clear_setup_status(struct ci_hdrc *ci, int n)
-{
-       n = ep_to_bit(ci, n);
-       return hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(n));
-}
-
 /**
  * hw_ep_prime: primes endpoint (execute without interruption)
  * @num:     endpoint number
@@ -961,6 +948,156 @@ __acquires(hwep->lock)
        return retval;
 }
 
+/**
+ * isr_setup_packet_handler: setup packet handler
+ * @ci: UDC descriptor
+ *
+ * This function handles setup packet 
+ */
+static void isr_setup_packet_handler(struct ci_hdrc *ci)
+__releases(ci->lock)
+__acquires(ci->lock)
+{
+       struct ci_hw_ep *hwep = &ci->ci_hw_ep[0];
+       struct usb_ctrlrequest req;
+       int type, num, dir, err = -EINVAL;
+       u8 tmode = 0;
+
+       /*
+        * Flush data and handshake transactions of previous
+        * setup packet.
+        */
+       _ep_nuke(ci->ep0out);
+       _ep_nuke(ci->ep0in);
+
+       /* read_setup_packet */
+       do {
+               hw_test_and_set_setup_guard(ci);
+               memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
+       } while (!hw_test_and_clear_setup_guard(ci));
+
+       type = req.bRequestType;
+
+       ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
+
+       switch (req.bRequest) {
+       case USB_REQ_CLEAR_FEATURE:
+               if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+                               le16_to_cpu(req.wValue) ==
+                               USB_ENDPOINT_HALT) {
+                       if (req.wLength != 0)
+                               break;
+                       num  = le16_to_cpu(req.wIndex);
+                       dir = num & USB_ENDPOINT_DIR_MASK;
+                       num &= USB_ENDPOINT_NUMBER_MASK;
+                       if (dir) /* TX */
+                               num += ci->hw_ep_max / 2;
+                       if (!ci->ci_hw_ep[num].wedge) {
+                               spin_unlock(&ci->lock);
+                               err = usb_ep_clear_halt(
+                                       &ci->ci_hw_ep[num].ep);
+                               spin_lock(&ci->lock);
+                               if (err)
+                                       break;
+                       }
+                       err = isr_setup_status_phase(ci);
+               } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
+                               le16_to_cpu(req.wValue) ==
+                               USB_DEVICE_REMOTE_WAKEUP) {
+                       if (req.wLength != 0)
+                               break;
+                       ci->remote_wakeup = 0;
+                       err = isr_setup_status_phase(ci);
+               } else {
+                       goto delegate;
+               }
+               break;
+       case USB_REQ_GET_STATUS:
+               if (type != (USB_DIR_IN|USB_RECIP_DEVICE)   &&
+                   type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
+                   type != (USB_DIR_IN|USB_RECIP_INTERFACE))
+                       goto delegate;
+               if (le16_to_cpu(req.wLength) != 2 ||
+                   le16_to_cpu(req.wValue)  != 0)
+                       break;
+               err = isr_get_status_response(ci, &req);
+               break;
+       case USB_REQ_SET_ADDRESS:
+               if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
+                       goto delegate;
+               if (le16_to_cpu(req.wLength) != 0 ||
+                   le16_to_cpu(req.wIndex)  != 0)
+                       break;
+               ci->address = (u8)le16_to_cpu(req.wValue);
+               ci->setaddr = true;
+               err = isr_setup_status_phase(ci);
+               break;
+       case USB_REQ_SET_FEATURE:
+               if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+                               le16_to_cpu(req.wValue) ==
+                               USB_ENDPOINT_HALT) {
+                       if (req.wLength != 0)
+                               break;
+                       num  = le16_to_cpu(req.wIndex);
+                       dir = num & USB_ENDPOINT_DIR_MASK;
+                       num &= USB_ENDPOINT_NUMBER_MASK;
+                       if (dir) /* TX */
+                               num += ci->hw_ep_max / 2;
+
+                       spin_unlock(&ci->lock);
+                       err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
+                       spin_lock(&ci->lock);
+                       if (!err)
+                               isr_setup_status_phase(ci);
+               } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
+                       if (req.wLength != 0)
+                               break;
+                       switch (le16_to_cpu(req.wValue)) {
+                       case USB_DEVICE_REMOTE_WAKEUP:
+                               ci->remote_wakeup = 1;
+                               err = isr_setup_status_phase(ci);
+                               break;
+                       case USB_DEVICE_TEST_MODE:
+                               tmode = le16_to_cpu(req.wIndex) >> 8;
+                               switch (tmode) {
+                               case TEST_J:
+                               case TEST_K:
+                               case TEST_SE0_NAK:
+                               case TEST_PACKET:
+                               case TEST_FORCE_EN:
+                                       ci->test_mode = tmode;
+                                       err = isr_setup_status_phase(
+                                                       ci);
+                                       break;
+                               default:
+                                       break;
+                               }
+                       default:
+                               goto delegate;
+                       }
+               } else {
+                       goto delegate;
+               }
+               break;
+       default:
+delegate:
+               if (req.wLength == 0)   /* no data phase */
+                       ci->ep0_dir = TX;
+
+               spin_unlock(&ci->lock);
+               err = ci->driver->setup(&ci->gadget, &req);
+               spin_lock(&ci->lock);
+               break;
+       }
+
+       if (err < 0) {
+               spin_unlock(&ci->lock);
+               if (usb_ep_set_halt(&hwep->ep))
+                       dev_err(ci->dev, "error: ep_set_halt\n");
+               spin_lock(&ci->lock);
+       }
+}
+
 /**
  * isr_tr_complete_handler: transaction complete interrupt handler
  * @ci: UDC descriptor
@@ -972,12 +1109,10 @@ __releases(ci->lock)
 __acquires(ci->lock)
 {
        unsigned i;
-       u8 tmode = 0;
+       int err;
 
        for (i = 0; i < ci->hw_ep_max; i++) {
                struct ci_hw_ep *hwep  = &ci->ci_hw_ep[i];
-               int type, num, dir, err = -EINVAL;
-               struct usb_ctrlrequest req;
 
                if (hwep->ep.desc == NULL)
                        continue;   /* not configured */
@@ -997,148 +1132,10 @@ __acquires(ci->lock)
                        }
                }
 
-               if (hwep->type != USB_ENDPOINT_XFER_CONTROL ||
-                   !hw_test_and_clear_setup_status(ci, i))
-                       continue;
-
-               if (i != 0) {
-                       dev_warn(ci->dev, "ctrl traffic at endpoint %d\n", i);
-                       continue;
-               }
-
-               /*
-                * Flush data and handshake transactions of previous
-                * setup packet.
-                */
-               _ep_nuke(ci->ep0out);
-               _ep_nuke(ci->ep0in);
-
-               /* read_setup_packet */
-               do {
-                       hw_test_and_set_setup_guard(ci);
-                       memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
-               } while (!hw_test_and_clear_setup_guard(ci));
-
-               type = req.bRequestType;
-
-               ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
-
-               switch (req.bRequest) {
-               case USB_REQ_CLEAR_FEATURE:
-                       if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
-                                       le16_to_cpu(req.wValue) ==
-                                       USB_ENDPOINT_HALT) {
-                               if (req.wLength != 0)
-                                       break;
-                               num  = le16_to_cpu(req.wIndex);
-                               dir = num & USB_ENDPOINT_DIR_MASK;
-                               num &= USB_ENDPOINT_NUMBER_MASK;
-                               if (dir) /* TX */
-                                       num += ci->hw_ep_max/2;
-                               if (!ci->ci_hw_ep[num].wedge) {
-                                       spin_unlock(&ci->lock);
-                                       err = usb_ep_clear_halt(
-                                               &ci->ci_hw_ep[num].ep);
-                                       spin_lock(&ci->lock);
-                                       if (err)
-                                               break;
-                               }
-                               err = isr_setup_status_phase(ci);
-                       } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
-                                       le16_to_cpu(req.wValue) ==
-                                       USB_DEVICE_REMOTE_WAKEUP) {
-                               if (req.wLength != 0)
-                                       break;
-                               ci->remote_wakeup = 0;
-                               err = isr_setup_status_phase(ci);
-                       } else {
-                               goto delegate;
-                       }
-                       break;
-               case USB_REQ_GET_STATUS:
-                       if (type != (USB_DIR_IN|USB_RECIP_DEVICE)   &&
-                           type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
-                           type != (USB_DIR_IN|USB_RECIP_INTERFACE))
-                               goto delegate;
-                       if (le16_to_cpu(req.wLength) != 2 ||
-                           le16_to_cpu(req.wValue)  != 0)
-                               break;
-                       err = isr_get_status_response(ci, &req);
-                       break;
-               case USB_REQ_SET_ADDRESS:
-                       if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
-                               goto delegate;
-                       if (le16_to_cpu(req.wLength) != 0 ||
-                           le16_to_cpu(req.wIndex)  != 0)
-                               break;
-                       ci->address = (u8)le16_to_cpu(req.wValue);
-                       ci->setaddr = true;
-                       err = isr_setup_status_phase(ci);
-                       break;
-               case USB_REQ_SET_FEATURE:
-                       if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
-                                       le16_to_cpu(req.wValue) ==
-                                       USB_ENDPOINT_HALT) {
-                               if (req.wLength != 0)
-                                       break;
-                               num  = le16_to_cpu(req.wIndex);
-                               dir = num & USB_ENDPOINT_DIR_MASK;
-                               num &= USB_ENDPOINT_NUMBER_MASK;
-                               if (dir) /* TX */
-                                       num += ci->hw_ep_max/2;
-
-                               spin_unlock(&ci->lock);
-                               err = usb_ep_set_halt(&ci->ci_hw_ep[num].ep);
-                               spin_lock(&ci->lock);
-                               if (!err)
-                                       isr_setup_status_phase(ci);
-                       } else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
-                               if (req.wLength != 0)
-                                       break;
-                               switch (le16_to_cpu(req.wValue)) {
-                               case USB_DEVICE_REMOTE_WAKEUP:
-                                       ci->remote_wakeup = 1;
-                                       err = isr_setup_status_phase(ci);
-                                       break;
-                               case USB_DEVICE_TEST_MODE:
-                                       tmode = le16_to_cpu(req.wIndex) >> 8;
-                                       switch (tmode) {
-                                       case TEST_J:
-                                       case TEST_K:
-                                       case TEST_SE0_NAK:
-                                       case TEST_PACKET:
-                                       case TEST_FORCE_EN:
-                                               ci->test_mode = tmode;
-                                               err = isr_setup_status_phase(
-                                                               ci);
-                                               break;
-                                       default:
-                                               break;
-                                       }
-                               default:
-                                       goto delegate;
-                               }
-                       } else {
-                               goto delegate;
-                       }
-                       break;
-               default:
-delegate:
-                       if (req.wLength == 0)   /* no data phase */
-                               ci->ep0_dir = TX;
-
-                       spin_unlock(&ci->lock);
-                       err = ci->driver->setup(&ci->gadget, &req);
-                       spin_lock(&ci->lock);
-                       break;
-               }
-
-               if (err < 0) {
-                       spin_unlock(&ci->lock);
-                       if (usb_ep_set_halt(&hwep->ep))
-                               dev_err(ci->dev, "error: ep_set_halt\n");
-                       spin_lock(&ci->lock);
-               }
+               /* Only handle setup packet below */
+               if (i == 0 &&
+                       hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
+                       isr_setup_packet_handler(ci);
        }
 }
 
@@ -1193,6 +1190,11 @@ static int ep_enable(struct usb_ep *ep,
 
        hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE);   /* needed? */
 
+       if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) {
+               dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n");
+               retval = -EINVAL;
+       }
+
        /*
         * Enable endpoints in the HW other than ep0 as ep0
         * is always enabled
@@ -1837,12 +1839,6 @@ void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
 
        dma_pool_destroy(ci->td_pool);
        dma_pool_destroy(ci->qh_pool);
-
-       if (ci->transceiver) {
-               otg_set_peripheral(ci->transceiver->otg, NULL);
-               if (ci->global_phy)
-                       usb_put_phy(ci->transceiver);
-       }
 }
 
 static int udc_id_switch_for_device(struct ci_hdrc *ci)
index 062967c90b2a06e66a081f81ab53aabe1a9e37e4..1ab4df1de2daddb7db1a6032b1b8e9320dd9b591 100644 (file)
@@ -10,7 +10,6 @@
 
 
 #define USB_MAXALTSETTING              128     /* Hard limit */
-#define USB_MAXENDPOINTS               30      /* Hard limit */
 
 #define USB_MAXCONFIG                  8       /* Arbitrary limit */
 
index 2a8afe6754b80245ce9726e8a3e20b7a9bb6dcd0..257876ea03a1a9102ac89d0282fb03affe813103 100644 (file)
@@ -769,6 +769,88 @@ static int check_ctrlrecip(struct usb_dev_state *ps, unsigned int requesttype,
        return ret;
 }
 
+static struct usb_host_endpoint *ep_to_host_endpoint(struct usb_device *dev,
+                                                    unsigned char ep)
+{
+       if (ep & USB_ENDPOINT_DIR_MASK)
+               return dev->ep_in[ep & USB_ENDPOINT_NUMBER_MASK];
+       else
+               return dev->ep_out[ep & USB_ENDPOINT_NUMBER_MASK];
+}
+
+static int parse_usbdevfs_streams(struct usb_dev_state *ps,
+                                 struct usbdevfs_streams __user *streams,
+                                 unsigned int *num_streams_ret,
+                                 unsigned int *num_eps_ret,
+                                 struct usb_host_endpoint ***eps_ret,
+                                 struct usb_interface **intf_ret)
+{
+       unsigned int i, num_streams, num_eps;
+       struct usb_host_endpoint **eps;
+       struct usb_interface *intf = NULL;
+       unsigned char ep;
+       int ifnum, ret;
+
+       if (get_user(num_streams, &streams->num_streams) ||
+           get_user(num_eps, &streams->num_eps))
+               return -EFAULT;
+
+       if (num_eps < 1 || num_eps > USB_MAXENDPOINTS)
+               return -EINVAL;
+
+       /* The XHCI controller allows max 2 ^ 16 streams */
+       if (num_streams_ret && (num_streams < 2 || num_streams > 65536))
+               return -EINVAL;
+
+       eps = kmalloc(num_eps * sizeof(*eps), GFP_KERNEL);
+       if (!eps)
+               return -ENOMEM;
+
+       for (i = 0; i < num_eps; i++) {
+               if (get_user(ep, &streams->eps[i])) {
+                       ret = -EFAULT;
+                       goto error;
+               }
+               eps[i] = ep_to_host_endpoint(ps->dev, ep);
+               if (!eps[i]) {
+                       ret = -EINVAL;
+                       goto error;
+               }
+
+               /* usb_alloc/free_streams operate on an usb_interface */
+               ifnum = findintfep(ps->dev, ep);
+               if (ifnum < 0) {
+                       ret = ifnum;
+                       goto error;
+               }
+
+               if (i == 0) {
+                       ret = checkintf(ps, ifnum);
+                       if (ret < 0)
+                               goto error;
+                       intf = usb_ifnum_to_if(ps->dev, ifnum);
+               } else {
+                       /* Verify all eps belong to the same interface */
+                       if (ifnum != intf->altsetting->desc.bInterfaceNumber) {
+                               ret = -EINVAL;
+                               goto error;
+                       }
+               }
+       }
+
+       if (num_streams_ret)
+               *num_streams_ret = num_streams;
+       *num_eps_ret = num_eps;
+       *eps_ret = eps;
+       *intf_ret = intf;
+
+       return 0;
+
+error:
+       kfree(eps);
+       return ret;
+}
+
 static int match_devt(struct device *dev, void *data)
 {
        return dev->devt == (dev_t) (unsigned long) data;
@@ -1043,6 +1125,20 @@ static int proc_bulk(struct usb_dev_state *ps, void __user *arg)
        return ret;
 }
 
+static void check_reset_of_active_ep(struct usb_device *udev,
+               unsigned int epnum, char *ioctl_name)
+{
+       struct usb_host_endpoint **eps;
+       struct usb_host_endpoint *ep;
+
+       eps = (epnum & USB_DIR_IN) ? udev->ep_in : udev->ep_out;
+       ep = eps[epnum & 0x0f];
+       if (ep && !list_empty(&ep->urb_list))
+               dev_warn(&udev->dev, "Process %d (%s) called USBDEVFS_%s for active endpoint 0x%02x\n",
+                               task_pid_nr(current), current->comm,
+                               ioctl_name, epnum);
+}
+
 static int proc_resetep(struct usb_dev_state *ps, void __user *arg)
 {
        unsigned int ep;
@@ -1056,6 +1152,7 @@ static int proc_resetep(struct usb_dev_state *ps, void __user *arg)
        ret = checkintf(ps, ret);
        if (ret)
                return ret;
+       check_reset_of_active_ep(ps->dev, ep, "RESETEP");
        usb_reset_endpoint(ps->dev, ep);
        return 0;
 }
@@ -1074,6 +1171,7 @@ static int proc_clearhalt(struct usb_dev_state *ps, void __user *arg)
        ret = checkintf(ps, ret);
        if (ret)
                return ret;
+       check_reset_of_active_ep(ps->dev, ep, "CLEAR_HALT");
        if (ep & USB_DIR_IN)
                pipe = usb_rcvbulkpipe(ps->dev, ep & 0x7f);
        else
@@ -1127,6 +1225,9 @@ static int proc_setintf(struct usb_dev_state *ps, void __user *arg)
                return -EFAULT;
        if ((ret = checkintf(ps, setintf.interface)))
                return ret;
+
+       destroy_async_on_interface(ps, setintf.interface);
+
        return usb_set_interface(ps->dev, setintf.interface,
                        setintf.altsetting);
 }
@@ -1189,6 +1290,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
        struct usb_ctrlrequest *dr = NULL;
        unsigned int u, totlen, isofrmlen;
        int i, ret, is_in, num_sgs = 0, ifnum = -1;
+       int number_of_packets = 0;
+       unsigned int stream_id = 0;
        void *buf;
 
        if (uurb->flags & ~(USBDEVFS_URB_ISO_ASAP |
@@ -1209,15 +1312,10 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
                if (ret)
                        return ret;
        }
-       if ((uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0) {
-               is_in = 1;
-               ep = ps->dev->ep_in[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
-       } else {
-               is_in = 0;
-               ep = ps->dev->ep_out[uurb->endpoint & USB_ENDPOINT_NUMBER_MASK];
-       }
+       ep = ep_to_host_endpoint(ps->dev, uurb->endpoint);
        if (!ep)
                return -ENOENT;
+       is_in = (uurb->endpoint & USB_ENDPOINT_DIR_MASK) != 0;
 
        u = 0;
        switch(uurb->type) {
@@ -1242,7 +1340,6 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
                                      le16_to_cpup(&dr->wIndex));
                if (ret)
                        goto error;
-               uurb->number_of_packets = 0;
                uurb->buffer_length = le16_to_cpup(&dr->wLength);
                uurb->buffer += 8;
                if ((dr->bRequestType & USB_DIR_IN) && uurb->buffer_length) {
@@ -1272,17 +1369,17 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
                        uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
                        goto interrupt_urb;
                }
-               uurb->number_of_packets = 0;
                num_sgs = DIV_ROUND_UP(uurb->buffer_length, USB_SG_SIZE);
                if (num_sgs == 1 || num_sgs > ps->dev->bus->sg_tablesize)
                        num_sgs = 0;
+               if (ep->streams)
+                       stream_id = uurb->stream_id;
                break;
 
        case USBDEVFS_URB_TYPE_INTERRUPT:
                if (!usb_endpoint_xfer_int(&ep->desc))
                        return -EINVAL;
  interrupt_urb:
-               uurb->number_of_packets = 0;
                break;
 
        case USBDEVFS_URB_TYPE_ISO:
@@ -1292,15 +1389,16 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
                        return -EINVAL;
                if (!usb_endpoint_xfer_isoc(&ep->desc))
                        return -EINVAL;
+               number_of_packets = uurb->number_of_packets;
                isofrmlen = sizeof(struct usbdevfs_iso_packet_desc) *
-                                  uurb->number_of_packets;
+                                  number_of_packets;
                if (!(isopkt = kmalloc(isofrmlen, GFP_KERNEL)))
                        return -ENOMEM;
                if (copy_from_user(isopkt, iso_frame_desc, isofrmlen)) {
                        ret = -EFAULT;
                        goto error;
                }
-               for (totlen = u = 0; u < uurb->number_of_packets; u++) {
+               for (totlen = u = 0; u < number_of_packets; u++) {
                        /*
                         * arbitrary limit need for USB 3.0
                         * bMaxBurst (0~15 allowed, 1~16 packets)
@@ -1331,7 +1429,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
                ret = -EFAULT;
                goto error;
        }
-       as = alloc_async(uurb->number_of_packets);
+       as = alloc_async(number_of_packets);
        if (!as) {
                ret = -ENOMEM;
                goto error;
@@ -1425,7 +1523,8 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
        as->urb->setup_packet = (unsigned char *)dr;
        dr = NULL;
        as->urb->start_frame = uurb->start_frame;
-       as->urb->number_of_packets = uurb->number_of_packets;
+       as->urb->number_of_packets = number_of_packets;
+       as->urb->stream_id = stream_id;
        if (uurb->type == USBDEVFS_URB_TYPE_ISO ||
                        ps->dev->speed == USB_SPEED_HIGH)
                as->urb->interval = 1 << min(15, ep->desc.bInterval - 1);
@@ -1433,7 +1532,7 @@ static int proc_do_submiturb(struct usb_dev_state *ps, struct usbdevfs_urb *uurb
                as->urb->interval = ep->desc.bInterval;
        as->urb->context = as;
        as->urb->complete = async_completed;
-       for (totlen = u = 0; u < uurb->number_of_packets; u++) {
+       for (totlen = u = 0; u < number_of_packets; u++) {
                as->urb->iso_frame_desc[u].offset = totlen;
                as->urb->iso_frame_desc[u].length = isopkt[u].length;
                totlen += isopkt[u].length;
@@ -1983,6 +2082,45 @@ static int proc_disconnect_claim(struct usb_dev_state *ps, void __user *arg)
        return claimintf(ps, dc.interface);
 }
 
+static int proc_alloc_streams(struct usb_dev_state *ps, void __user *arg)
+{
+       unsigned num_streams, num_eps;
+       struct usb_host_endpoint **eps;
+       struct usb_interface *intf;
+       int r;
+
+       r = parse_usbdevfs_streams(ps, arg, &num_streams, &num_eps,
+                                  &eps, &intf);
+       if (r)
+               return r;
+
+       destroy_async_on_interface(ps,
+                                  intf->altsetting[0].desc.bInterfaceNumber);
+
+       r = usb_alloc_streams(intf, eps, num_eps, num_streams, GFP_KERNEL);
+       kfree(eps);
+       return r;
+}
+
+static int proc_free_streams(struct usb_dev_state *ps, void __user *arg)
+{
+       unsigned num_eps;
+       struct usb_host_endpoint **eps;
+       struct usb_interface *intf;
+       int r;
+
+       r = parse_usbdevfs_streams(ps, arg, NULL, &num_eps, &eps, &intf);
+       if (r)
+               return r;
+
+       destroy_async_on_interface(ps,
+                                  intf->altsetting[0].desc.bInterfaceNumber);
+
+       r = usb_free_streams(intf, eps, num_eps, GFP_KERNEL);
+       kfree(eps);
+       return r;
+}
+
 /*
  * NOTE:  All requests here that have interface numbers as parameters
  * are assuming that somehow the configuration has been prevented from
@@ -2159,6 +2297,12 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
        case USBDEVFS_DISCONNECT_CLAIM:
                ret = proc_disconnect_claim(ps, p);
                break;
+       case USBDEVFS_ALLOC_STREAMS:
+               ret = proc_alloc_streams(ps, p);
+               break;
+       case USBDEVFS_FREE_STREAMS:
+               ret = proc_free_streams(ps, p);
+               break;
        }
        usb_unlock_device(dev);
        if (ret >= 0)
index ab90a01568283c3c0d2a0a3df9b653bfba65a812..888881e5f2926b75dfd86ce44d668adb0b7e6bf6 100644 (file)
@@ -312,9 +312,9 @@ static int usb_probe_interface(struct device *dev)
                return error;
        }
 
-       id = usb_match_id(intf, driver->id_table);
+       id = usb_match_dynamic_id(intf, driver);
        if (!id)
-               id = usb_match_dynamic_id(intf, driver);
+               id = usb_match_id(intf, driver->id_table);
        if (!id)
                return error;
 
@@ -400,8 +400,9 @@ static int usb_unbind_interface(struct device *dev)
 {
        struct usb_driver *driver = to_usb_driver(dev->driver);
        struct usb_interface *intf = to_usb_interface(dev);
+       struct usb_host_endpoint *ep, **eps = NULL;
        struct usb_device *udev;
-       int error, r, lpm_disable_error;
+       int i, j, error, r, lpm_disable_error;
 
        intf->condition = USB_INTERFACE_UNBINDING;
 
@@ -425,6 +426,26 @@ static int usb_unbind_interface(struct device *dev)
        driver->disconnect(intf);
        usb_cancel_queued_reset(intf);
 
+       /* Free streams */
+       for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
+               ep = &intf->cur_altsetting->endpoint[i];
+               if (ep->streams == 0)
+                       continue;
+               if (j == 0) {
+                       eps = kmalloc(USB_MAXENDPOINTS * sizeof(void *),
+                                     GFP_KERNEL);
+                       if (!eps) {
+                               dev_warn(dev, "oom, leaking streams\n");
+                               break;
+                       }
+               }
+               eps[j++] = ep;
+       }
+       if (j) {
+               usb_free_streams(intf, eps, j, GFP_KERNEL);
+               kfree(eps);
+       }
+
        /* Reset other interface state.
         * We cannot do a Set-Interface if the device is suspended or
         * if it is prepared for a system sleep (since installing a new
@@ -990,8 +1011,7 @@ EXPORT_SYMBOL_GPL(usb_deregister);
  * it doesn't support pre_reset/post_reset/reset_resume or
  * because it doesn't support suspend/resume.
  *
- * The caller must hold @intf's device's lock, but not its pm_mutex
- * and not @intf->dev.sem.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
  */
 void usb_forced_unbind_intf(struct usb_interface *intf)
 {
@@ -1004,16 +1024,37 @@ void usb_forced_unbind_intf(struct usb_interface *intf)
        intf->needs_binding = 1;
 }
 
+/*
+ * Unbind drivers for @udev's marked interfaces.  These interfaces have
+ * the needs_binding flag set, for example by usb_resume_interface().
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_marked_interfaces(struct usb_device *udev)
+{
+       struct usb_host_config  *config;
+       int                     i;
+       struct usb_interface    *intf;
+
+       config = udev->actconfig;
+       if (config) {
+               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
+                       intf = config->interface[i];
+                       if (intf->dev.driver && intf->needs_binding)
+                               usb_forced_unbind_intf(intf);
+               }
+       }
+}
+
 /* Delayed forced unbinding of a USB interface driver and scan
  * for rebinding.
  *
- * The caller must hold @intf's device's lock, but not its pm_mutex
- * and not @intf->dev.sem.
+ * The caller must hold @intf's device's lock, but not @intf's lock.
  *
  * Note: Rebinds will be skipped if a system sleep transition is in
  * progress and the PM "complete" callback hasn't occurred yet.
  */
-void usb_rebind_intf(struct usb_interface *intf)
+static void usb_rebind_intf(struct usb_interface *intf)
 {
        int rc;
 
@@ -1030,68 +1071,66 @@ void usb_rebind_intf(struct usb_interface *intf)
        }
 }
 
-#ifdef CONFIG_PM
-
-/* Unbind drivers for @udev's interfaces that don't support suspend/resume
- * There is no check for reset_resume here because it can be determined
- * only during resume whether reset_resume is needed.
+/*
+ * Rebind drivers to @udev's marked interfaces.  These interfaces have
+ * the needs_binding flag set.
  *
  * The caller must hold @udev's device lock.
  */
-static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
+static void rebind_marked_interfaces(struct usb_device *udev)
 {
        struct usb_host_config  *config;
        int                     i;
        struct usb_interface    *intf;
-       struct usb_driver       *drv;
 
        config = udev->actconfig;
        if (config) {
                for (i = 0; i < config->desc.bNumInterfaces; ++i) {
                        intf = config->interface[i];
-
-                       if (intf->dev.driver) {
-                               drv = to_usb_driver(intf->dev.driver);
-                               if (!drv->suspend || !drv->resume)
-                                       usb_forced_unbind_intf(intf);
-                       }
+                       if (intf->needs_binding)
+                               usb_rebind_intf(intf);
                }
        }
 }
 
-/* Unbind drivers for @udev's interfaces that failed to support reset-resume.
- * These interfaces have the needs_binding flag set by usb_resume_interface().
+/*
+ * Unbind all of @udev's marked interfaces and then rebind all of them.
+ * This ordering is necessary because some drivers claim several interfaces
+ * when they are first probed.
  *
  * The caller must hold @udev's device lock.
  */
-static void unbind_no_reset_resume_drivers_interfaces(struct usb_device *udev)
+void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev)
 {
-       struct usb_host_config  *config;
-       int                     i;
-       struct usb_interface    *intf;
-
-       config = udev->actconfig;
-       if (config) {
-               for (i = 0; i < config->desc.bNumInterfaces; ++i) {
-                       intf = config->interface[i];
-                       if (intf->dev.driver && intf->needs_binding)
-                               usb_forced_unbind_intf(intf);
-               }
-       }
+       unbind_marked_interfaces(udev);
+       rebind_marked_interfaces(udev);
 }
 
-static void do_rebind_interfaces(struct usb_device *udev)
+#ifdef CONFIG_PM
+
+/* Unbind drivers for @udev's interfaces that don't support suspend/resume
+ * There is no check for reset_resume here because it can be determined
+ * only during resume whether reset_resume is needed.
+ *
+ * The caller must hold @udev's device lock.
+ */
+static void unbind_no_pm_drivers_interfaces(struct usb_device *udev)
 {
        struct usb_host_config  *config;
        int                     i;
        struct usb_interface    *intf;
+       struct usb_driver       *drv;
 
        config = udev->actconfig;
        if (config) {
                for (i = 0; i < config->desc.bNumInterfaces; ++i) {
                        intf = config->interface[i];
-                       if (intf->needs_binding)
-                               usb_rebind_intf(intf);
+
+                       if (intf->dev.driver) {
+                               drv = to_usb_driver(intf->dev.driver);
+                               if (!drv->suspend || !drv->resume)
+                                       usb_forced_unbind_intf(intf);
+                       }
                }
        }
 }
@@ -1420,7 +1459,7 @@ int usb_resume_complete(struct device *dev)
         * whose needs_binding flag is set
         */
        if (udev->state != USB_STATE_NOTATTACHED)
-               do_rebind_interfaces(udev);
+               rebind_marked_interfaces(udev);
        return 0;
 }
 
@@ -1442,7 +1481,7 @@ int usb_resume(struct device *dev, pm_message_t msg)
                pm_runtime_disable(dev);
                pm_runtime_set_active(dev);
                pm_runtime_enable(dev);
-               unbind_no_reset_resume_drivers_interfaces(udev);
+               unbind_marked_interfaces(udev);
        }
 
        /* Avoid PM error messages for devices disconnected while suspended
index 2518c325075093a9c26044f2d9dd18483dc7dd60..9c4e2922b04d0f2c44b405f2074085896d75a7ce 100644 (file)
@@ -2049,7 +2049,7 @@ int usb_alloc_streams(struct usb_interface *interface,
 {
        struct usb_hcd *hcd;
        struct usb_device *dev;
-       int i;
+       int i, ret;
 
        dev = interface_to_usbdev(interface);
        hcd = bus_to_hcd(dev->bus);
@@ -2058,13 +2058,24 @@ int usb_alloc_streams(struct usb_interface *interface,
        if (dev->speed != USB_SPEED_SUPER)
                return -EINVAL;
 
-       /* Streams only apply to bulk endpoints. */
-       for (i = 0; i < num_eps; i++)
+       for (i = 0; i < num_eps; i++) {
+               /* Streams only apply to bulk endpoints. */
                if (!usb_endpoint_xfer_bulk(&eps[i]->desc))
                        return -EINVAL;
+               /* Re-alloc is not allowed */
+               if (eps[i]->streams)
+                       return -EINVAL;
+       }
 
-       return hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
+       ret = hcd->driver->alloc_streams(hcd, dev, eps, num_eps,
                        num_streams, mem_flags);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < num_eps; i++)
+               eps[i]->streams = ret;
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(usb_alloc_streams);
 
@@ -2078,8 +2089,7 @@ EXPORT_SYMBOL_GPL(usb_alloc_streams);
  * Reverts a group of bulk endpoints back to not using stream IDs.
  * Can fail if we are given bad arguments, or HCD is broken.
  *
- * Return: On success, the number of allocated streams. On failure, a negative
- * error code.
+ * Return: 0 on success. On failure, a negative error code.
  */
 int usb_free_streams(struct usb_interface *interface,
                struct usb_host_endpoint **eps, unsigned int num_eps,
@@ -2087,19 +2097,26 @@ int usb_free_streams(struct usb_interface *interface,
 {
        struct usb_hcd *hcd;
        struct usb_device *dev;
-       int i;
+       int i, ret;
 
        dev = interface_to_usbdev(interface);
        hcd = bus_to_hcd(dev->bus);
        if (dev->speed != USB_SPEED_SUPER)
                return -EINVAL;
 
-       /* Streams only apply to bulk endpoints. */
+       /* Double-free is not allowed */
        for (i = 0; i < num_eps; i++)
-               if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
+               if (!eps[i] || !eps[i]->streams)
                        return -EINVAL;
 
-       return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+       ret = hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+       if (ret < 0)
+               return ret;
+
+       for (i = 0; i < num_eps; i++)
+               eps[i]->streams = 0;
+
+       return ret;
 }
 EXPORT_SYMBOL_GPL(usb_free_streams);
 
index 610735823d93fa94e54cbb231d7d4d56f7b38825..090469ebfcfff3a1fb686c4adbf0817bc1b27f22 100644 (file)
@@ -141,19 +141,27 @@ static int usb_device_supports_lpm(struct usb_device *udev)
                return 0;
        }
 
-       /* All USB 3.0 must support LPM, but we need their max exit latency
-        * information from the SuperSpeed Extended Capabilities BOS descriptor.
+       /*
+        * According to the USB 3.0 spec, all USB 3.0 devices must support LPM.
+        * However, there are some that don't, and they set the U1/U2 exit
+        * latencies to zero.
         */
        if (!udev->bos->ss_cap) {
-               dev_warn(&udev->dev, "No LPM exit latency info found.  "
-                               "Power management will be impacted.\n");
+               dev_info(&udev->dev, "No LPM exit latency info found, disabling LPM.\n");
+               return 0;
+       }
+
+       if (udev->bos->ss_cap->bU1devExitLat == 0 &&
+                       udev->bos->ss_cap->bU2DevExitLat == 0) {
+               if (udev->parent)
+                       dev_info(&udev->dev, "LPM exit latency is zeroed, disabling LPM.\n");
+               else
+                       dev_info(&udev->dev, "We don't know the algorithms for LPM for this host, disabling LPM.\n");
                return 0;
        }
-       if (udev->parent->lpm_capable)
-               return 1;
 
-       dev_warn(&udev->dev, "Parent hub missing LPM exit latency info.  "
-                       "Power management will be impacted.\n");
+       if (!udev->parent || udev->parent->lpm_capable)
+               return 1;
        return 0;
 }
 
@@ -499,7 +507,8 @@ static void led_work (struct work_struct *work)
                changed++;
        }
        if (changed)
-               schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+               queue_delayed_work(system_power_efficient_wq,
+                               &hub->leds, LED_CYCLE_PERIOD);
 }
 
 /* use a short timeout for hub/port status fetches */
@@ -1041,7 +1050,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                if (type == HUB_INIT) {
                        delay = hub_power_on(hub, false);
                        INIT_DELAYED_WORK(&hub->init_work, hub_init_func2);
-                       schedule_delayed_work(&hub->init_work,
+                       queue_delayed_work(system_power_efficient_wq,
+                                       &hub->init_work,
                                        msecs_to_jiffies(delay));
 
                        /* Suppress autosuspend until init is done */
@@ -1195,7 +1205,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                /* Don't do a long sleep inside a workqueue routine */
                if (type == HUB_INIT2) {
                        INIT_DELAYED_WORK(&hub->init_work, hub_init_func3);
-                       schedule_delayed_work(&hub->init_work,
+                       queue_delayed_work(system_power_efficient_wq,
+                                       &hub->init_work,
                                        msecs_to_jiffies(delay));
                        return;         /* Continues at init3: below */
                } else {
@@ -1209,7 +1220,8 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
        if (status < 0)
                dev_err(hub->intfdev, "activate --> %d\n", status);
        if (hub->has_indicators && blinkenlights)
-               schedule_delayed_work(&hub->leds, LED_CYCLE_PERIOD);
+               queue_delayed_work(system_power_efficient_wq,
+                               &hub->leds, LED_CYCLE_PERIOD);
 
        /* Scan all ports that need attention */
        kick_khubd(hub);
@@ -3095,9 +3107,19 @@ static int finish_port_resume(struct usb_device *udev)
         * operation is carried out here, after the port has been
         * resumed.
         */
-       if (udev->reset_resume)
+       if (udev->reset_resume) {
+               /*
+                * If the device morphs or switches modes when it is reset,
+                * we don't want to perform a reset-resume.  We'll fail the
+                * resume, which will cause a logical disconnect, and then
+                * the device will be rediscovered.
+                */
  retry_reset_resume:
-               status = usb_reset_and_verify_device(udev);
+               if (udev->quirks & USB_QUIRK_RESET)
+                       status = -ENODEV;
+               else
+                       status = usb_reset_and_verify_device(udev);
+       }
 
        /* 10.5.4.5 says be sure devices in the tree are still there.
         * For now let's assume the device didn't go crazy on resume,
@@ -3960,7 +3982,7 @@ static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
        connect_type = usb_get_hub_port_connect_type(udev->parent,
                        udev->portnum);
 
-       if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
+       if ((udev->bos->ext_cap->bmAttributes & cpu_to_le32(USB_BESL_SUPPORT)) ||
                        connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
                udev->usb2_hw_lpm_allowed = 1;
                usb_set_usb2_hardware_lpm(udev, 1);
@@ -4109,8 +4131,12 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 
                        did_new_scheme = true;
                        retval = hub_enable_device(udev);
-                       if (retval < 0)
+                       if (retval < 0) {
+                               dev_err(&udev->dev,
+                                       "hub failed to enable device, error %d\n",
+                                       retval);
                                goto fail;
+                       }
 
 #define GET_DESCRIPTOR_BUFSIZE 64
                        buf = kmalloc(GET_DESCRIPTOR_BUFSIZE, GFP_NOIO);
@@ -4313,7 +4339,8 @@ check_highspeed (struct usb_hub *hub, struct usb_device *udev, int port1)
                /* hub LEDs are probably harder to miss than syslog */
                if (hub->has_indicators) {
                        hub->indicator[port1-1] = INDICATOR_GREEN_BLINK;
-                       schedule_delayed_work (&hub->leds, 0);
+                       queue_delayed_work(system_power_efficient_wq,
+                                       &hub->leds, 0);
                }
        }
        kfree(qual);
@@ -4542,7 +4569,9 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                                if (hub->has_indicators) {
                                        hub->indicator[port1-1] =
                                                INDICATOR_AMBER_BLINK;
-                                       schedule_delayed_work (&hub->leds, 0);
+                                       queue_delayed_work(
+                                               system_power_efficient_wq,
+                                               &hub->leds, 0);
                                }
                                status = -ENOTCONN;     /* Don't retry */
                                goto loop_disable;
@@ -4741,6 +4770,8 @@ static void hub_events(void)
 
                /* deal with port status changes */
                for (i = 1; i <= hdev->maxchild; i++) {
+                       struct usb_device *udev = hub->ports[i - 1]->child;
+
                        if (test_bit(i, hub->busy_bits))
                                continue;
                        connect_change = test_bit(i, hub->change_bits);
@@ -4839,8 +4870,6 @@ static void hub_events(void)
                         */
                        if (hub_port_warm_reset_required(hub, portstatus)) {
                                int status;
-                               struct usb_device *udev =
-                                       hub->ports[i - 1]->child;
 
                                dev_dbg(hub_dev, "warm reset port %d\n", i);
                                if (!udev ||
@@ -4857,6 +4886,24 @@ static void hub_events(void)
                                        usb_unlock_device(udev);
                                        connect_change = 0;
                                }
+                       /*
+                        * On disconnect USB3 protocol ports transit from U0 to
+                        * SS.Inactive to Rx.Detect. If this happens a warm-
+                        * reset is not needed, but a (re)connect may happen
+                        * before khubd runs and sees the disconnect, and the
+                        * device may be an unknown state.
+                        *
+                        * If the port went through SS.Inactive without khubd
+                        * seeing it the C_LINK_STATE change flag will be set,
+                        * and we reset the dev to put it in a known state.
+                        */
+                       } else if (udev && hub_is_superspeed(hub->hdev) &&
+                                  (portchange & USB_PORT_STAT_C_LINK_STATE) &&
+                                  (portstatus & USB_PORT_STAT_CONNECTION)) {
+                               usb_lock_device(udev);
+                               usb_reset_device(udev);
+                               usb_unlock_device(udev);
+                               connect_change = 0;
                        }
 
                        if (connect_change)
@@ -5114,7 +5161,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        struct usb_hcd                  *hcd = bus_to_hcd(udev->bus);
        struct usb_device_descriptor    descriptor = udev->descriptor;
        struct usb_host_bos             *bos;
-       int                             i, ret = 0;
+       int                             i, j, ret = 0;
        int                             port1 = udev->portnum;
 
        if (udev->state == USB_STATE_NOTATTACHED ||
@@ -5240,6 +5287,9 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                                ret);
                        goto re_enumerate;
                }
+               /* Resetting also frees any allocated streams */
+               for (j = 0; j < intf->cur_altsetting->desc.bNumEndpoints; j++)
+                       intf->cur_altsetting->endpoint[j].streams = 0;
        }
 
 done:
@@ -5342,10 +5392,11 @@ int usb_reset_device(struct usb_device *udev)
                                else if (cintf->condition ==
                                                USB_INTERFACE_BOUND)
                                        rebind = 1;
+                               if (rebind)
+                                       cintf->needs_binding = 1;
                        }
-                       if (ret == 0 && rebind)
-                               usb_rebind_intf(cintf);
                }
+               usb_unbind_and_rebind_marked_interfaces(udev);
        }
 
        usb_autosuspend_device(udev);
index 08d95e9d56c2162f52b824fcecd84f8536a64b4e..3cdcd0a2c0b722b2aca23e9fb937b5db3ce023e0 100644 (file)
@@ -1293,8 +1293,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
        struct usb_interface *iface;
        struct usb_host_interface *alt;
        struct usb_hcd *hcd = bus_to_hcd(dev->bus);
-       int ret;
-       int manual = 0;
+       int i, ret, manual = 0;
        unsigned int epaddr;
        unsigned int pipe;
 
@@ -1329,6 +1328,10 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
                mutex_unlock(hcd->bandwidth_mutex);
                return -ENOMEM;
        }
+       /* Changing alt-setting also frees any allocated streams */
+       for (i = 0; i < iface->cur_altsetting->desc.bNumEndpoints; i++)
+               iface->cur_altsetting->endpoint[i].streams = 0;
+
        ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
        if (ret < 0) {
                dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
index a87532845bf9b9e1eecceff9ab1d5a064e7607fe..75bf649da82d58198f38752874f39a00e726065e 100644 (file)
@@ -55,7 +55,7 @@ extern int usb_match_one_id_intf(struct usb_device *dev,
 extern int usb_match_device(struct usb_device *dev,
                            const struct usb_device_id *id);
 extern void usb_forced_unbind_intf(struct usb_interface *intf);
-extern void usb_rebind_intf(struct usb_interface *intf);
+extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
 
 extern void usb_hub_release_all_ports(struct usb_device *hdev,
                struct usb_dev_state *owner);
index 8205799e6db32082cfd36aa9343464fa16283cf0..c93918b70d032e88654f8db40873e98b03112da7 100644 (file)
@@ -71,6 +71,26 @@ static const char *dwc2_op_state_str(struct dwc2_hsotg *hsotg)
        }
 }
 
+/**
+ * dwc2_handle_usb_port_intr - handles OTG PRTINT interrupts.
+ * When the PRTINT interrupt fires, there are certain status bits in the Host
+ * Port that needs to get cleared.
+ *
+ * @hsotg: Programming view of DWC_otg controller
+ */
+static void dwc2_handle_usb_port_intr(struct dwc2_hsotg *hsotg)
+{
+       u32 hprt0 = readl(hsotg->regs + HPRT0);
+
+       if (hprt0 & HPRT0_ENACHG) {
+               hprt0 &= ~HPRT0_ENA;
+               writel(hprt0, hsotg->regs + HPRT0);
+       }
+
+       /* Clear interrupt */
+       writel(GINTSTS_PRTINT, hsotg->regs + GINTSTS);
+}
+
 /**
  * dwc2_handle_mode_mismatch_intr() - Logs a mode mismatch warning message
  *
@@ -479,9 +499,8 @@ irqreturn_t dwc2_handle_common_intr(int irq, void *dev)
                if (dwc2_is_device_mode(hsotg)) {
                        dev_dbg(hsotg->dev,
                                " --Port interrupt received in Device mode--\n");
-                       gintsts = GINTSTS_PRTINT;
-                       writel(gintsts, hsotg->regs + GINTSTS);
-                       retval = 1;
+                       dwc2_handle_usb_port_intr(hsotg);
+                       retval = IRQ_HANDLED;
                }
        }
 
index 012f17ec1a37d3fa6b60f0a289e547e5cf054381..47b9eb5389b468d4710263bdd9a8d052cbba635c 100644 (file)
@@ -975,8 +975,8 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
                                  struct dwc2_qtd *qtd)
 {
        struct dwc2_hcd_urb *urb = qtd->urb;
-       int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
        enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE;
+       int pipe_type;
        int urb_xfer_done;
 
        if (dbg_hc(chan))
@@ -984,6 +984,11 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
                         "--Host Channel %d Interrupt: Transfer Complete--\n",
                         chnum);
 
+       if (!urb)
+               goto handle_xfercomp_done;
+
+       pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+
        if (hsotg->core_params->dma_desc_enable > 0) {
                dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status);
                if (pipe_type == USB_ENDPOINT_XFER_ISOC)
@@ -1005,9 +1010,6 @@ static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
                }
        }
 
-       if (!urb)
-               goto handle_xfercomp_done;
-
        /* Update the QTD and URB states */
        switch (pipe_type) {
        case USB_ENDPOINT_XFER_CONTROL:
@@ -1105,7 +1107,7 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
                               struct dwc2_qtd *qtd)
 {
        struct dwc2_hcd_urb *urb = qtd->urb;
-       int pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+       int pipe_type;
 
        dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n",
                chnum);
@@ -1119,6 +1121,8 @@ static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
        if (!urb)
                goto handle_stall_halt;
 
+       pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
+
        if (pipe_type == USB_ENDPOINT_XFER_CONTROL)
                dwc2_host_complete(hsotg, qtd, -EPIPE);
 
index a49217ae35333846bb76912666f0efa12bca6813..d001417e8e370cd7f3e7fa2d9b9c2678271987dc 100644 (file)
@@ -61,9 +61,10 @@ void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
  * dwc3_core_soft_reset - Issues core soft reset and PHY reset
  * @dwc: pointer to our context structure
  */
-static void dwc3_core_soft_reset(struct dwc3 *dwc)
+static int dwc3_core_soft_reset(struct dwc3 *dwc)
 {
        u32             reg;
+       int             ret;
 
        /* Before Resetting PHY, put Core in Reset */
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
@@ -82,6 +83,15 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
 
        usb_phy_init(dwc->usb2_phy);
        usb_phy_init(dwc->usb3_phy);
+       ret = phy_init(dwc->usb2_generic_phy);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_init(dwc->usb3_generic_phy);
+       if (ret < 0) {
+               phy_exit(dwc->usb2_generic_phy);
+               return ret;
+       }
        mdelay(100);
 
        /* Clear USB3 PHY reset */
@@ -100,6 +110,8 @@ static void dwc3_core_soft_reset(struct dwc3 *dwc)
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
        reg &= ~DWC3_GCTL_CORESOFTRESET;
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
+
+       return 0;
 }
 
 /**
@@ -242,6 +254,90 @@ static void dwc3_event_buffers_cleanup(struct dwc3 *dwc)
        }
 }
 
+static int dwc3_alloc_scratch_buffers(struct dwc3 *dwc)
+{
+       if (!dwc->has_hibernation)
+               return 0;
+
+       if (!dwc->nr_scratch)
+               return 0;
+
+       dwc->scratchbuf = kmalloc_array(dwc->nr_scratch,
+                       DWC3_SCRATCHBUF_SIZE, GFP_KERNEL);
+       if (!dwc->scratchbuf)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static int dwc3_setup_scratch_buffers(struct dwc3 *dwc)
+{
+       dma_addr_t scratch_addr;
+       u32 param;
+       int ret;
+
+       if (!dwc->has_hibernation)
+               return 0;
+
+       if (!dwc->nr_scratch)
+               return 0;
+
+        /* should never fall here */
+       if (!WARN_ON(dwc->scratchbuf))
+               return 0;
+
+       scratch_addr = dma_map_single(dwc->dev, dwc->scratchbuf,
+                       dwc->nr_scratch * DWC3_SCRATCHBUF_SIZE,
+                       DMA_BIDIRECTIONAL);
+       if (dma_mapping_error(dwc->dev, scratch_addr)) {
+               dev_err(dwc->dev, "failed to map scratch buffer\n");
+               ret = -EFAULT;
+               goto err0;
+       }
+
+       dwc->scratch_addr = scratch_addr;
+
+       param = lower_32_bits(scratch_addr);
+
+       ret = dwc3_send_gadget_generic_command(dwc,
+                       DWC3_DGCMD_SET_SCRATCHPAD_ADDR_LO, param);
+       if (ret < 0)
+               goto err1;
+
+       param = upper_32_bits(scratch_addr);
+
+       ret = dwc3_send_gadget_generic_command(dwc,
+                       DWC3_DGCMD_SET_SCRATCHPAD_ADDR_HI, param);
+       if (ret < 0)
+               goto err1;
+
+       return 0;
+
+err1:
+       dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+                       DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+
+err0:
+       return ret;
+}
+
+static void dwc3_free_scratch_buffers(struct dwc3 *dwc)
+{
+       if (!dwc->has_hibernation)
+               return;
+
+       if (!dwc->nr_scratch)
+               return;
+
+        /* should never fall here */
+       if (!WARN_ON(dwc->scratchbuf))
+               return;
+
+       dma_unmap_single(dwc->dev, dwc->scratch_addr, dwc->nr_scratch *
+                       DWC3_SCRATCHBUF_SIZE, DMA_BIDIRECTIONAL);
+       kfree(dwc->scratchbuf);
+}
+
 static void dwc3_core_num_eps(struct dwc3 *dwc)
 {
        struct dwc3_hwparams    *parms = &dwc->hwparams;
@@ -277,6 +373,7 @@ static void dwc3_cache_hwparams(struct dwc3 *dwc)
 static int dwc3_core_init(struct dwc3 *dwc)
 {
        unsigned long           timeout;
+       u32                     hwparams4 = dwc->hwparams.hwparams4;
        u32                     reg;
        int                     ret;
 
@@ -306,7 +403,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
                cpu_relax();
        } while (true);
 
-       dwc3_core_soft_reset(dwc);
+       ret = dwc3_core_soft_reset(dwc);
+       if (ret)
+               goto err0;
 
        reg = dwc3_readl(dwc->regs, DWC3_GCTL);
        reg &= ~DWC3_GCTL_SCALEDOWN_MASK;
@@ -314,7 +413,29 @@ static int dwc3_core_init(struct dwc3 *dwc)
 
        switch (DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1)) {
        case DWC3_GHWPARAMS1_EN_PWROPT_CLK:
-               reg &= ~DWC3_GCTL_DSBLCLKGTNG;
+               /**
+                * WORKAROUND: DWC3 revisions between 2.10a and 2.50a have an
+                * issue which would cause xHCI compliance tests to fail.
+                *
+                * Because of that we cannot enable clock gating on such
+                * configurations.
+                *
+                * Refers to:
+                *
+                * STAR#9000588375: Clock Gating, SOF Issues when ref_clk-Based
+                * SOF/ITP Mode Used
+                */
+               if ((dwc->dr_mode == USB_DR_MODE_HOST ||
+                               dwc->dr_mode == USB_DR_MODE_OTG) &&
+                               (dwc->revision >= DWC3_REVISION_210A &&
+                               dwc->revision <= DWC3_REVISION_250A))
+                       reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
+               else
+                       reg &= ~DWC3_GCTL_DSBLCLKGTNG;
+               break;
+       case DWC3_GHWPARAMS1_EN_PWROPT_HIB:
+               /* enable hibernation here */
+               dwc->nr_scratch = DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(hwparams4);
                break;
        default:
                dev_dbg(dwc->dev, "No power optimization available\n");
@@ -333,16 +454,36 @@ static int dwc3_core_init(struct dwc3 *dwc)
 
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
 
+       ret = dwc3_alloc_scratch_buffers(dwc);
+       if (ret)
+               goto err1;
+
+       ret = dwc3_setup_scratch_buffers(dwc);
+       if (ret)
+               goto err2;
+
        return 0;
 
+err2:
+       dwc3_free_scratch_buffers(dwc);
+
+err1:
+       usb_phy_shutdown(dwc->usb2_phy);
+       usb_phy_shutdown(dwc->usb3_phy);
+       phy_exit(dwc->usb2_generic_phy);
+       phy_exit(dwc->usb3_generic_phy);
+
 err0:
        return ret;
 }
 
 static void dwc3_core_exit(struct dwc3 *dwc)
 {
+       dwc3_free_scratch_buffers(dwc);
        usb_phy_shutdown(dwc->usb2_phy);
        usb_phy_shutdown(dwc->usb3_phy);
+       phy_exit(dwc->usb2_generic_phy);
+       phy_exit(dwc->usb3_generic_phy);
 }
 
 #define DWC3_ALIGN_MASK                (16 - 1)
@@ -411,32 +552,52 @@ static int dwc3_probe(struct platform_device *pdev)
 
        if (IS_ERR(dwc->usb2_phy)) {
                ret = PTR_ERR(dwc->usb2_phy);
-
-               /*
-                * 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)
+               if (ret == -ENXIO || ret == -ENODEV) {
+                       dwc->usb2_phy = NULL;
+               } else if (ret == -EPROBE_DEFER) {
                        return ret;
-
-               dev_err(dev, "no usb2 phy configured\n");
-               return -EPROBE_DEFER;
+               } else {
+                       dev_err(dev, "no usb2 phy configured\n");
+                       return ret;
+               }
        }
 
        if (IS_ERR(dwc->usb3_phy)) {
                ret = PTR_ERR(dwc->usb3_phy);
+               if (ret == -ENXIO || ret == -ENODEV) {
+                       dwc->usb3_phy = NULL;
+               } else if (ret == -EPROBE_DEFER) {
+                       return ret;
+               } else {
+                       dev_err(dev, "no usb3 phy configured\n");
+                       return ret;
+               }
+       }
 
-               /*
-                * 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)
+       dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
+       if (IS_ERR(dwc->usb2_generic_phy)) {
+               ret = PTR_ERR(dwc->usb2_generic_phy);
+               if (ret == -ENOSYS || ret == -ENODEV) {
+                       dwc->usb2_generic_phy = NULL;
+               } else if (ret == -EPROBE_DEFER) {
+                       return ret;
+               } else {
+                       dev_err(dev, "no usb2 phy configured\n");
                        return ret;
+               }
+       }
 
-               dev_err(dev, "no usb3 phy configured\n");
-               return -EPROBE_DEFER;
+       dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
+       if (IS_ERR(dwc->usb3_generic_phy)) {
+               ret = PTR_ERR(dwc->usb3_generic_phy);
+               if (ret == -ENOSYS || ret == -ENODEV) {
+                       dwc->usb3_generic_phy = NULL;
+               } else if (ret == -EPROBE_DEFER) {
+                       return ret;
+               } else {
+                       dev_err(dev, "no usb3 phy configured\n");
+                       return ret;
+               }
        }
 
        dwc->xhci_resources[0].start = res->start;
@@ -479,6 +640,14 @@ static int dwc3_probe(struct platform_device *pdev)
                goto err0;
        }
 
+       if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
+               dwc->dr_mode = USB_DR_MODE_HOST;
+       else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
+               dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
+
+       if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
+               dwc->dr_mode = USB_DR_MODE_OTG;
+
        ret = dwc3_core_init(dwc);
        if (ret) {
                dev_err(dev, "failed to initialize core\n");
@@ -487,21 +656,20 @@ static int dwc3_probe(struct platform_device *pdev)
 
        usb_phy_set_suspend(dwc->usb2_phy, 0);
        usb_phy_set_suspend(dwc->usb3_phy, 0);
+       ret = phy_power_on(dwc->usb2_generic_phy);
+       if (ret < 0)
+               goto err1;
+
+       ret = phy_power_on(dwc->usb3_generic_phy);
+       if (ret < 0)
+               goto err_usb2phy_power;
 
        ret = dwc3_event_buffers_setup(dwc);
        if (ret) {
                dev_err(dwc->dev, "failed to setup event buffers\n");
-               goto err1;
+               goto err_usb3phy_power;
        }
 
-       if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
-               dwc->dr_mode = USB_DR_MODE_HOST;
-       else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET))
-               dwc->dr_mode = USB_DR_MODE_PERIPHERAL;
-
-       if (dwc->dr_mode == USB_DR_MODE_UNKNOWN)
-               dwc->dr_mode = USB_DR_MODE_OTG;
-
        switch (dwc->dr_mode) {
        case USB_DR_MODE_PERIPHERAL:
                dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE);
@@ -568,6 +736,12 @@ err3:
 err2:
        dwc3_event_buffers_cleanup(dwc);
 
+err_usb3phy_power:
+       phy_power_off(dwc->usb3_generic_phy);
+
+err_usb2phy_power:
+       phy_power_off(dwc->usb2_generic_phy);
+
 err1:
        usb_phy_set_suspend(dwc->usb2_phy, 1);
        usb_phy_set_suspend(dwc->usb3_phy, 1);
@@ -585,6 +759,8 @@ static int dwc3_remove(struct platform_device *pdev)
 
        usb_phy_set_suspend(dwc->usb2_phy, 1);
        usb_phy_set_suspend(dwc->usb3_phy, 1);
+       phy_power_off(dwc->usb2_generic_phy);
+       phy_power_off(dwc->usb3_generic_phy);
 
        pm_runtime_put_sync(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
@@ -682,6 +858,8 @@ static int dwc3_suspend(struct device *dev)
 
        usb_phy_shutdown(dwc->usb3_phy);
        usb_phy_shutdown(dwc->usb2_phy);
+       phy_exit(dwc->usb2_generic_phy);
+       phy_exit(dwc->usb3_generic_phy);
 
        return 0;
 }
@@ -690,9 +868,17 @@ static int dwc3_resume(struct device *dev)
 {
        struct dwc3     *dwc = dev_get_drvdata(dev);
        unsigned long   flags;
+       int             ret;
 
        usb_phy_init(dwc->usb3_phy);
        usb_phy_init(dwc->usb2_phy);
+       ret = phy_init(dwc->usb2_generic_phy);
+       if (ret < 0)
+               return ret;
+
+       ret = phy_init(dwc->usb3_generic_phy);
+       if (ret < 0)
+               goto err_usb2phy_init;
 
        spin_lock_irqsave(&dwc->lock, flags);
 
@@ -716,6 +902,11 @@ static int dwc3_resume(struct device *dev)
        pm_runtime_enable(dev);
 
        return 0;
+
+err_usb2phy_init:
+       phy_exit(dwc->usb2_generic_phy);
+
+       return ret;
 }
 
 static const struct dev_pm_ops dwc3_dev_pm_ops = {
index f8af8d44af850075b60a64e1eac29e54bef7fc5a..57332e3768e4d8eab0eebc64f12c3c6fde456eff 100644 (file)
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
 
+#include <linux/phy/phy.h>
+
 /* Global constants */
 #define DWC3_EP0_BOUNCE_SIZE   512
 #define DWC3_ENDPOINTS_NUM     32
 #define DWC3_XHCI_RESOURCES_NUM        2
 
+#define DWC3_SCRATCHBUF_SIZE   4096    /* each buffer is assumed to be 4KiB */
 #define DWC3_EVENT_SIZE                4       /* bytes */
 #define DWC3_EVENT_MAX_NUM     64      /* 2 events/endpoint */
 #define DWC3_EVENT_BUFFERS_SIZE        (DWC3_EVENT_SIZE * DWC3_EVENT_MAX_NUM)
 #define DWC3_GCTL_PRTCAP_OTG   3
 
 #define DWC3_GCTL_CORESOFTRESET                (1 << 11)
+#define DWC3_GCTL_SOFITPSYNC           (1 << 10)
 #define DWC3_GCTL_SCALEDOWN(n)         ((n) << 4)
 #define DWC3_GCTL_SCALEDOWN_MASK       DWC3_GCTL_SCALEDOWN(3)
 #define DWC3_GCTL_DISSCRAMBLE          (1 << 3)
 /* Device Endpoint Command Register */
 #define DWC3_DEPCMD_PARAM_SHIFT                16
 #define DWC3_DEPCMD_PARAM(x)           ((x) << DWC3_DEPCMD_PARAM_SHIFT)
-#define DWC3_DEPCMD_GET_RSC_IDX(x)     (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
+#define DWC3_DEPCMD_GET_RSC_IDX(x)     (((x) >> DWC3_DEPCMD_PARAM_SHIFT) & 0x7f)
 #define DWC3_DEPCMD_STATUS(x)          (((x) >> 15) & 1)
 #define DWC3_DEPCMD_HIPRI_FORCERM      (1 << 11)
 #define DWC3_DEPCMD_CMDACT             (1 << 10)
@@ -393,6 +397,7 @@ struct dwc3_event_buffer {
  * @busy_slot: first slot which is owned by HW
  * @desc: usb_endpoint_descriptor pointer
  * @dwc: pointer to DWC controller
+ * @saved_state: ep state saved during hibernation
  * @flags: endpoint flags (wedged, stalled, ...)
  * @current_trb: index of current used trb
  * @number: endpoint number (1 - 15)
@@ -415,6 +420,7 @@ struct dwc3_ep {
        const struct usb_ss_ep_comp_descriptor *comp_desc;
        struct dwc3             *dwc;
 
+       u32                     saved_state;
        unsigned                flags;
 #define DWC3_EP_ENABLED                (1 << 0)
 #define DWC3_EP_STALL          (1 << 1)
@@ -598,6 +604,7 @@ struct dwc3_scratchpad_array {
  * @ep0_trb: dma address of ep0_trb
  * @ep0_usb_req: dummy req used while handling STD USB requests
  * @ep0_bounce_addr: dma address of ep0_bounce
+ * @scratch_addr: dma address of scratchbuf
  * @lock: for synchronizing
  * @dev: pointer to our struct device
  * @xhci: pointer to our xHCI child
@@ -606,6 +613,7 @@ struct dwc3_scratchpad_array {
  * @gadget_driver: pointer to the gadget driver
  * @regs: base address for our registers
  * @regs_size: address space size
+ * @nr_scratch: number of scratch buffers
  * @num_event_buffers: calculated number of event buffers
  * @u1u2: only used on revisions <1.83a for workaround
  * @maximum_speed: maximum speed requested (mainly for testing purposes)
@@ -613,16 +621,10 @@ struct dwc3_scratchpad_array {
  * @dr_mode: requested mode of operation
  * @usb2_phy: pointer to USB2 PHY
  * @usb3_phy: pointer to USB3 PHY
+ * @usb2_generic_phy: pointer to USB2 PHY
+ * @usb3_generic_phy: pointer to USB3 PHY
  * @dcfg: saved contents of DCFG register
  * @gctl: saved contents of GCTL register
- * @is_selfpowered: true when we are selfpowered
- * @three_stage_setup: set if we perform a three phase setup
- * @ep0_bounced: true when we used bounce buffer
- * @ep0_expect_in: true when we expect a DATA IN transfer
- * @start_config_issued: true when StartConfig command has been issued
- * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
- * @needs_fifo_resize: not all users might want fifo resizing, flag it
- * @resize_fifos: tells us it's ok to reconfigure our TxFIFO sizes.
  * @isoch_delay: wValue from Set Isochronous Delay request;
  * @u2sel: parameter from Set SEL request.
  * @u2pel: parameter from Set SEL request.
@@ -637,15 +639,31 @@ struct dwc3_scratchpad_array {
  * @mem: points to start of memory which is used for this struct.
  * @hwparams: copy of hwparams registers
  * @root: debugfs root folder pointer
+ * @regset: debugfs pointer to regdump file
+ * @test_mode: true when we're entering a USB test mode
+ * @test_mode_nr: test feature selector
+ * @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
+ * @is_selfpowered: true when we are selfpowered
+ * @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
  */
 struct dwc3 {
        struct usb_ctrlrequest  *ctrl_req;
        struct dwc3_trb         *ep0_trb;
        void                    *ep0_bounce;
+       void                    *scratchbuf;
        u8                      *setup_buf;
        dma_addr_t              ctrl_req_addr;
        dma_addr_t              ep0_trb_addr;
        dma_addr_t              ep0_bounce_addr;
+       dma_addr_t              scratch_addr;
        struct dwc3_request     ep0_usb_req;
 
        /* device lock */
@@ -665,6 +683,9 @@ struct dwc3 {
        struct usb_phy          *usb2_phy;
        struct usb_phy          *usb3_phy;
 
+       struct phy              *usb2_generic_phy;
+       struct phy              *usb3_generic_phy;
+
        void __iomem            *regs;
        size_t                  regs_size;
 
@@ -674,6 +695,7 @@ struct dwc3 {
        u32                     dcfg;
        u32                     gctl;
 
+       u32                     nr_scratch;
        u32                     num_event_buffers;
        u32                     u1u2;
        u32                     maximum_speed;
@@ -695,17 +717,9 @@ struct dwc3 {
 #define DWC3_REVISION_230A     0x5533230a
 #define DWC3_REVISION_240A     0x5533240a
 #define DWC3_REVISION_250A     0x5533250a
-
-       unsigned                is_selfpowered:1;
-       unsigned                three_stage_setup:1;
-       unsigned                ep0_bounced:1;
-       unsigned                ep0_expect_in:1;
-       unsigned                start_config_issued:1;
-       unsigned                setup_packet_pending:1;
-       unsigned                delayed_status:1;
-       unsigned                needs_fifo_resize:1;
-       unsigned                resize_fifos:1;
-       unsigned                pullups_connected:1;
+#define DWC3_REVISION_260A     0x5533260a
+#define DWC3_REVISION_270A     0x5533270a
+#define DWC3_REVISION_280A     0x5533280a
 
        enum dwc3_ep0_next      ep0_next_event;
        enum dwc3_ep0_state     ep0state;
@@ -730,6 +744,18 @@ struct dwc3 {
 
        u8                      test_mode;
        u8                      test_mode_nr;
+
+       unsigned                delayed_status:1;
+       unsigned                ep0_bounced:1;
+       unsigned                ep0_expect_in:1;
+       unsigned                has_hibernation:1;
+       unsigned                is_selfpowered: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;
 };
 
 /* -------------------------------------------------------------------------- */
@@ -815,15 +841,15 @@ struct dwc3_event_depevt {
  *     12      - VndrDevTstRcved
  * @reserved15_12: Reserved, not used
  * @event_info: Information about this event
- * @reserved31_24: Reserved, not used
+ * @reserved31_25: Reserved, not used
  */
 struct dwc3_event_devt {
        u32     one_bit:1;
        u32     device_event:7;
        u32     type:4;
        u32     reserved15_12:4;
-       u32     event_info:8;
-       u32     reserved31_24:8;
+       u32     event_info:9;
+       u32     reserved31_25:7;
 } __packed;
 
 /**
@@ -856,6 +882,19 @@ union dwc3_event {
        struct dwc3_event_gevt          gevt;
 };
 
+/**
+ * struct dwc3_gadget_ep_cmd_params - representation of endpoint command
+ * parameters
+ * @param2: third parameter
+ * @param1: second parameter
+ * @param0: first parameter
+ */
+struct dwc3_gadget_ep_cmd_params {
+       u32     param2;
+       u32     param1;
+       u32     param0;
+};
+
 /*
  * DWC3 Features to be used as Driver Data
  */
@@ -881,11 +920,31 @@ static inline void dwc3_host_exit(struct dwc3 *dwc)
 #if IS_ENABLED(CONFIG_USB_DWC3_GADGET) || IS_ENABLED(CONFIG_USB_DWC3_DUAL_ROLE)
 int dwc3_gadget_init(struct dwc3 *dwc);
 void dwc3_gadget_exit(struct dwc3 *dwc);
+int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
+int dwc3_gadget_get_link_state(struct dwc3 *dwc);
+int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
+int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
+               unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
+int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
 #else
 static inline int dwc3_gadget_init(struct dwc3 *dwc)
 { return 0; }
 static inline void dwc3_gadget_exit(struct dwc3 *dwc)
 { }
+static inline int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
+{ return 0; }
+static inline int dwc3_gadget_get_link_state(struct dwc3 *dwc)
+{ return 0; }
+static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc,
+               enum dwc3_link_state state)
+{ return 0; }
+
+static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
+               unsigned cmd, struct dwc3_gadget_ep_cmd_params *params)
+{ return 0; }
+static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc,
+               int cmd, u32 param)
+{ return 0; }
 #endif
 
 /* power management interface */
index b1d7ee6e40b7a6e3ac69f60a89dff571012bfe1c..1160ff41bed4972fb2c11ea576ddecfc9a9f2548 100644 (file)
@@ -423,11 +423,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
        }
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "missing memory base resource\n");
-               return -EINVAL;
-       }
-
        base = devm_ioremap_resource(dev, res);
        if (IS_ERR(base))
                return PTR_ERR(base);
index 2da0a5a2803aab36b4842fe8804c4700ddd30683..a740eac74d56d502675107f03ffbd324e85f1f63 100644 (file)
@@ -67,6 +67,22 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode)
        return 0;
 }
 
+/**
+ * dwc3_gadget_get_link_state - Gets current state of USB Link
+ * @dwc: pointer to our context structure
+ *
+ * Caller should take care of locking. This function will
+ * return the link state on success (>= 0) or -ETIMEDOUT.
+ */
+int dwc3_gadget_get_link_state(struct dwc3 *dwc)
+{
+       u32             reg;
+
+       reg = dwc3_readl(dwc->regs, DWC3_DSTS);
+
+       return DWC3_DSTS_USBLNKST(reg);
+}
+
 /**
  * dwc3_gadget_set_link_state - Sets USB Link to a particular State
  * @dwc: pointer to our context structure
@@ -417,7 +433,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep)
 static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
                const struct usb_endpoint_descriptor *desc,
                const struct usb_ss_ep_comp_descriptor *comp_desc,
-               bool ignore)
+               bool ignore, bool restore)
 {
        struct dwc3_gadget_ep_cmd_params params;
 
@@ -436,6 +452,11 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep,
        if (ignore)
                params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM;
 
+       if (restore) {
+               params.param0 |= DWC3_DEPCFG_ACTION_RESTORE;
+               params.param2 |= dep->saved_state;
+       }
+
        params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN
                | DWC3_DEPCFG_XFER_NOT_READY_EN;
 
@@ -494,7 +515,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep)
 static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
                const struct usb_endpoint_descriptor *desc,
                const struct usb_ss_ep_comp_descriptor *comp_desc,
-               bool ignore)
+               bool ignore, bool restore)
 {
        struct dwc3             *dwc = dep->dwc;
        u32                     reg;
@@ -508,7 +529,8 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
                        return ret;
        }
 
-       ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore);
+       ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore,
+                       restore);
        if (ret)
                return ret;
 
@@ -548,13 +570,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
        return 0;
 }
 
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum);
+static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force);
 static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
        struct dwc3_request             *req;
 
        if (!list_empty(&dep->req_queued)) {
-               dwc3_stop_active_transfer(dwc, dep->number);
+               dwc3_stop_active_transfer(dwc, dep->number, true);
 
                /* - giveback all requests to gadget driver */
                while (!list_empty(&dep->req_queued)) {
@@ -659,7 +681,7 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
        }
 
        spin_lock_irqsave(&dwc->lock, flags);
-       ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false);
+       ret = __dwc3_gadget_ep_enable(dep, desc, ep->comp_desc, false, false);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        return ret;
@@ -771,9 +793,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
                        trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS_FIRST;
                else
                        trb->ctrl = DWC3_TRBCTL_ISOCHRONOUS;
-
-               if (!req->request.no_interrupt && !chain)
-                       trb->ctrl |= DWC3_TRB_CTRL_IOC;
                break;
 
        case USB_ENDPOINT_XFER_BULK:
@@ -788,6 +807,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
                BUG();
        }
 
+       if (!req->request.no_interrupt && !chain)
+               trb->ctrl |= DWC3_TRB_CTRL_IOC;
+
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                trb->ctrl |= DWC3_TRB_CTRL_ISP_IMI;
                trb->ctrl |= DWC3_TRB_CTRL_CSP;
@@ -1077,7 +1099,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
                 */
                if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
                        if (list_empty(&dep->req_queued)) {
-                               dwc3_stop_active_transfer(dwc, dep->number);
+                               dwc3_stop_active_transfer(dwc, dep->number, true);
                                dep->flags = DWC3_EP_ENABLED;
                        }
                        return 0;
@@ -1107,6 +1129,23 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
                return ret;
        }
 
+       /*
+        * 4. Stream Capable Bulk Endpoints. We need to start the transfer
+        * right away, otherwise host will not know we have streams to be
+        * handled.
+        */
+       if (dep->stream_capable) {
+               int     ret;
+
+               ret = __dwc3_gadget_kick_transfer(dep, 0, true);
+               if (ret && ret != -EBUSY) {
+                       struct dwc3     *dwc = dep->dwc;
+
+                       dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
+                                       dep->name);
+               }
+       }
+
        return 0;
 }
 
@@ -1163,7 +1202,7 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
                }
                if (r == req) {
                        /* wait until it is processed */
-                       dwc3_stop_active_transfer(dwc, dep->number);
+                       dwc3_stop_active_transfer(dwc, dep->number, true);
                        goto out1;
                }
                dev_err(dwc->dev, "request %p was not queued to %s\n",
@@ -1194,8 +1233,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
                ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
                        DWC3_DEPCMD_SETSTALL, &params);
                if (ret)
-                       dev_err(dwc->dev, "failed to %s STALL on %s\n",
-                                       value ? "set" : "clear",
+                       dev_err(dwc->dev, "failed to set STALL on %s\n",
                                        dep->name);
                else
                        dep->flags |= DWC3_EP_STALL;
@@ -1203,8 +1241,7 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value)
                ret = dwc3_send_gadget_ep_cmd(dwc, dep->number,
                        DWC3_DEPCMD_CLEARSTALL, &params);
                if (ret)
-                       dev_err(dwc->dev, "failed to %s STALL on %s\n",
-                                       value ? "set" : "clear",
+                       dev_err(dwc->dev, "failed to clear STALL on %s\n",
                                        dep->name);
                else
                        dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
@@ -1387,7 +1424,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
        return 0;
 }
 
-static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
+static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 {
        u32                     reg;
        u32                     timeout = 500;
@@ -1402,9 +1439,17 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on)
                if (dwc->revision >= DWC3_REVISION_194A)
                        reg &= ~DWC3_DCTL_KEEP_CONNECT;
                reg |= DWC3_DCTL_RUN_STOP;
+
+               if (dwc->has_hibernation)
+                       reg |= DWC3_DCTL_KEEP_CONNECT;
+
                dwc->pullups_connected = true;
        } else {
                reg &= ~DWC3_DCTL_RUN_STOP;
+
+               if (dwc->has_hibernation && !suspend)
+                       reg &= ~DWC3_DCTL_KEEP_CONNECT;
+
                dwc->pullups_connected = false;
        }
 
@@ -1442,7 +1487,7 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
        is_on = !!is_on;
 
        spin_lock_irqsave(&dwc->lock, flags);
-       ret = dwc3_gadget_run_stop(dwc, is_on);
+       ret = dwc3_gadget_run_stop(dwc, is_on, false);
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        return ret;
@@ -1549,14 +1594,16 @@ static int dwc3_gadget_start(struct usb_gadget *g,
        dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 
        dep = dwc->eps[0];
-       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+                       false);
        if (ret) {
                dev_err(dwc->dev, "failed to enable %s\n", dep->name);
                goto err2;
        }
 
        dep = dwc->eps[1];
-       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+                       false);
        if (ret) {
                dev_err(dwc->dev, "failed to enable %s\n", dep->name);
                goto err3;
@@ -1849,15 +1896,12 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
                         */
                        dep->flags = DWC3_EP_PENDING_REQUEST;
                } else {
-                       dwc3_stop_active_transfer(dwc, dep->number);
+                       dwc3_stop_active_transfer(dwc, dep->number, true);
                        dep->flags = DWC3_EP_ENABLED;
                }
                return 1;
        }
 
-       if ((event->status & DEPEVT_STATUS_IOC) &&
-                       (trb->ctrl & DWC3_TRB_CTRL_IOC))
-               return 0;
        return 1;
 }
 
@@ -1999,7 +2043,25 @@ static void dwc3_disconnect_gadget(struct dwc3 *dwc)
        }
 }
 
-static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
+static void dwc3_suspend_gadget(struct dwc3 *dwc)
+{
+       if (dwc->gadget_driver && dwc->gadget_driver->suspend) {
+               spin_unlock(&dwc->lock);
+               dwc->gadget_driver->suspend(&dwc->gadget);
+               spin_lock(&dwc->lock);
+       }
+}
+
+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);
+               spin_lock(&dwc->lock);
+       }
+}
+
+static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
 {
        struct dwc3_ep *dep;
        struct dwc3_gadget_ep_cmd_params params;
@@ -2031,7 +2093,8 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum)
         */
 
        cmd = DWC3_DEPCMD_ENDTRANSFER;
-       cmd |= DWC3_DEPCMD_HIPRI_FORCERM | DWC3_DEPCMD_CMDIOC;
+       cmd |= force ? DWC3_DEPCMD_HIPRI_FORCERM : 0;
+       cmd |= DWC3_DEPCMD_CMDIOC;
        cmd |= DWC3_DEPCMD_PARAM(dep->resource_index);
        memset(&params, 0, sizeof(params));
        ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, &params);
@@ -2259,18 +2322,24 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
                 */
                reg |= DWC3_DCTL_HIRD_THRES(12);
 
+               dwc3_writel(dwc->regs, DWC3_DCTL, reg);
+       } else {
+               reg = dwc3_readl(dwc->regs, DWC3_DCTL);
+               reg &= ~DWC3_DCTL_HIRD_THRES_MASK;
                dwc3_writel(dwc->regs, DWC3_DCTL, reg);
        }
 
        dep = dwc->eps[0];
-       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
+       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
+                       false);
        if (ret) {
                dev_err(dwc->dev, "failed to enable %s\n", dep->name);
                return;
        }
 
        dep = dwc->eps[1];
-       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true);
+       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, true,
+                       false);
        if (ret) {
                dev_err(dwc->dev, "failed to enable %s\n", dep->name);
                return;
@@ -2378,9 +2447,50 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
 
        dwc->link_state = next;
 
+       switch (next) {
+       case DWC3_LINK_STATE_U1:
+               if (dwc->speed == USB_SPEED_SUPER)
+                       dwc3_suspend_gadget(dwc);
+               break;
+       case DWC3_LINK_STATE_U2:
+       case DWC3_LINK_STATE_U3:
+               dwc3_suspend_gadget(dwc);
+               break;
+       case DWC3_LINK_STATE_RESUME:
+               dwc3_resume_gadget(dwc);
+               break;
+       default:
+               /* do nothing */
+               break;
+       }
+
        dev_vdbg(dwc->dev, "%s link %d\n", __func__, dwc->link_state);
 }
 
+static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc,
+               unsigned int evtinfo)
+{
+       unsigned int is_ss = evtinfo & BIT(4);
+
+       /**
+        * WORKAROUND: DWC3 revison 2.20a with hibernation support
+        * have a known issue which can cause USB CV TD.9.23 to fail
+        * randomly.
+        *
+        * Because of this issue, core could generate bogus hibernation
+        * events which SW needs to ignore.
+        *
+        * Refers to:
+        *
+        * STAR#9000546576: Device Mode Hibernation: Issue in USB 2.0
+        * Device Fallback from SuperSpeed
+        */
+       if (is_ss ^ (dwc->speed == USB_SPEED_SUPER))
+               return;
+
+       /* enter hibernation here */
+}
+
 static void dwc3_gadget_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_devt *event)
 {
@@ -2397,6 +2507,13 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
        case DWC3_DEVICE_EVENT_WAKEUP:
                dwc3_gadget_wakeup_interrupt(dwc);
                break;
+       case DWC3_DEVICE_EVENT_HIBER_REQ:
+               if (dev_WARN_ONCE(dwc->dev, !dwc->has_hibernation,
+                                       "unexpected hibernation event\n"))
+                       break;
+
+               dwc3_gadget_hibernation_interrupt(dwc, event->event_info);
+               break;
        case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE:
                dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
                break;
@@ -2661,8 +2778,10 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
 
 int dwc3_gadget_prepare(struct dwc3 *dwc)
 {
-       if (dwc->pullups_connected)
+       if (dwc->pullups_connected) {
                dwc3_gadget_disable_irq(dwc);
+               dwc3_gadget_run_stop(dwc, true, true);
+       }
 
        return 0;
 }
@@ -2671,7 +2790,7 @@ void dwc3_gadget_complete(struct dwc3 *dwc)
 {
        if (dwc->pullups_connected) {
                dwc3_gadget_enable_irq(dwc);
-               dwc3_gadget_run_stop(dwc, true);
+               dwc3_gadget_run_stop(dwc, true, false);
        }
 }
 
@@ -2694,12 +2813,14 @@ int dwc3_gadget_resume(struct dwc3 *dwc)
        dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
 
        dep = dwc->eps[0];
-       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+                       false);
        if (ret)
                goto err0;
 
        dep = dwc->eps[1];
-       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false);
+       ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false,
+                       false);
        if (ret)
                goto err1;
 
index febe1aa7b71424790c96d92852eb8ea4d4d51453..a0ee75b68a803bd74d40a925930ff6767c29c392 100644 (file)
@@ -56,12 +56,6 @@ struct dwc3;
 /* DEPXFERCFG parameter 0 */
 #define DWC3_DEPXFERCFG_NUM_XFER_RES(n)        ((n) & 0xffff)
 
-struct dwc3_gadget_ep_cmd_params {
-       u32     param2;
-       u32     param1;
-       u32     param0;
-};
-
 /* -------------------------------------------------------------------------- */
 
 #define to_dwc3_request(r)     (container_of(r, struct dwc3_request, request))
@@ -85,9 +79,6 @@ static inline void dwc3_gadget_move_request_queued(struct dwc3_request *req)
 void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
                int status);
 
-int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode);
-int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state);
-
 void dwc3_ep0_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event);
 void dwc3_ep0_out_start(struct dwc3 *dwc);
@@ -95,9 +86,6 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
 int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
                gfp_t gfp_flags);
 int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value);
-int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
-               unsigned cmd, struct dwc3_gadget_ep_cmd_params *params);
-int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param);
 
 /**
  * dwc3_gadget_ep_get_transfer_index - Gets transfer index from HW
index 8154165aa60142303a073c316ffcc8dde92e9d0a..3557c7e5040d3387a3a7f8c819a72cd6b7af33dd 100644 (file)
@@ -226,7 +226,7 @@ config USB_GR_UDC
 config USB_OMAP
        tristate "OMAP USB Device Controller"
        depends on ARCH_OMAP1
-       select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 || MACH_OMAP_H4_OTG
+       select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3
        help
           Many Texas Instruments OMAP processors have flexible full
           speed USB device controllers, with support for up to 30
@@ -301,7 +301,6 @@ config USB_PXA27X
           gadget drivers to also be dynamically linked.
 
 config USB_S3C_HSOTG
-       depends on ARM
        tristate "Designware/S3C HS/OtG USB Device controller"
        help
          The Designware USB2.0 high-speed gadget controller
index cea8c20a142555af1a1c01c5c9a8b9ef1ce11b65..f605ad8c1902fc775cffc39dcd53be8d65fe3a8f 100644 (file)
@@ -1758,15 +1758,15 @@ static int at91udc_probe(struct platform_device *pdev)
 
        /* newer chips have more FIFO memory than rm9200 */
        if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) {
-               usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
-               usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
-               usb_ep_set_maxpacket_limit(&udc->ep[4].ep, 512);
-               usb_ep_set_maxpacket_limit(&udc->ep[5].ep, 512);
+               udc->ep[0].maxpacket = 64;
+               udc->ep[3].maxpacket = 64;
+               udc->ep[4].maxpacket = 512;
+               udc->ep[5].maxpacket = 512;
        } else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
-               usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
+               udc->ep[3].maxpacket = 64;
        } else if (cpu_is_at91sam9263()) {
-               usb_ep_set_maxpacket_limit(&udc->ep[0].ep, 64);
-               usb_ep_set_maxpacket_limit(&udc->ep[3].ep, 64);
+               udc->ep[0].maxpacket = 64;
+               udc->ep[3].maxpacket = 64;
        }
 
        udc->udp_baseaddr = ioremap(res->start, resource_size(res));
index 52771d4c44bc96c7974fd50e82f4256d02bc9dc2..9f65324f9ae0c94471f284bcb8a66e49e420f486 100644 (file)
@@ -1661,7 +1661,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
        if (dma_status) {
                int i;
 
-               for (i = 1; i < USBA_NR_ENDPOINTS; i++)
+               for (i = 1; i < USBA_NR_DMAS; i++)
                        if (dma_status & (1 << i))
                                usba_dma_irq(udc, &udc->usba_ep[i]);
        }
@@ -1670,7 +1670,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
        if (ep_status) {
                int i;
 
-               for (i = 0; i < USBA_NR_ENDPOINTS; i++)
+               for (i = 0; i < udc->num_ep; i++)
                        if (ep_status & (1 << i)) {
                                if (ep_is_control(&udc->usba_ep[i]))
                                        usba_control_irq(udc, &udc->usba_ep[i]);
@@ -1827,12 +1827,12 @@ static int atmel_usba_stop(struct usb_gadget *gadget,
        toggle_bias(0);
        usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 
-       udc->driver = NULL;
-
        clk_disable_unprepare(udc->hclk);
        clk_disable_unprepare(udc->pclk);
 
-       DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name);
+       DBG(DBG_GADGET, "unregistered driver `%s'\n", udc->driver->driver.name);
+
+       udc->driver = NULL;
 
        return 0;
 }
@@ -1914,6 +1914,12 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
                i++;
        }
 
+       if (i == 0) {
+               dev_err(&pdev->dev, "of_probe: no endpoint specified\n");
+               ret = -EINVAL;
+               goto err;
+       }
+
        return eps;
 err:
        return ERR_PTR(ret);
index 2922db50befe73c221b906ef3c7f3f28cafb36a4..a70706e8cb02e9eaeebd531bc7167bb48d9d9705 100644 (file)
 #define USBA_FIFO_BASE(x)      ((x) << 16)
 
 /* Synth parameters */
-#define USBA_NR_ENDPOINTS      7
+#define USBA_NR_DMAS           7
 
 #define EP0_FIFO_SIZE          64
 #define EP0_EPT_SIZE           USBA_EPT_SIZE_64
index d742bed7a5fa604ee7144f3b2c17cd9f6e06bc80..fab906429b80533fc18a965efa5b9eb504f46ea7 100644 (file)
@@ -1139,7 +1139,7 @@ struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
 
        uc = copy_gadget_strings(sp, n_gstrings, n_strings);
        if (IS_ERR(uc))
-               return ERR_PTR(PTR_ERR(uc));
+               return ERR_CAST(uc);
 
        n_gs = get_containers_gs(uc);
        ret = usb_string_ids_tab(cdev, n_gs[0]->strings);
index 2b43343940766251ee5f274b807b882273f09008..2e164dca08e89fc29ea1887f0afe7b2a09e1a5af 100644 (file)
 #include <linux/usb/composite.h>
 #include <linux/usb/functionfs.h>
 
+#include <linux/aio.h>
+#include <linux/mmu_context.h>
+#include <linux/poll.h>
+
 #include "u_fs.h"
 #include "configfs.h"
 
@@ -99,6 +103,14 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
 }
 
 
+static inline enum ffs_setup_state
+ffs_setup_state_clear_cancelled(struct ffs_data *ffs)
+{
+       return (enum ffs_setup_state)
+               cmpxchg(&ffs->setup_state, FFS_SETUP_CANCELLED, FFS_NO_SETUP);
+}
+
+
 static void ffs_func_eps_disable(struct ffs_function *func);
 static int __must_check ffs_func_eps_enable(struct ffs_function *func);
 
@@ -122,8 +134,8 @@ struct ffs_ep {
        struct usb_ep                   *ep;    /* P: ffs->eps_lock */
        struct usb_request              *req;   /* P: epfile->mutex */
 
-       /* [0]: full speed, [1]: high speed */
-       struct usb_endpoint_descriptor  *descs[2];
+       /* [0]: full speed, [1]: high speed, [2]: super speed */
+       struct usb_endpoint_descriptor  *descs[3];
 
        u8                              num;
 
@@ -148,6 +160,25 @@ struct ffs_epfile {
        unsigned char                   _pad;
 };
 
+/*  ffs_io_data structure ***************************************************/
+
+struct ffs_io_data {
+       bool aio;
+       bool read;
+
+       struct kiocb *kiocb;
+       const struct iovec *iovec;
+       unsigned long nr_segs;
+       char __user *buf;
+       size_t len;
+
+       struct mm_struct *mm;
+       struct work_struct work;
+
+       struct usb_ep *ep;
+       struct usb_request *req;
+};
+
 static int  __must_check ffs_epfiles_create(struct ffs_data *ffs);
 static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count);
 
@@ -161,8 +192,10 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
 DEFINE_MUTEX(ffs_lock);
 EXPORT_SYMBOL(ffs_lock);
 
-static struct ffs_dev *ffs_find_dev(const char *name);
+static struct ffs_dev *_ffs_find_dev(const char *name);
+static struct ffs_dev *_ffs_alloc_dev(void);
 static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
+static void _ffs_free_dev(struct ffs_dev *dev);
 static void *ffs_acquire_dev(const char *dev_name);
 static void ffs_release_dev(struct ffs_data *ffs_data);
 static int ffs_ready(struct ffs_data *ffs);
@@ -218,7 +251,7 @@ static int __ffs_ep0_queue_wait(struct ffs_data *ffs, char *data, size_t len)
        }
 
        ffs->setup_state = FFS_NO_SETUP;
-       return ffs->ep0req_status;
+       return req->status ? req->status : req->actual;
 }
 
 static int __ffs_ep0_stall(struct ffs_data *ffs)
@@ -244,7 +277,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
        ENTER();
 
        /* Fast check if setup was canceled */
-       if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+       if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
                return -EIDRM;
 
        /* Acquire mutex */
@@ -310,8 +343,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
                 * rather then _irqsave
                 */
                spin_lock_irq(&ffs->ev.waitq.lock);
-               switch (FFS_SETUP_STATE(ffs)) {
-               case FFS_SETUP_CANCELED:
+               switch (ffs_setup_state_clear_cancelled(ffs)) {
+               case FFS_SETUP_CANCELLED:
                        ret = -EIDRM;
                        goto done_spin;
 
@@ -346,7 +379,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
                /*
                 * We are guaranteed to be still in FFS_ACTIVE state
                 * but the state of setup could have changed from
-                * FFS_SETUP_PENDING to FFS_SETUP_CANCELED so we need
+                * FFS_SETUP_PENDING to FFS_SETUP_CANCELLED so we need
                 * to check for that.  If that happened we copied data
                 * from user space in vain but it's unlikely.
                 *
@@ -355,7 +388,8 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
                 * transition can be performed and it's protected by
                 * mutex.
                 */
-               if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+               if (ffs_setup_state_clear_cancelled(ffs) ==
+                   FFS_SETUP_CANCELLED) {
                        ret = -EIDRM;
 done_spin:
                        spin_unlock_irq(&ffs->ev.waitq.lock);
@@ -421,7 +455,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
        ENTER();
 
        /* Fast check if setup was canceled */
-       if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED)
+       if (ffs_setup_state_clear_cancelled(ffs) == FFS_SETUP_CANCELLED)
                return -EIDRM;
 
        /* Acquire mutex */
@@ -441,8 +475,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
         */
        spin_lock_irq(&ffs->ev.waitq.lock);
 
-       switch (FFS_SETUP_STATE(ffs)) {
-       case FFS_SETUP_CANCELED:
+       switch (ffs_setup_state_clear_cancelled(ffs)) {
+       case FFS_SETUP_CANCELLED:
                ret = -EIDRM;
                break;
 
@@ -489,7 +523,8 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
                spin_lock_irq(&ffs->ev.waitq.lock);
 
                /* See ffs_ep0_write() */
-               if (FFS_SETUP_STATE(ffs) == FFS_SETUP_CANCELED) {
+               if (ffs_setup_state_clear_cancelled(ffs) ==
+                   FFS_SETUP_CANCELLED) {
                        ret = -EIDRM;
                        break;
                }
@@ -558,6 +593,45 @@ static long ffs_ep0_ioctl(struct file *file, unsigned code, unsigned long value)
        return ret;
 }
 
+static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
+{
+       struct ffs_data *ffs = file->private_data;
+       unsigned int mask = POLLWRNORM;
+       int ret;
+
+       poll_wait(file, &ffs->ev.waitq, wait);
+
+       ret = ffs_mutex_lock(&ffs->mutex, file->f_flags & O_NONBLOCK);
+       if (unlikely(ret < 0))
+               return mask;
+
+       switch (ffs->state) {
+       case FFS_READ_DESCRIPTORS:
+       case FFS_READ_STRINGS:
+               mask |= POLLOUT;
+               break;
+
+       case FFS_ACTIVE:
+               switch (ffs->setup_state) {
+               case FFS_NO_SETUP:
+                       if (ffs->ev.count)
+                               mask |= POLLIN;
+                       break;
+
+               case FFS_SETUP_PENDING:
+               case FFS_SETUP_CANCELLED:
+                       mask |= (POLLIN | POLLOUT);
+                       break;
+               }
+       case FFS_CLOSING:
+               break;
+       }
+
+       mutex_unlock(&ffs->mutex);
+
+       return mask;
+}
+
 static const struct file_operations ffs_ep0_operations = {
        .llseek =       no_llseek,
 
@@ -566,6 +640,7 @@ static const struct file_operations ffs_ep0_operations = {
        .read =         ffs_ep0_read,
        .release =      ffs_ep0_release,
        .unlocked_ioctl =       ffs_ep0_ioctl,
+       .poll =         ffs_ep0_poll,
 };
 
 
@@ -581,8 +656,52 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req)
        }
 }
 
-static ssize_t ffs_epfile_io(struct file *file,
-                            char __user *buf, size_t len, int read)
+static void ffs_user_copy_worker(struct work_struct *work)
+{
+       struct ffs_io_data *io_data = container_of(work, struct ffs_io_data,
+                                                  work);
+       int ret = io_data->req->status ? io_data->req->status :
+                                        io_data->req->actual;
+
+       if (io_data->read && ret > 0) {
+               int i;
+               size_t pos = 0;
+               use_mm(io_data->mm);
+               for (i = 0; i < io_data->nr_segs; i++) {
+                       if (unlikely(copy_to_user(io_data->iovec[i].iov_base,
+                                                &io_data->buf[pos],
+                                                io_data->iovec[i].iov_len))) {
+                               ret = -EFAULT;
+                               break;
+                       }
+                       pos += io_data->iovec[i].iov_len;
+               }
+               unuse_mm(io_data->mm);
+       }
+
+       aio_complete(io_data->kiocb, ret, ret);
+
+       usb_ep_free_request(io_data->ep, io_data->req);
+
+       io_data->kiocb->private = NULL;
+       if (io_data->read)
+               kfree(io_data->iovec);
+       kfree(io_data->buf);
+       kfree(io_data);
+}
+
+static void ffs_epfile_async_io_complete(struct usb_ep *_ep,
+                                        struct usb_request *req)
+{
+       struct ffs_io_data *io_data = req->context;
+
+       ENTER();
+
+       INIT_WORK(&io_data->work, ffs_user_copy_worker);
+       schedule_work(&io_data->work);
+}
+
+static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
 {
        struct ffs_epfile *epfile = file->private_data;
        struct ffs_ep *ep;
@@ -612,7 +731,7 @@ static ssize_t ffs_epfile_io(struct file *file,
        }
 
        /* Do we halt? */
-       halt = !read == !epfile->in;
+       halt = (!io_data->read == !epfile->in);
        if (halt && epfile->isoc) {
                ret = -EINVAL;
                goto error;
@@ -630,15 +749,32 @@ static ssize_t ffs_epfile_io(struct file *file,
                 * Controller may require buffer size to be aligned to
                 * maxpacketsize of an out endpoint.
                 */
-               data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len;
+               data_len = io_data->read ?
+                          usb_ep_align_maybe(gadget, ep->ep, io_data->len) :
+                          io_data->len;
 
                data = kmalloc(data_len, GFP_KERNEL);
                if (unlikely(!data))
                        return -ENOMEM;
-
-               if (!read && unlikely(copy_from_user(data, buf, len))) {
-                       ret = -EFAULT;
-                       goto error;
+               if (io_data->aio && !io_data->read) {
+                       int i;
+                       size_t pos = 0;
+                       for (i = 0; i < io_data->nr_segs; i++) {
+                               if (unlikely(copy_from_user(&data[pos],
+                                            io_data->iovec[i].iov_base,
+                                            io_data->iovec[i].iov_len))) {
+                                       ret = -EFAULT;
+                                       goto error;
+                               }
+                               pos += io_data->iovec[i].iov_len;
+                       }
+               } else {
+                       if (!io_data->read &&
+                           unlikely(__copy_from_user(data, io_data->buf,
+                                                     io_data->len))) {
+                               ret = -EFAULT;
+                               goto error;
+                       }
                }
        }
 
@@ -661,40 +797,78 @@ static ssize_t ffs_epfile_io(struct file *file,
                ret = -EBADMSG;
        } else {
                /* Fire the request */
-               DECLARE_COMPLETION_ONSTACK(done);
+               struct usb_request *req;
 
-               struct usb_request *req = ep->req;
-               req->context  = &done;
-               req->complete = ffs_epfile_io_complete;
-               req->buf      = data;
-               req->length   = data_len;
+               if (io_data->aio) {
+                       req = usb_ep_alloc_request(ep->ep, GFP_KERNEL);
+                       if (unlikely(!req))
+                               goto error_lock;
 
-               ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+                       req->buf      = data;
+                       req->length   = io_data->len;
 
-               spin_unlock_irq(&epfile->ffs->eps_lock);
+                       io_data->buf = data;
+                       io_data->ep = ep->ep;
+                       io_data->req = req;
 
-               if (unlikely(ret < 0)) {
-                       /* nop */
-               } else if (unlikely(wait_for_completion_interruptible(&done))) {
-                       ret = -EINTR;
-                       usb_ep_dequeue(ep->ep, req);
+                       req->context  = io_data;
+                       req->complete = ffs_epfile_async_io_complete;
+
+                       ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+                       if (unlikely(ret)) {
+                               usb_ep_free_request(ep->ep, req);
+                               goto error_lock;
+                       }
+                       ret = -EIOCBQUEUED;
+
+                       spin_unlock_irq(&epfile->ffs->eps_lock);
                } else {
-                       /*
-                        * XXX We may end up silently droping data here.
-                        * Since data_len (i.e. req->length) may be bigger
-                        * than len (after being rounded up to maxpacketsize),
-                        * we may end up with more data then user space has
-                        * space for.
-                        */
-                       ret = ep->status;
-                       if (read && ret > 0 &&
-                           unlikely(copy_to_user(buf, data,
-                                                 min_t(size_t, ret, len))))
-                               ret = -EFAULT;
+                       DECLARE_COMPLETION_ONSTACK(done);
+
+                       req = ep->req;
+                       req->buf      = data;
+                       req->length   = io_data->len;
+
+                       req->context  = &done;
+                       req->complete = ffs_epfile_io_complete;
+
+                       ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
+
+                       spin_unlock_irq(&epfile->ffs->eps_lock);
+
+                       if (unlikely(ret < 0)) {
+                               /* nop */
+                       } else if (unlikely(
+                                  wait_for_completion_interruptible(&done))) {
+                               ret = -EINTR;
+                               usb_ep_dequeue(ep->ep, req);
+                       } else {
+                               /*
+                                * XXX We may end up silently droping data
+                                * here.  Since data_len (i.e. req->length) may
+                                * be bigger than len (after being rounded up
+                                * to maxpacketsize), we may end up with more
+                                * data then user space has space for.
+                                */
+                               ret = ep->status;
+                               if (io_data->read && ret > 0) {
+                                       ret = min_t(size_t, ret, io_data->len);
+
+                                       if (unlikely(copy_to_user(io_data->buf,
+                                               data, ret)))
+                                               ret = -EFAULT;
+                               }
+                       }
+                       kfree(data);
                }
        }
 
        mutex_unlock(&epfile->mutex);
+       return ret;
+
+error_lock:
+       spin_unlock_irq(&epfile->ffs->eps_lock);
+       mutex_unlock(&epfile->mutex);
 error:
        kfree(data);
        return ret;
@@ -704,17 +878,31 @@ static ssize_t
 ffs_epfile_write(struct file *file, const char __user *buf, size_t len,
                 loff_t *ptr)
 {
+       struct ffs_io_data io_data;
+
        ENTER();
 
-       return ffs_epfile_io(file, (char __user *)buf, len, 0);
+       io_data.aio = false;
+       io_data.read = false;
+       io_data.buf = (char * __user)buf;
+       io_data.len = len;
+
+       return ffs_epfile_io(file, &io_data);
 }
 
 static ssize_t
 ffs_epfile_read(struct file *file, char __user *buf, size_t len, loff_t *ptr)
 {
+       struct ffs_io_data io_data;
+
        ENTER();
 
-       return ffs_epfile_io(file, buf, len, 1);
+       io_data.aio = false;
+       io_data.read = true;
+       io_data.buf = buf;
+       io_data.len = len;
+
+       return ffs_epfile_io(file, &io_data);
 }
 
 static int
@@ -733,6 +921,89 @@ ffs_epfile_open(struct inode *inode, struct file *file)
        return 0;
 }
 
+static int ffs_aio_cancel(struct kiocb *kiocb)
+{
+       struct ffs_io_data *io_data = kiocb->private;
+       struct ffs_epfile *epfile = kiocb->ki_filp->private_data;
+       int value;
+
+       ENTER();
+
+       spin_lock_irq(&epfile->ffs->eps_lock);
+
+       if (likely(io_data && io_data->ep && io_data->req))
+               value = usb_ep_dequeue(io_data->ep, io_data->req);
+       else
+               value = -EINVAL;
+
+       spin_unlock_irq(&epfile->ffs->eps_lock);
+
+       return value;
+}
+
+static ssize_t ffs_epfile_aio_write(struct kiocb *kiocb,
+                                   const struct iovec *iovec,
+                                   unsigned long nr_segs, loff_t loff)
+{
+       struct ffs_io_data *io_data;
+
+       ENTER();
+
+       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+       if (unlikely(!io_data))
+               return -ENOMEM;
+
+       io_data->aio = true;
+       io_data->read = false;
+       io_data->kiocb = kiocb;
+       io_data->iovec = iovec;
+       io_data->nr_segs = nr_segs;
+       io_data->len = kiocb->ki_nbytes;
+       io_data->mm = current->mm;
+
+       kiocb->private = io_data;
+
+       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+       return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
+static ssize_t ffs_epfile_aio_read(struct kiocb *kiocb,
+                                  const struct iovec *iovec,
+                                  unsigned long nr_segs, loff_t loff)
+{
+       struct ffs_io_data *io_data;
+       struct iovec *iovec_copy;
+
+       ENTER();
+
+       iovec_copy = kmalloc_array(nr_segs, sizeof(*iovec_copy), GFP_KERNEL);
+       if (unlikely(!iovec_copy))
+               return -ENOMEM;
+
+       memcpy(iovec_copy, iovec, sizeof(struct iovec)*nr_segs);
+
+       io_data = kmalloc(sizeof(*io_data), GFP_KERNEL);
+       if (unlikely(!io_data)) {
+               kfree(iovec_copy);
+               return -ENOMEM;
+       }
+
+       io_data->aio = true;
+       io_data->read = true;
+       io_data->kiocb = kiocb;
+       io_data->iovec = iovec_copy;
+       io_data->nr_segs = nr_segs;
+       io_data->len = kiocb->ki_nbytes;
+       io_data->mm = current->mm;
+
+       kiocb->private = io_data;
+
+       kiocb_set_cancel_fn(kiocb, ffs_aio_cancel);
+
+       return ffs_epfile_io(kiocb->ki_filp, io_data);
+}
+
 static int
 ffs_epfile_release(struct inode *inode, struct file *file)
 {
@@ -789,6 +1060,8 @@ static const struct file_operations ffs_epfile_operations = {
        .open =         ffs_epfile_open,
        .write =        ffs_epfile_write,
        .read =         ffs_epfile_read,
+       .aio_write =    ffs_epfile_aio_write,
+       .aio_read =     ffs_epfile_aio_read,
        .release =      ffs_epfile_release,
        .unlocked_ioctl =       ffs_epfile_ioctl,
 };
@@ -1172,7 +1445,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
        if (ffs->epfiles)
                ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
 
-       kfree(ffs->raw_descs);
+       kfree(ffs->raw_descs_data);
        kfree(ffs->raw_strings);
        kfree(ffs->stringtabs);
 }
@@ -1184,14 +1457,15 @@ static void ffs_data_reset(struct ffs_data *ffs)
        ffs_data_clear(ffs);
 
        ffs->epfiles = NULL;
+       ffs->raw_descs_data = NULL;
        ffs->raw_descs = NULL;
        ffs->raw_strings = NULL;
        ffs->stringtabs = NULL;
 
        ffs->raw_descs_length = 0;
-       ffs->raw_fs_descs_length = 0;
        ffs->fs_descs_count = 0;
        ffs->hs_descs_count = 0;
+       ffs->ss_descs_count = 0;
 
        ffs->strings_count = 0;
        ffs->interfaces_count = 0;
@@ -1334,7 +1608,24 @@ static int ffs_func_eps_enable(struct ffs_function *func)
        spin_lock_irqsave(&func->ffs->eps_lock, flags);
        do {
                struct usb_endpoint_descriptor *ds;
-               ds = ep->descs[ep->descs[1] ? 1 : 0];
+               int desc_idx;
+
+               if (ffs->gadget->speed == USB_SPEED_SUPER)
+                       desc_idx = 2;
+               else if (ffs->gadget->speed == USB_SPEED_HIGH)
+                       desc_idx = 1;
+               else
+                       desc_idx = 0;
+
+               /* fall-back to lower speed if desc missing for current speed */
+               do {
+                       ds = ep->descs[desc_idx];
+               } while (!ds && --desc_idx >= 0);
+
+               if (!ds) {
+                       ret = -EINVAL;
+                       break;
+               }
 
                ep->ep->driver_data = ep;
                ep->ep->desc = ds;
@@ -1469,6 +1760,12 @@ static int __must_check ffs_do_desc(char *data, unsigned len,
        }
                break;
 
+       case USB_DT_SS_ENDPOINT_COMP:
+               pr_vdebug("EP SS companion descriptor\n");
+               if (length != sizeof(struct usb_ss_ep_comp_descriptor))
+                       goto inv_length;
+               break;
+
        case USB_DT_OTHER_SPEED_CONFIG:
        case USB_DT_INTERFACE_POWER:
        case USB_DT_DEBUG:
@@ -1579,60 +1876,76 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
 static int __ffs_data_got_descs(struct ffs_data *ffs,
                                char *const _data, size_t len)
 {
-       unsigned fs_count, hs_count;
-       int fs_len, ret = -EINVAL;
-       char *data = _data;
+       char *data = _data, *raw_descs;
+       unsigned counts[3], flags;
+       int ret = -EINVAL, i;
 
        ENTER();
 
-       if (unlikely(get_unaligned_le32(data) != FUNCTIONFS_DESCRIPTORS_MAGIC ||
-                    get_unaligned_le32(data + 4) != len))
+       if (get_unaligned_le32(data + 4) != len)
                goto error;
-       fs_count = get_unaligned_le32(data +  8);
-       hs_count = get_unaligned_le32(data + 12);
 
-       if (!fs_count && !hs_count)
-               goto einval;
-
-       data += 16;
-       len  -= 16;
-
-       if (likely(fs_count)) {
-               fs_len = ffs_do_descs(fs_count, data, len,
-                                     __ffs_data_do_entity, ffs);
-               if (unlikely(fs_len < 0)) {
-                       ret = fs_len;
+       switch (get_unaligned_le32(data)) {
+       case FUNCTIONFS_DESCRIPTORS_MAGIC:
+               flags = FUNCTIONFS_HAS_FS_DESC | FUNCTIONFS_HAS_HS_DESC;
+               data += 8;
+               len  -= 8;
+               break;
+       case FUNCTIONFS_DESCRIPTORS_MAGIC_V2:
+               flags = get_unaligned_le32(data + 8);
+               if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
+                             FUNCTIONFS_HAS_HS_DESC |
+                             FUNCTIONFS_HAS_SS_DESC)) {
+                       ret = -ENOSYS;
                        goto error;
                }
+               data += 12;
+               len  -= 12;
+               break;
+       default:
+               goto error;
+       }
 
-               data += fs_len;
-               len  -= fs_len;
-       } else {
-               fs_len = 0;
+       /* Read fs_count, hs_count and ss_count (if present) */
+       for (i = 0; i < 3; ++i) {
+               if (!(flags & (1 << i))) {
+                       counts[i] = 0;
+               } else if (len < 4) {
+                       goto error;
+               } else {
+                       counts[i] = get_unaligned_le32(data);
+                       data += 4;
+                       len  -= 4;
+               }
        }
 
-       if (likely(hs_count)) {
-               ret = ffs_do_descs(hs_count, data, len,
+       /* Read descriptors */
+       raw_descs = data;
+       for (i = 0; i < 3; ++i) {
+               if (!counts[i])
+                       continue;
+               ret = ffs_do_descs(counts[i], data, len,
                                   __ffs_data_do_entity, ffs);
-               if (unlikely(ret < 0))
+               if (ret < 0)
                        goto error;
-       } else {
-               ret = 0;
+               data += ret;
+               len  -= ret;
        }
 
-       if (unlikely(len != ret))
-               goto einval;
+       if (raw_descs == data || len) {
+               ret = -EINVAL;
+               goto error;
+       }
 
-       ffs->raw_fs_descs_length = fs_len;
-       ffs->raw_descs_length    = fs_len + ret;
-       ffs->raw_descs           = _data;
-       ffs->fs_descs_count      = fs_count;
-       ffs->hs_descs_count      = hs_count;
+       ffs->raw_descs_data     = _data;
+       ffs->raw_descs          = raw_descs;
+       ffs->raw_descs_length   = data - raw_descs;
+       ffs->fs_descs_count     = counts[0];
+       ffs->hs_descs_count     = counts[1];
+       ffs->ss_descs_count     = counts[2];
 
        return 0;
 
-einval:
-       ret = -EINVAL;
 error:
        kfree(_data);
        return ret;
@@ -1789,7 +2102,7 @@ static void __ffs_event_add(struct ffs_data *ffs,
         * the source does nothing.
         */
        if (ffs->setup_state == FFS_SETUP_PENDING)
-               ffs->setup_state = FFS_SETUP_CANCELED;
+               ffs->setup_state = FFS_SETUP_CANCELLED;
 
        switch (type) {
        case FUNCTIONFS_RESUME:
@@ -1850,21 +2163,28 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
        struct usb_endpoint_descriptor *ds = (void *)desc;
        struct ffs_function *func = priv;
        struct ffs_ep *ffs_ep;
-
-       /*
-        * If hs_descriptors is not NULL then we are reading hs
-        * descriptors now
-        */
-       const int isHS = func->function.hs_descriptors != NULL;
-       unsigned idx;
+       unsigned ep_desc_id, idx;
+       static const char *speed_names[] = { "full", "high", "super" };
 
        if (type != FFS_DESCRIPTOR)
                return 0;
 
-       if (isHS)
+       /*
+        * If ss_descriptors is not NULL, we are reading super speed
+        * descriptors; if hs_descriptors is not NULL, we are reading high
+        * speed descriptors; otherwise, we are reading full speed
+        * descriptors.
+        */
+       if (func->function.ss_descriptors) {
+               ep_desc_id = 2;
+               func->function.ss_descriptors[(long)valuep] = desc;
+       } else if (func->function.hs_descriptors) {
+               ep_desc_id = 1;
                func->function.hs_descriptors[(long)valuep] = desc;
-       else
+       } else {
+               ep_desc_id = 0;
                func->function.fs_descriptors[(long)valuep]    = desc;
+       }
 
        if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT)
                return 0;
@@ -1872,13 +2192,13 @@ static int __ffs_func_bind_do_descs(enum ffs_entity_type type, u8 *valuep,
        idx = (ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) - 1;
        ffs_ep = func->eps + idx;
 
-       if (unlikely(ffs_ep->descs[isHS])) {
-               pr_vdebug("two %sspeed descriptors for EP %d\n",
-                         isHS ? "high" : "full",
+       if (unlikely(ffs_ep->descs[ep_desc_id])) {
+               pr_err("two %sspeed descriptors for EP %d\n",
+                         speed_names[ep_desc_id],
                          ds->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
                return -EINVAL;
        }
-       ffs_ep->descs[isHS] = ds;
+       ffs_ep->descs[ep_desc_id] = ds;
 
        ffs_dump_mem(": Original  ep desc", ds, ds->bLength);
        if (ffs_ep->ep) {
@@ -2022,8 +2342,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
        const int full = !!func->ffs->fs_descs_count;
        const int high = gadget_is_dualspeed(func->gadget) &&
                func->ffs->hs_descs_count;
+       const int super = gadget_is_superspeed(func->gadget) &&
+               func->ffs->ss_descs_count;
 
-       int ret;
+       int fs_len, hs_len, ret;
 
        /* Make it a single chunk, less management later on */
        vla_group(d);
@@ -2032,15 +2354,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
                full ? ffs->fs_descs_count + 1 : 0);
        vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
                high ? ffs->hs_descs_count + 1 : 0);
+       vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
+               super ? ffs->ss_descs_count + 1 : 0);
        vla_item_with_sz(d, short, inums, ffs->interfaces_count);
-       vla_item_with_sz(d, char, raw_descs,
-               high ? ffs->raw_descs_length : ffs->raw_fs_descs_length);
+       vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
        char *vlabuf;
 
        ENTER();
 
-       /* Only high speed but not supported by gadget? */
-       if (unlikely(!(full | high)))
+       /* Has descriptors only for speeds gadget does not support */
+       if (unlikely(!(full | high | super)))
                return -ENOTSUPP;
 
        /* Allocate a single chunk, less management later on */
@@ -2050,8 +2373,10 @@ static int _ffs_func_bind(struct usb_configuration *c,
 
        /* Zero */
        memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
-       memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16,
-              d_raw_descs__sz);
+       /* Copy descriptors  */
+       memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
+              ffs->raw_descs_length);
+
        memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
        for (ret = ffs->eps_count; ret; --ret) {
                struct ffs_ep *ptr;
@@ -2073,22 +2398,38 @@ static int _ffs_func_bind(struct usb_configuration *c,
         */
        if (likely(full)) {
                func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
-               ret = ffs_do_descs(ffs->fs_descs_count,
-                                  vla_ptr(vlabuf, d, raw_descs),
-                                  d_raw_descs__sz,
-                                  __ffs_func_bind_do_descs, func);
-               if (unlikely(ret < 0))
+               fs_len = ffs_do_descs(ffs->fs_descs_count,
+                                     vla_ptr(vlabuf, d, raw_descs),
+                                     d_raw_descs__sz,
+                                     __ffs_func_bind_do_descs, func);
+               if (unlikely(fs_len < 0)) {
+                       ret = fs_len;
                        goto error;
+               }
        } else {
-               ret = 0;
+               fs_len = 0;
        }
 
        if (likely(high)) {
                func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
-               ret = ffs_do_descs(ffs->hs_descs_count,
-                                  vla_ptr(vlabuf, d, raw_descs) + ret,
-                                  d_raw_descs__sz - ret,
-                                  __ffs_func_bind_do_descs, func);
+               hs_len = ffs_do_descs(ffs->hs_descs_count,
+                                     vla_ptr(vlabuf, d, raw_descs) + fs_len,
+                                     d_raw_descs__sz - fs_len,
+                                     __ffs_func_bind_do_descs, func);
+               if (unlikely(hs_len < 0)) {
+                       ret = hs_len;
+                       goto error;
+               }
+       } else {
+               hs_len = 0;
+       }
+
+       if (likely(super)) {
+               func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
+               ret = ffs_do_descs(ffs->ss_descs_count,
+                               vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
+                               d_raw_descs__sz - fs_len - hs_len,
+                               __ffs_func_bind_do_descs, func);
                if (unlikely(ret < 0))
                        goto error;
        }
@@ -2099,7 +2440,8 @@ static int _ffs_func_bind(struct usb_configuration *c,
         * now.
         */
        ret = ffs_do_descs(ffs->fs_descs_count +
-                          (high ? ffs->hs_descs_count : 0),
+                          (high ? ffs->hs_descs_count : 0) +
+                          (super ? ffs->ss_descs_count : 0),
                           vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
                           __ffs_func_bind_do_nums, func);
        if (unlikely(ret < 0))
@@ -2258,7 +2600,7 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
 
 static LIST_HEAD(ffs_devices);
 
-static struct ffs_dev *_ffs_find_dev(const char *name)
+static struct ffs_dev *_ffs_do_find_dev(const char *name)
 {
        struct ffs_dev *dev;
 
@@ -2275,7 +2617,7 @@ static struct ffs_dev *_ffs_find_dev(const char *name)
 /*
  * ffs_lock must be taken by the caller of this function
  */
-static struct ffs_dev *ffs_get_single_dev(void)
+static struct ffs_dev *_ffs_get_single_dev(void)
 {
        struct ffs_dev *dev;
 
@@ -2291,15 +2633,15 @@ static struct ffs_dev *ffs_get_single_dev(void)
 /*
  * ffs_lock must be taken by the caller of this function
  */
-static struct ffs_dev *ffs_find_dev(const char *name)
+static struct ffs_dev *_ffs_find_dev(const char *name)
 {
        struct ffs_dev *dev;
 
-       dev = ffs_get_single_dev();
+       dev = _ffs_get_single_dev();
        if (dev)
                return dev;
 
-       return _ffs_find_dev(name);
+       return _ffs_do_find_dev(name);
 }
 
 /* Configfs support *********************************************************/
@@ -2335,7 +2677,7 @@ static void ffs_free_inst(struct usb_function_instance *f)
 
        opts = to_f_fs_opts(f);
        ffs_dev_lock();
-       ffs_free_dev(opts->dev);
+       _ffs_free_dev(opts->dev);
        ffs_dev_unlock();
        kfree(opts);
 }
@@ -2390,7 +2732,7 @@ static struct usb_function_instance *ffs_alloc_inst(void)
        opts->func_inst.set_inst_name = ffs_set_inst_name;
        opts->func_inst.free_func_inst = ffs_free_inst;
        ffs_dev_lock();
-       dev = ffs_alloc_dev();
+       dev = _ffs_alloc_dev();
        ffs_dev_unlock();
        if (IS_ERR(dev)) {
                kfree(opts);
@@ -2446,6 +2788,7 @@ static void ffs_func_unbind(struct usb_configuration *c,
         */
        func->function.fs_descriptors = NULL;
        func->function.hs_descriptors = NULL;
+       func->function.ss_descriptors = NULL;
        func->interfaces_nums = NULL;
 
        ffs_event_add(ffs, FUNCTIONFS_UNBIND);
@@ -2478,12 +2821,12 @@ static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
 /*
  * ffs_lock must be taken by the caller of this function
  */
-struct ffs_dev *ffs_alloc_dev(void)
+static struct ffs_dev *_ffs_alloc_dev(void)
 {
        struct ffs_dev *dev;
        int ret;
 
-       if (ffs_get_single_dev())
+       if (_ffs_get_single_dev())
                        return ERR_PTR(-EBUSY);
 
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
@@ -2511,10 +2854,10 @@ static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
 {
        struct ffs_dev *existing;
 
-       existing = _ffs_find_dev(name);
+       existing = _ffs_do_find_dev(name);
        if (existing)
                return -EBUSY;
-       
+
        dev->name = name;
 
        return 0;
@@ -2555,7 +2898,7 @@ EXPORT_SYMBOL(ffs_single_dev);
 /*
  * ffs_lock must be taken by the caller of this function
  */
-void ffs_free_dev(struct ffs_dev *dev)
+static void _ffs_free_dev(struct ffs_dev *dev)
 {
        list_del(&dev->entry);
        if (dev->name_allocated)
@@ -2572,7 +2915,7 @@ static void *ffs_acquire_dev(const char *dev_name)
        ENTER();
        ffs_dev_lock();
 
-       ffs_dev = ffs_find_dev(dev_name);
+       ffs_dev = _ffs_find_dev(dev_name);
        if (!ffs_dev)
                ffs_dev = ERR_PTR(-ENODEV);
        else if (ffs_dev->mounted)
@@ -2595,11 +2938,12 @@ static void ffs_release_dev(struct ffs_data *ffs_data)
        ffs_dev_lock();
 
        ffs_dev = ffs_data->private_data;
-       if (ffs_dev)
+       if (ffs_dev) {
                ffs_dev->mounted = false;
-       
-       if (ffs_dev->ffs_release_dev_callback)
-               ffs_dev->ffs_release_dev_callback(ffs_dev);
+
+               if (ffs_dev->ffs_release_dev_callback)
+                       ffs_dev->ffs_release_dev_callback(ffs_dev);
+       }
 
        ffs_dev_unlock();
 }
index f1a59190ac9a0b2ad002ca5a0ebcd3d9668126b6..df4a0dcbc99350f70d84e8c77a1bb0713041ac9b 100644 (file)
@@ -276,7 +276,7 @@ static int geth_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
        }
 
        net = gether_connect(&geth->port);
-       return IS_ERR(net) ? PTR_ERR(net) : 0;
+       return PTR_RET(net);
 }
 
 static void geth_disable(struct usb_function *f)
index 914cbd84ee40b8c6f14af524eb914d76c9c31aac..f984ee75324d33b9fcf248588a45801538812450 100644 (file)
@@ -225,14 +225,8 @@ static void gr_dfs_create(struct gr_udc *dev)
        const char *name = "gr_udc_state";
 
        dev->dfs_root = debugfs_create_dir(dev_name(dev->dev), NULL);
-       if (IS_ERR(dev->dfs_root)) {
-               dev_err(dev->dev, "Failed to create debugfs directory\n");
-               return;
-       }
-       dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root,
-                                            dev, &gr_dfs_fops);
-       if (IS_ERR(dev->dfs_state))
-               dev_err(dev->dev, "Failed to create debugfs file %s\n", name);
+       dev->dfs_state = debugfs_create_file(name, 0444, dev->dfs_root, dev,
+                                            &gr_dfs_fops);
 }
 
 static void gr_dfs_delete(struct gr_udc *dev)
index b94c049ab0d0877762a5d76e1cac957eff34771f..b5be6f0308c270f2a844db3f00e7884380e5c2e6 100644 (file)
@@ -439,11 +439,9 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
        /* FIXME writebehind for O_NONBLOCK and poll(), qlen = 1 */
 
        value = -ENOMEM;
-       kbuf = kmalloc (len, GFP_KERNEL);
-       if (!kbuf)
-               goto free1;
-       if (copy_from_user (kbuf, buf, len)) {
-               value = -EFAULT;
+       kbuf = memdup_user(buf, len);
+       if (!kbuf) {
+               value = PTR_ERR(kbuf);
                goto free1;
        }
 
@@ -452,7 +450,6 @@ ep_write (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
                data->name, len, (int) value);
 free1:
        mutex_unlock(&data->lock);
-       kfree (kbuf);
        return value;
 }
 
index 049ebab0d3607e7719843335c14201878df285c2..a139894c600fc6a5f022d78e37f1d15ce0bc2934 100644 (file)
@@ -3295,9 +3295,9 @@ usb_clk_enable_fail:
 pll_set_fail:
        clk_disable(udc->usb_pll_clk);
 pll_enable_fail:
-       clk_put(udc->usb_slv_clk);
-usb_otg_clk_get_fail:
        clk_put(udc->usb_otg_clk);
+usb_otg_clk_get_fail:
+       clk_put(udc->usb_slv_clk);
 usb_clk_get_fail:
        clk_put(udc->usb_pll_clk);
 pll_get_fail:
index 69b76efd11e9bea3f11d9d2effe293c04045f01c..6474081dcbaff171dcc1413d78de383716977dcd 100644 (file)
@@ -427,12 +427,17 @@ setup_rx_reqs(struct printer_dev *dev)
                req->length = USB_BUFSIZE;
                req->complete = rx_complete;
 
+               /* here, we unlock, and only unlock, to avoid deadlock. */
+               spin_unlock(&dev->lock);
                error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
+               spin_lock(&dev->lock);
                if (error) {
                        DBG(dev, "rx submit --> %d\n", error);
                        list_add(&req->list, &dev->rx_reqs);
                        break;
-               } else {
+               }
+               /* if the req is empty, then add it into dev->rx_reqs_active. */
+               else if (list_empty(&req->list)) {
                        list_add(&req->list, &dev->rx_reqs_active);
                }
        }
@@ -1133,6 +1138,7 @@ static int __init printer_bind_config(struct usb_configuration *c)
                                  NULL, "g_printer");
        if (IS_ERR(dev->pdev)) {
                ERROR(dev, "Failed to create device: g_printer\n");
+               status = PTR_ERR(dev->pdev);
                goto fail;
        }
 
index 1172eaeddd85f14dfa5772b6169dad1715832967..2a9cb674926aa860ada38454ac1227aad50512e2 100644 (file)
@@ -617,7 +617,7 @@ static int s3c_hsotg_write_fifo(struct s3c_hsotg *hsotg,
        to_write = DIV_ROUND_UP(to_write, 4);
        data = hs_req->req.buf + buf_pos;
 
-       writesl(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
+       iowrite32_rep(hsotg->regs + EPFIFO(hs_ep->index), data, to_write);
 
        return (to_write >= can_write) ? -ENOSPC : 0;
 }
@@ -720,8 +720,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
                ureq->length, ureq->actual);
        if (0)
                dev_dbg(hsotg->dev,
-                       "REQ buf %p len %d dma 0x%08x noi=%d zp=%d snok=%d\n",
-                       ureq->buf, length, ureq->dma,
+                       "REQ buf %p len %d dma 0x%pad noi=%d zp=%d snok=%d\n",
+                       ureq->buf, length, &ureq->dma,
                        ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
 
        maxreq = get_ep_limit(hs_ep);
@@ -789,8 +789,8 @@ static void s3c_hsotg_start_req(struct s3c_hsotg *hsotg,
                dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index);
                writel(ureq->dma, hsotg->regs + dma_reg);
 
-               dev_dbg(hsotg->dev, "%s: 0x%08x => 0x%08x\n",
-                       __func__, ureq->dma, dma_reg);
+               dev_dbg(hsotg->dev, "%s: 0x%pad => 0x%08x\n",
+                       __func__, &ureq->dma, dma_reg);
        }
 
        ctrl |= DxEPCTL_EPEna;  /* ensure ep enabled */
@@ -1185,6 +1185,41 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg,
 static void s3c_hsotg_enqueue_setup(struct s3c_hsotg *hsotg);
 static void s3c_hsotg_disconnect(struct s3c_hsotg *hsotg);
 
+/**
+ * s3c_hsotg_stall_ep0 - stall ep0
+ * @hsotg: The device state
+ *
+ * Set stall for ep0 as response for setup request.
+ */
+static void s3c_hsotg_stall_ep0(struct s3c_hsotg *hsotg) {
+       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       u32 reg;
+       u32 ctrl;
+
+       dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
+       reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
+
+       /*
+        * DxEPCTL_Stall will be cleared by EP once it has
+        * taken effect, so no need to clear later.
+        */
+
+       ctrl = readl(hsotg->regs + reg);
+       ctrl |= DxEPCTL_Stall;
+       ctrl |= DxEPCTL_CNAK;
+       writel(ctrl, hsotg->regs + reg);
+
+       dev_dbg(hsotg->dev,
+               "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
+               ctrl, reg, readl(hsotg->regs + reg));
+
+        /*
+         * complete won't be called, so we enqueue
+         * setup request here
+         */
+        s3c_hsotg_enqueue_setup(hsotg);
+}
+
 /**
  * s3c_hsotg_process_control - process a control request
  * @hsotg: The device state
@@ -1262,38 +1297,8 @@ static void s3c_hsotg_process_control(struct s3c_hsotg *hsotg,
         * so respond with a STALL for the status stage to indicate failure.
         */
 
-       if (ret < 0) {
-               u32 reg;
-               u32 ctrl;
-
-               dev_dbg(hsotg->dev, "ep0 stall (dir=%d)\n", ep0->dir_in);
-               reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0;
-
-               /*
-                * DxEPCTL_Stall will be cleared by EP once it has
-                * taken effect, so no need to clear later.
-                */
-
-               ctrl = readl(hsotg->regs + reg);
-               ctrl |= DxEPCTL_Stall;
-               ctrl |= DxEPCTL_CNAK;
-               writel(ctrl, hsotg->regs + reg);
-
-               dev_dbg(hsotg->dev,
-                       "written DxEPCTL=0x%08x to %08x (DxEPCTL=0x%08x)\n",
-                       ctrl, reg, readl(hsotg->regs + reg));
-
-               /*
-                * don't believe we need to anything more to get the EP
-                * to reply with a STALL packet
-                */
-
-                /*
-                 * complete won't be called, so we enqueue
-                 * setup request here
-                 */
-                s3c_hsotg_enqueue_setup(hsotg);
-       }
+       if (ret < 0)
+               s3c_hsotg_stall_ep0(hsotg);
 }
 
 /**
@@ -1488,7 +1493,7 @@ static void s3c_hsotg_rx_data(struct s3c_hsotg *hsotg, int ep_idx, int size)
         * note, we might over-write the buffer end by 3 bytes depending on
         * alignment of the data.
         */
-       readsl(fifo, hs_req->req.buf + read_ptr, to_read);
+       ioread32_rep(fifo, hs_req->req.buf + read_ptr, to_read);
 }
 
 /**
@@ -2832,6 +2837,15 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 
        dev_info(hs->dev, "%s(ep %p %s, %d)\n", __func__, ep, ep->name, value);
 
+       if (index == 0) {
+               if (value)
+                       s3c_hsotg_stall_ep0(hs);
+               else
+                       dev_warn(hs->dev,
+                                "%s: can't clear halt on ep0\n", __func__);
+               return 0;
+       }
+
        /* write both IN and OUT control registers */
 
        epreg = DIEPCTL(index);
@@ -3760,10 +3774,55 @@ static int s3c_hsotg_remove(struct platform_device *pdev)
        return 0;
 }
 
-#if 1
-#define s3c_hsotg_suspend NULL
-#define s3c_hsotg_resume NULL
-#endif
+static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
+       unsigned long flags;
+       int ret = 0;
+
+       if (hsotg->driver)
+               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);
+
+       if (hsotg->driver) {
+               int ep;
+               for (ep = 0; ep < hsotg->num_of_eps; ep++)
+                       s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
+
+               ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
+                                            hsotg->supplies);
+       }
+
+       return ret;
+}
+
+static int s3c_hsotg_resume(struct platform_device *pdev)
+{
+       struct s3c_hsotg *hsotg = platform_get_drvdata(pdev);
+       unsigned long flags;
+       int ret = 0;
+
+       if (hsotg->driver) {
+               dev_info(hsotg->dev, "resuming usb gadget %s\n",
+                        hsotg->driver->driver.name);
+               ret = regulator_bulk_enable(ARRAY_SIZE(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);
+
+       return ret;
+}
 
 #ifdef CONFIG_OF
 static const struct of_device_id s3c_hsotg_of_ids[] = {
index ea4bbfe72ec0ba1ef0ff111fd1f982b2db981565..10c6a128250c6cd249bb56b35655ec946820dc32 100644 (file)
@@ -1344,7 +1344,6 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
 
        return 0;
 err_add_udc:
-err_add_device:
        clk_disable(hsudc->uclk);
 err_res:
        if (!IS_ERR_OR_NULL(hsudc->transceiver))
index 0f8aad78b54f7095a26b6e413a22ff5ae1e016df..460c266b8e24bf2bf3c4b8459f2cb5443b9694e2 100644 (file)
@@ -1613,7 +1613,7 @@ static struct se_wwn *usbg_make_tport(
                return ERR_PTR(-ENOMEM);
        }
        tport->tport_wwpn = wwpn;
-       snprintf(tport->tport_name, sizeof(tport->tport_name), wnn_name);
+       snprintf(tport->tport_name, sizeof(tport->tport_name), "%s", wnn_name);
        return &tport->tport_wwn;
 }
 
index b7d4f82872b700e14d4fc2469bcc1f936a94bf61..50d09c289137024271a480acab22ff1d8443d96e 100644 (file)
@@ -48,6 +48,8 @@
 
 #define UETH__VERSION  "29-May-2008"
 
+#define GETHER_NAPI_WEIGHT     32
+
 struct eth_dev {
        /* lock is held while accessing port_usb
         */
@@ -72,6 +74,7 @@ struct eth_dev {
                                                struct sk_buff_head *list);
 
        struct work_struct      work;
+       struct napi_struct      rx_napi;
 
        unsigned long           todo;
 #define        WORK_RX_MEMORY          0
@@ -253,18 +256,16 @@ enomem:
                DBG(dev, "rx submit --> %d\n", retval);
                if (skb)
                        dev_kfree_skb_any(skb);
-               spin_lock_irqsave(&dev->req_lock, flags);
-               list_add(&req->list, &dev->rx_reqs);
-               spin_unlock_irqrestore(&dev->req_lock, flags);
        }
        return retval;
 }
 
 static void rx_complete(struct usb_ep *ep, struct usb_request *req)
 {
-       struct sk_buff  *skb = req->context, *skb2;
+       struct sk_buff  *skb = req->context;
        struct eth_dev  *dev = ep->driver_data;
        int             status = req->status;
+       bool            rx_queue = 0;
 
        switch (status) {
 
@@ -288,30 +289,8 @@ static void rx_complete(struct usb_ep *ep, struct usb_request *req)
                } else {
                        skb_queue_tail(&dev->rx_frames, skb);
                }
-               skb = NULL;
-
-               skb2 = skb_dequeue(&dev->rx_frames);
-               while (skb2) {
-                       if (status < 0
-                                       || ETH_HLEN > skb2->len
-                                       || skb2->len > VLAN_ETH_FRAME_LEN) {
-                               dev->net->stats.rx_errors++;
-                               dev->net->stats.rx_length_errors++;
-                               DBG(dev, "rx length %d\n", skb2->len);
-                               dev_kfree_skb_any(skb2);
-                               goto next_frame;
-                       }
-                       skb2->protocol = eth_type_trans(skb2, dev->net);
-                       dev->net->stats.rx_packets++;
-                       dev->net->stats.rx_bytes += skb2->len;
-
-                       /* no buffer copies needed, unless hardware can't
-                        * use skb buffers.
-                        */
-                       status = netif_rx(skb2);
-next_frame:
-                       skb2 = skb_dequeue(&dev->rx_frames);
-               }
+               if (!status)
+                       rx_queue = 1;
                break;
 
        /* software-driven interface shutdown */
@@ -334,22 +313,20 @@ quiesce:
                /* FALLTHROUGH */
 
        default:
+               rx_queue = 1;
+               dev_kfree_skb_any(skb);
                dev->net->stats.rx_errors++;
                DBG(dev, "rx status %d\n", status);
                break;
        }
 
-       if (skb)
-               dev_kfree_skb_any(skb);
-       if (!netif_running(dev->net)) {
 clean:
                spin_lock(&dev->req_lock);
                list_add(&req->list, &dev->rx_reqs);
                spin_unlock(&dev->req_lock);
-               req = NULL;
-       }
-       if (req)
-               rx_submit(dev, req, GFP_ATOMIC);
+
+       if (rx_queue && likely(napi_schedule_prep(&dev->rx_napi)))
+               __napi_schedule(&dev->rx_napi);
 }
 
 static int prealloc(struct list_head *list, struct usb_ep *ep, unsigned n)
@@ -414,16 +391,24 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
 {
        struct usb_request      *req;
        unsigned long           flags;
+       int                     rx_counts = 0;
 
        /* fill unused rxq slots with some skb */
        spin_lock_irqsave(&dev->req_lock, flags);
        while (!list_empty(&dev->rx_reqs)) {
+
+               if (++rx_counts > qlen(dev->gadget, dev->qmult))
+                       break;
+
                req = container_of(dev->rx_reqs.next,
                                struct usb_request, list);
                list_del_init(&req->list);
                spin_unlock_irqrestore(&dev->req_lock, flags);
 
                if (rx_submit(dev, req, gfp_flags) < 0) {
+                       spin_lock_irqsave(&dev->req_lock, flags);
+                       list_add(&req->list, &dev->rx_reqs);
+                       spin_unlock_irqrestore(&dev->req_lock, flags);
                        defer_kevent(dev, WORK_RX_MEMORY);
                        return;
                }
@@ -433,6 +418,41 @@ static void rx_fill(struct eth_dev *dev, gfp_t gfp_flags)
        spin_unlock_irqrestore(&dev->req_lock, flags);
 }
 
+static int gether_poll(struct napi_struct *napi, int budget)
+{
+       struct eth_dev  *dev = container_of(napi, struct eth_dev, rx_napi);
+       struct sk_buff  *skb;
+       unsigned int    work_done = 0;
+       int             status = 0;
+
+       while ((skb = skb_dequeue(&dev->rx_frames))) {
+               if (status < 0
+                               || ETH_HLEN > skb->len
+                               || skb->len > VLAN_ETH_FRAME_LEN) {
+                       dev->net->stats.rx_errors++;
+                       dev->net->stats.rx_length_errors++;
+                       DBG(dev, "rx length %d\n", skb->len);
+                       dev_kfree_skb_any(skb);
+                       continue;
+               }
+               skb->protocol = eth_type_trans(skb, dev->net);
+               dev->net->stats.rx_packets++;
+               dev->net->stats.rx_bytes += skb->len;
+
+               status = netif_rx_ni(skb);
+       }
+
+       if (netif_running(dev->net)) {
+               rx_fill(dev, GFP_KERNEL);
+               work_done++;
+       }
+
+       if (work_done < budget)
+               napi_complete(&dev->rx_napi);
+
+       return work_done;
+}
+
 static void eth_work(struct work_struct *work)
 {
        struct eth_dev  *dev = container_of(work, struct eth_dev, work);
@@ -625,6 +645,7 @@ static void eth_start(struct eth_dev *dev, gfp_t gfp_flags)
        /* and open the tx floodgates */
        atomic_set(&dev->tx_qlen, 0);
        netif_wake_queue(dev->net);
+       napi_enable(&dev->rx_napi);
 }
 
 static int eth_open(struct net_device *net)
@@ -651,6 +672,7 @@ static int eth_stop(struct net_device *net)
        unsigned long   flags;
 
        VDBG(dev, "%s\n", __func__);
+       napi_disable(&dev->rx_napi);
        netif_stop_queue(net);
 
        DBG(dev, "stop stats: rx/tx %ld/%ld, errs %ld/%ld\n",
@@ -768,6 +790,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
                return ERR_PTR(-ENOMEM);
 
        dev = netdev_priv(net);
+       netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
        spin_lock_init(&dev->lock);
        spin_lock_init(&dev->req_lock);
        INIT_WORK(&dev->work, eth_work);
@@ -830,6 +853,7 @@ struct net_device *gether_setup_name_default(const char *netname)
                return ERR_PTR(-ENOMEM);
 
        dev = netdev_priv(net);
+       netif_napi_add(net, &dev->rx_napi, gether_poll, GETHER_NAPI_WEIGHT);
        spin_lock_init(&dev->lock);
        spin_lock_init(&dev->req_lock);
        INIT_WORK(&dev->work, eth_work);
@@ -1113,6 +1137,7 @@ void gether_disconnect(struct gether *link)
 {
        struct eth_dev          *dev = link->ioport;
        struct usb_request      *req;
+       struct sk_buff          *skb;
 
        WARN_ON(!dev);
        if (!dev)
@@ -1139,6 +1164,12 @@ void gether_disconnect(struct gether *link)
                spin_lock(&dev->req_lock);
        }
        spin_unlock(&dev->req_lock);
+
+       spin_lock(&dev->rx_frames.lock);
+       while ((skb = __skb_dequeue(&dev->rx_frames)))
+               dev_kfree_skb_any(skb);
+       spin_unlock(&dev->rx_frames.lock);
+
        link->in_ep->driver_data = NULL;
        link->in_ep->desc = NULL;
 
index bc2d3718219b9ec60a39e7cc0c3ce2ca4cff8a02..bf0ba375d459ac59710711815a934b7f97c8f6ab 100644 (file)
@@ -65,10 +65,8 @@ static inline void ffs_dev_unlock(void)
        mutex_unlock(&ffs_lock);
 }
 
-struct ffs_dev *ffs_alloc_dev(void);
 int ffs_name_dev(struct ffs_dev *dev, const char *name);
 int ffs_single_dev(struct ffs_dev *dev);
-void ffs_free_dev(struct ffs_dev *dev);
 
 struct ffs_epfile;
 struct ffs_function;
@@ -125,7 +123,7 @@ enum ffs_setup_state {
         * setup.  If this state is set read/write on ep0 return
         * -EIDRM.  This state is only set when adding event.
         */
-       FFS_SETUP_CANCELED
+       FFS_SETUP_CANCELLED
 };
 
 struct ffs_data {
@@ -156,7 +154,6 @@ struct ffs_data {
         */
        struct usb_request              *ep0req;                /* P: mutex */
        struct completion               ep0req_completion;      /* P: mutex */
-       int                             ep0req_status;          /* P: mutex */
 
        /* reference counter */
        atomic_t                        ref;
@@ -168,19 +165,18 @@ struct ffs_data {
 
        /*
         * Possible transitions:
-        * + FFS_NO_SETUP       -> FFS_SETUP_PENDING  -- P: ev.waitq.lock
+        * + FFS_NO_SETUP        -> FFS_SETUP_PENDING  -- P: ev.waitq.lock
         *               happens only in ep0 read which is P: mutex
-        * + FFS_SETUP_PENDING  -> FFS_NO_SETUP       -- P: ev.waitq.lock
+        * + FFS_SETUP_PENDING   -> FFS_NO_SETUP       -- P: ev.waitq.lock
         *               happens only in ep0 i/o  which is P: mutex
-        * + FFS_SETUP_PENDING  -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
-        * + FFS_SETUP_CANCELED -> FFS_NO_SETUP       -- cmpxchg
+        * + FFS_SETUP_PENDING   -> FFS_SETUP_CANCELLED -- P: ev.waitq.lock
+        * + FFS_SETUP_CANCELLED -> FFS_NO_SETUP        -- cmpxchg
+        *
+        * This field should never be accessed directly and instead
+        * ffs_setup_state_clear_cancelled function should be used.
         */
        enum ffs_setup_state            setup_state;
 
-#define FFS_SETUP_STATE(ffs)                                   \
-       ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state,     \
-                                      FFS_SETUP_CANCELED, FFS_NO_SETUP))
-
        /* Events & such. */
        struct {
                u8                              types[4];
@@ -210,16 +206,16 @@ struct ffs_data {
 
        /* filled by __ffs_data_got_descs() */
        /*
-        * Real descriptors are 16 bytes after raw_descs (so you need
-        * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
-        * first full speed descriptor).  raw_descs_length and
-        * raw_fs_descs_length do not have those 16 bytes added.
+        * raw_descs is what you kfree, real_descs points inside of raw_descs,
+        * where full speed, high speed and super speed descriptors start.
+        * real_descs_length is the length of all those descriptors.
         */
+       const void                      *raw_descs_data;
        const void                      *raw_descs;
        unsigned                        raw_descs_length;
-       unsigned                        raw_fs_descs_length;
        unsigned                        fs_descs_count;
        unsigned                        hs_descs_count;
+       unsigned                        ss_descs_count;
 
        unsigned short                  strings_count;
        unsigned short                  interfaces_count;
index a9707da7da0bff969c68d6d5a0fa66f2816ef8b7..e22b82660831a00bb9b42803538e1816562c7a6f 100644 (file)
@@ -584,7 +584,6 @@ config FHCI_DEBUG
 config USB_U132_HCD
        tristate "Elan U132 Adapter Host Controller"
        depends on USB_FTDI_ELAN
-       default M
        help
          The U132 adapter is a USB to CardBus adapter specifically designed
          for PC cards that contain an OHCI host controller. Typical PC cards
index 01536cfd361da2bb5f275cd1812ae2eb2e9b9729..b3a0e11073aae8a3ed873305cd1737a1d2a45beb 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2007 Steven Brown <sbrown@cortland.com>
  * Copyright 2010-2012 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2014 Hans de Goede <hdegoede@redhat.com>
  *
  * Derived from the ohci-ssb driver
  * Copyright 2007 Michael Buesch <m@bues.ch>
@@ -18,6 +19,7 @@
  *
  * Licensed under the GNU/GPL. See COPYING for details.
  */
+#include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/kernel.h>
@@ -25,6 +27,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 #include "ehci.h"
 
 #define DRIVER_DESC "EHCI generic platform driver"
+#define EHCI_MAX_CLKS 3
+#define hcd_to_ehci_priv(h) ((struct ehci_platform_priv *)hcd_to_ehci(h)->priv)
+
+struct ehci_platform_priv {
+       struct clk *clks[EHCI_MAX_CLKS];
+       struct phy *phy;
+};
 
 static const char hcd_name[] = "ehci-platform";
 
@@ -45,8 +55,6 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
 
        hcd->has_tt = pdata->has_tt;
        ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
-       ehci->big_endian_desc = pdata->big_endian_desc;
-       ehci->big_endian_mmio = pdata->big_endian_mmio;
 
        if (pdata->pre_setup) {
                retval = pdata->pre_setup(hcd);
@@ -64,38 +72,91 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
        return 0;
 }
 
+static int ehci_platform_power_on(struct platform_device *dev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(dev);
+       struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+       int clk, ret;
+
+       for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
+               ret = clk_prepare_enable(priv->clks[clk]);
+               if (ret)
+                       goto err_disable_clks;
+       }
+
+       if (priv->phy) {
+               ret = phy_init(priv->phy);
+               if (ret)
+                       goto err_disable_clks;
+
+               ret = phy_power_on(priv->phy);
+               if (ret)
+                       goto err_exit_phy;
+       }
+
+       return 0;
+
+err_exit_phy:
+       phy_exit(priv->phy);
+err_disable_clks:
+       while (--clk >= 0)
+               clk_disable_unprepare(priv->clks[clk]);
+
+       return ret;
+}
+
+static void ehci_platform_power_off(struct platform_device *dev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(dev);
+       struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+       int clk;
+
+       if (priv->phy) {
+               phy_power_off(priv->phy);
+               phy_exit(priv->phy);
+       }
+
+       for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
+               if (priv->clks[clk])
+                       clk_disable_unprepare(priv->clks[clk]);
+}
+
 static struct hc_driver __read_mostly ehci_platform_hc_driver;
 
 static const struct ehci_driver_overrides platform_overrides __initconst = {
-       .reset =        ehci_platform_reset,
+       .reset =                ehci_platform_reset,
+       .extra_priv_size =      sizeof(struct ehci_platform_priv),
 };
 
-static struct usb_ehci_pdata ehci_platform_defaults;
+static struct usb_ehci_pdata ehci_platform_defaults = {
+       .power_on =             ehci_platform_power_on,
+       .power_suspend =        ehci_platform_power_off,
+       .power_off =            ehci_platform_power_off,
+};
 
 static int ehci_platform_probe(struct platform_device *dev)
 {
        struct usb_hcd *hcd;
        struct resource *res_mem;
-       struct usb_ehci_pdata *pdata;
-       int irq;
-       int err;
+       struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+       struct ehci_platform_priv *priv;
+       struct ehci_hcd *ehci;
+       int err, irq, clk = 0;
 
        if (usb_disabled())
                return -ENODEV;
 
        /*
-        * use reasonable defaults so platforms don't have to provide these.
-        * with DT probing on ARM, none of these are set.
+        * Use reasonable defaults so platforms don't have to provide these
+        * with DT probing on ARM.
         */
-       if (!dev_get_platdata(&dev->dev))
-               dev->dev.platform_data = &ehci_platform_defaults;
+       if (!pdata)
+               pdata = &ehci_platform_defaults;
 
        err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
        if (err)
                return err;
 
-       pdata = dev_get_platdata(&dev->dev);
-
        irq = platform_get_irq(dev, 0);
        if (irq < 0) {
                dev_err(&dev->dev, "no irq provided");
@@ -107,17 +168,72 @@ static int ehci_platform_probe(struct platform_device *dev)
                return -ENXIO;
        }
 
+       hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
+                            dev_name(&dev->dev));
+       if (!hcd)
+               return -ENOMEM;
+
+       platform_set_drvdata(dev, hcd);
+       dev->dev.platform_data = pdata;
+       priv = hcd_to_ehci_priv(hcd);
+       ehci = hcd_to_ehci(hcd);
+
+       if (pdata == &ehci_platform_defaults && dev->dev.of_node) {
+               if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
+                       ehci->big_endian_mmio = 1;
+
+               if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
+                       ehci->big_endian_desc = 1;
+
+               if (of_property_read_bool(dev->dev.of_node, "big-endian"))
+                       ehci->big_endian_mmio = ehci->big_endian_desc = 1;
+
+               priv->phy = devm_phy_get(&dev->dev, "usb");
+               if (IS_ERR(priv->phy)) {
+                       err = PTR_ERR(priv->phy);
+                       if (err == -EPROBE_DEFER)
+                               goto err_put_hcd;
+                       priv->phy = NULL;
+               }
+
+               for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
+                       priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
+                       if (IS_ERR(priv->clks[clk])) {
+                               err = PTR_ERR(priv->clks[clk]);
+                               if (err == -EPROBE_DEFER)
+                                       goto err_put_clks;
+                               priv->clks[clk] = NULL;
+                               break;
+                       }
+               }
+       }
+
+       if (pdata->big_endian_desc)
+               ehci->big_endian_desc = 1;
+       if (pdata->big_endian_mmio)
+               ehci->big_endian_mmio = 1;
+
+#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_MMIO
+       if (ehci->big_endian_mmio) {
+               dev_err(&dev->dev,
+                       "Error: CONFIG_USB_EHCI_BIG_ENDIAN_MMIO not set\n");
+               err = -EINVAL;
+               goto err_put_clks;
+       }
+#endif
+#ifndef CONFIG_USB_EHCI_BIG_ENDIAN_DESC
+       if (ehci->big_endian_desc) {
+               dev_err(&dev->dev,
+                       "Error: CONFIG_USB_EHCI_BIG_ENDIAN_DESC not set\n");
+               err = -EINVAL;
+               goto err_put_clks;
+       }
+#endif
+
        if (pdata->power_on) {
                err = pdata->power_on(dev);
                if (err < 0)
-                       return err;
-       }
-
-       hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
-                            dev_name(&dev->dev));
-       if (!hcd) {
-               err = -ENOMEM;
-               goto err_power;
+                       goto err_put_clks;
        }
 
        hcd->rsrc_start = res_mem->start;
@@ -126,22 +242,28 @@ static int ehci_platform_probe(struct platform_device *dev)
        hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
-               goto err_put_hcd;
+               goto err_power;
        }
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err)
-               goto err_put_hcd;
+               goto err_power;
 
        device_wakeup_enable(hcd->self.controller);
        platform_set_drvdata(dev, hcd);
 
        return err;
 
-err_put_hcd:
-       usb_put_hcd(hcd);
 err_power:
        if (pdata->power_off)
                pdata->power_off(dev);
+err_put_clks:
+       while (--clk >= 0)
+               clk_put(priv->clks[clk]);
+err_put_hcd:
+       if (pdata == &ehci_platform_defaults)
+               dev->dev.platform_data = NULL;
+
+       usb_put_hcd(hcd);
 
        return err;
 }
@@ -150,13 +272,19 @@ static int ehci_platform_remove(struct platform_device *dev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
        struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
+       struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
+       int clk;
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(hcd);
 
        if (pdata->power_off)
                pdata->power_off(dev);
 
+       for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++)
+               clk_put(priv->clks[clk]);
+
+       usb_put_hcd(hcd);
+
        if (pdata == &ehci_platform_defaults)
                dev->dev.platform_data = NULL;
 
@@ -207,8 +335,10 @@ static int ehci_platform_resume(struct device *dev)
 static const struct of_device_id vt8500_ehci_ids[] = {
        { .compatible = "via,vt8500-ehci", },
        { .compatible = "wm,prizm-ehci", },
+       { .compatible = "generic-ehci", },
        {}
 };
+MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
 
 static const struct platform_device_id ehci_platform_table[] = {
        { "ehci-platform", 0 },
index af28b748e87a01d840b12b3084ea063665c48485..27ac6ad53c3d9eda25c1f20c3ede43c46f3dd500 100644 (file)
 
 #include "ehci.h"
 
-#define TEGRA_USB_BASE                 0xC5000000
-#define TEGRA_USB2_BASE                        0xC5004000
-#define TEGRA_USB3_BASE                        0xC5008000
-
 #define PORT_WAKE_BITS (PORT_WKOC_E|PORT_WKDISC_E|PORT_WKCONN_E)
 
 #define TEGRA_USB_DMA_ALIGN 32
index e07669993f5835858e5fffc087cfc45dc869a75b..d0d8fadf706610ca6483cd1cea19583f6fffbf1f 100644 (file)
@@ -261,8 +261,44 @@ static int __hwahc_op_wusbhc_start(struct wusbhc *wusbhc)
                dev_err(dev, "cannot listen to notifications: %d\n", result);
                goto error_stop;
        }
+       /*
+        * If WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS is set,
+        *  disable transfer notifications.
+        */
+       if (hwahc->wa.quirks &
+               WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS) {
+               struct usb_host_interface *cur_altsetting =
+                       hwahc->wa.usb_iface->cur_altsetting;
+
+               result = usb_control_msg(hwahc->wa.usb_dev,
+                               usb_sndctrlpipe(hwahc->wa.usb_dev, 0),
+                               WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS,
+                               USB_DIR_OUT | USB_TYPE_VENDOR |
+                                       USB_RECIP_INTERFACE,
+                               WA_REQ_ALEREON_FEATURE_SET,
+                               cur_altsetting->desc.bInterfaceNumber,
+                               NULL, 0,
+                               USB_CTRL_SET_TIMEOUT);
+               /*
+                * If we successfully sent the control message, start DTI here
+                * because no transfer notifications will be received which is
+                * where DTI is normally started.
+                */
+               if (result == 0)
+                       result = wa_dti_start(&hwahc->wa);
+               else
+                       result = 0;     /* OK.  Continue normally. */
+
+               if (result < 0) {
+                       dev_err(dev, "cannot start DTI: %d\n", result);
+                       goto error_dti_start;
+               }
+       }
+
        return result;
 
+error_dti_start:
+       wa_nep_disarm(&hwahc->wa);
 error_stop:
        __wa_clear_feature(&hwahc->wa, WA_ENABLE);
        return result;
@@ -827,10 +863,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface)
 static struct usb_device_id hwahc_id_table[] = {
        /* Alereon 5310 */
        { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01),
-         .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
+         .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
+               WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
        /* Alereon 5611 */
        { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01),
-         .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
+         .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC |
+               WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS },
        /* FIXME: use class labels for this */
        { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
        {},
index 68f674cd095fa227cf06ade8eded93364184861b..b6002c951c5cbbede27756b449b9b66e4a97e1fc 100644 (file)
@@ -3,6 +3,7 @@
  *
  * Copyright 2007 Michael Buesch <m@bues.ch>
  * Copyright 2011-2012 Hauke Mehrtens <hauke@hauke-m.de>
+ * Copyright 2014 Hans de Goede <hdegoede@redhat.com>
  *
  * Derived from the OCHI-SSB driver
  * Derived from the OHCI-PCI driver
  * Licensed under the GNU/GPL. See COPYING for details.
  */
 
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
 #include <linux/hrtimer.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/err.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/usb/ohci_pdriver.h>
 #include <linux/usb.h>
 #include "ohci.h"
 
 #define DRIVER_DESC "OHCI generic platform driver"
+#define OHCI_MAX_CLKS 3
+#define hcd_to_ohci_priv(h) ((struct ohci_platform_priv *)hcd_to_ohci(h)->priv)
+
+struct ohci_platform_priv {
+       struct clk *clks[OHCI_MAX_CLKS];
+       struct phy *phy;
+};
 
 static const char hcd_name[] = "ohci-platform";
 
@@ -36,10 +47,6 @@ static int ohci_platform_reset(struct usb_hcd *hcd)
        struct usb_ohci_pdata *pdata = dev_get_platdata(&pdev->dev);
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
 
-       if (pdata->big_endian_desc)
-               ohci->flags |= OHCI_QUIRK_BE_DESC;
-       if (pdata->big_endian_mmio)
-               ohci->flags |= OHCI_QUIRK_BE_MMIO;
        if (pdata->no_big_frame_no)
                ohci->flags |= OHCI_QUIRK_FRAME_NO;
        if (pdata->num_ports)
@@ -48,11 +55,67 @@ static int ohci_platform_reset(struct usb_hcd *hcd)
        return ohci_setup(hcd);
 }
 
+static int ohci_platform_power_on(struct platform_device *dev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(dev);
+       struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+       int clk, ret;
+
+       for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++) {
+               ret = clk_prepare_enable(priv->clks[clk]);
+               if (ret)
+                       goto err_disable_clks;
+       }
+
+       if (priv->phy) {
+               ret = phy_init(priv->phy);
+               if (ret)
+                       goto err_disable_clks;
+
+               ret = phy_power_on(priv->phy);
+               if (ret)
+                       goto err_exit_phy;
+       }
+
+       return 0;
+
+err_exit_phy:
+       phy_exit(priv->phy);
+err_disable_clks:
+       while (--clk >= 0)
+               clk_disable_unprepare(priv->clks[clk]);
+
+       return ret;
+}
+
+static void ohci_platform_power_off(struct platform_device *dev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(dev);
+       struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+       int clk;
+
+       if (priv->phy) {
+               phy_power_off(priv->phy);
+               phy_exit(priv->phy);
+       }
+
+       for (clk = OHCI_MAX_CLKS - 1; clk >= 0; clk--)
+               if (priv->clks[clk])
+                       clk_disable_unprepare(priv->clks[clk]);
+}
+
 static struct hc_driver __read_mostly ohci_platform_hc_driver;
 
 static const struct ohci_driver_overrides platform_overrides __initconst = {
-       .product_desc = "Generic Platform OHCI controller",
-       .reset =        ohci_platform_reset,
+       .product_desc =         "Generic Platform OHCI controller",
+       .reset =                ohci_platform_reset,
+       .extra_priv_size =      sizeof(struct ohci_platform_priv),
+};
+
+static struct usb_ohci_pdata ohci_platform_defaults = {
+       .power_on =             ohci_platform_power_on,
+       .power_suspend =        ohci_platform_power_off,
+       .power_off =            ohci_platform_power_off,
 };
 
 static int ohci_platform_probe(struct platform_device *dev)
@@ -60,17 +123,24 @@ static int ohci_platform_probe(struct platform_device *dev)
        struct usb_hcd *hcd;
        struct resource *res_mem;
        struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
-       int irq;
-       int err = -ENOMEM;
-
-       if (!pdata) {
-               WARN_ON(1);
-               return -ENODEV;
-       }
+       struct ohci_platform_priv *priv;
+       struct ohci_hcd *ohci;
+       int err, irq, clk = 0;
 
        if (usb_disabled())
                return -ENODEV;
 
+       /*
+        * Use reasonable defaults so platforms don't have to provide these
+        * with DT probing on ARM.
+        */
+       if (!pdata)
+               pdata = &ohci_platform_defaults;
+
+       err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
+       if (err)
+               return err;
+
        irq = platform_get_irq(dev, 0);
        if (irq < 0) {
                dev_err(&dev->dev, "no irq provided");
@@ -83,17 +153,72 @@ static int ohci_platform_probe(struct platform_device *dev)
                return -ENXIO;
        }
 
+       hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
+                       dev_name(&dev->dev));
+       if (!hcd)
+               return -ENOMEM;
+
+       platform_set_drvdata(dev, hcd);
+       dev->dev.platform_data = pdata;
+       priv = hcd_to_ohci_priv(hcd);
+       ohci = hcd_to_ohci(hcd);
+
+       if (pdata == &ohci_platform_defaults && dev->dev.of_node) {
+               if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
+                       ohci->flags |= OHCI_QUIRK_BE_MMIO;
+
+               if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
+                       ohci->flags |= OHCI_QUIRK_BE_DESC;
+
+               if (of_property_read_bool(dev->dev.of_node, "big-endian"))
+                       ohci->flags |= OHCI_QUIRK_BE_MMIO | OHCI_QUIRK_BE_DESC;
+
+               priv->phy = devm_phy_get(&dev->dev, "usb");
+               if (IS_ERR(priv->phy)) {
+                       err = PTR_ERR(priv->phy);
+                       if (err == -EPROBE_DEFER)
+                               goto err_put_hcd;
+                       priv->phy = NULL;
+               }
+
+               for (clk = 0; clk < OHCI_MAX_CLKS; clk++) {
+                       priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
+                       if (IS_ERR(priv->clks[clk])) {
+                               err = PTR_ERR(priv->clks[clk]);
+                               if (err == -EPROBE_DEFER)
+                                       goto err_put_clks;
+                               priv->clks[clk] = NULL;
+                               break;
+                       }
+               }
+       }
+
+       if (pdata->big_endian_desc)
+               ohci->flags |= OHCI_QUIRK_BE_DESC;
+       if (pdata->big_endian_mmio)
+               ohci->flags |= OHCI_QUIRK_BE_MMIO;
+
+#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_MMIO
+       if (ohci->flags & OHCI_QUIRK_BE_MMIO) {
+               dev_err(&dev->dev,
+                       "Error: CONFIG_USB_OHCI_BIG_ENDIAN_MMIO not set\n");
+               err = -EINVAL;
+               goto err_put_clks;
+       }
+#endif
+#ifndef CONFIG_USB_OHCI_BIG_ENDIAN_DESC
+       if (ohci->flags & OHCI_QUIRK_BE_DESC) {
+               dev_err(&dev->dev,
+                       "Error: CONFIG_USB_OHCI_BIG_ENDIAN_DESC not set\n");
+               err = -EINVAL;
+               goto err_put_clks;
+       }
+#endif
+
        if (pdata->power_on) {
                err = pdata->power_on(dev);
                if (err < 0)
-                       return err;
-       }
-
-       hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
-                       dev_name(&dev->dev));
-       if (!hcd) {
-               err = -ENOMEM;
-               goto err_power;
+                       goto err_put_clks;
        }
 
        hcd->rsrc_start = res_mem->start;
@@ -102,11 +227,11 @@ static int ohci_platform_probe(struct platform_device *dev)
        hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
-               goto err_put_hcd;
+               goto err_power;
        }
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err)
-               goto err_put_hcd;
+               goto err_power;
 
        device_wakeup_enable(hcd->self.controller);
 
@@ -114,11 +239,17 @@ static int ohci_platform_probe(struct platform_device *dev)
 
        return err;
 
-err_put_hcd:
-       usb_put_hcd(hcd);
 err_power:
        if (pdata->power_off)
                pdata->power_off(dev);
+err_put_clks:
+       while (--clk >= 0)
+               clk_put(priv->clks[clk]);
+err_put_hcd:
+       if (pdata == &ohci_platform_defaults)
+               dev->dev.platform_data = NULL;
+
+       usb_put_hcd(hcd);
 
        return err;
 }
@@ -127,13 +258,22 @@ static int ohci_platform_remove(struct platform_device *dev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
        struct usb_ohci_pdata *pdata = dev_get_platdata(&dev->dev);
+       struct ohci_platform_priv *priv = hcd_to_ohci_priv(hcd);
+       int clk;
 
        usb_remove_hcd(hcd);
-       usb_put_hcd(hcd);
 
        if (pdata->power_off)
                pdata->power_off(dev);
 
+       for (clk = 0; clk < OHCI_MAX_CLKS && priv->clks[clk]; clk++)
+               clk_put(priv->clks[clk]);
+
+       usb_put_hcd(hcd);
+
+       if (pdata == &ohci_platform_defaults)
+               dev->dev.platform_data = NULL;
+
        return 0;
 }
 
@@ -180,6 +320,12 @@ static int ohci_platform_resume(struct device *dev)
 #define ohci_platform_resume   NULL
 #endif /* CONFIG_PM */
 
+static const struct of_device_id ohci_platform_ids[] = {
+       { .compatible = "generic-ohci", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ohci_platform_ids);
+
 static const struct platform_device_id ohci_platform_table[] = {
        { "ohci-platform", 0 },
        { }
@@ -200,6 +346,7 @@ static struct platform_driver ohci_platform_driver = {
                .owner  = THIS_MODULE,
                .name   = "ohci-platform",
                .pm     = &ohci_platform_pm_ops,
+               .of_match_table = ohci_platform_ids,
        }
 };
 
index 44e6c9da88922431f3551d4e563e2fec525ffed1..01833ab2b5c397ced4d86b83b7c3c3d476e8d286 100644 (file)
@@ -148,6 +148,7 @@ static void uhci_hcd_platform_shutdown(struct platform_device *op)
 }
 
 static const struct of_device_id platform_uhci_ids[] = {
+       { .compatible = "generic-uhci", },
        { .compatible = "platform-uhci", },
        {}
 };
index 9992fbfec85f04f2b153825ed9128f894fb25b74..1ad6bc1951c79a1dbc71b53bb709891a308ffcfc 100644 (file)
@@ -732,9 +732,11 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                /* Set the U1 and U2 exit latencies. */
                memcpy(buf, &usb_bos_descriptor,
                                USB_DT_BOS_SIZE + USB_DT_USB_SS_CAP_SIZE);
-               temp = readl(&xhci->cap_regs->hcs_params3);
-               buf[12] = HCS_U1_LATENCY(temp);
-               put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
+               if ((xhci->quirks & XHCI_LPM_SUPPORT)) {
+                       temp = readl(&xhci->cap_regs->hcs_params3);
+                       buf[12] = HCS_U1_LATENCY(temp);
+                       put_unaligned_le16(HCS_U2_LATENCY(temp), &buf[13]);
+               }
 
                /* Indicate whether the host has LTM support. */
                temp = readl(&xhci->cap_regs->hcc_params);
index bce4391a0e7d708873180ab40f3ef17737f148a1..c089668308ad181fa68a02e9f80d80440612fa7f 100644 (file)
@@ -149,14 +149,140 @@ static void xhci_link_rings(struct xhci_hcd *xhci, struct xhci_ring *ring,
        }
 }
 
+/*
+ * We need a radix tree for mapping physical addresses of TRBs to which stream
+ * ID they belong to.  We need to do this because the host controller won't tell
+ * us which stream ring the TRB came from.  We could store the stream ID in an
+ * event data TRB, but that doesn't help us for the cancellation case, since the
+ * endpoint may stop before it reaches that event data TRB.
+ *
+ * The radix tree maps the upper portion of the TRB DMA address to a ring
+ * segment that has the same upper portion of DMA addresses.  For example, say I
+ * have segments of size 1KB, that are always 1KB aligned.  A segment may
+ * start at 0x10c91000 and end at 0x10c913f0.  If I use the upper 10 bits, the
+ * key to the stream ID is 0x43244.  I can use the DMA address of the TRB to
+ * pass the radix tree a key to get the right stream ID:
+ *
+ *     0x10c90fff >> 10 = 0x43243
+ *     0x10c912c0 >> 10 = 0x43244
+ *     0x10c91400 >> 10 = 0x43245
+ *
+ * Obviously, only those TRBs with DMA addresses that are within the segment
+ * will make the radix tree return the stream ID for that ring.
+ *
+ * Caveats for the radix tree:
+ *
+ * The radix tree uses an unsigned long as a key pair.  On 32-bit systems, an
+ * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
+ * 64-bits.  Since we only request 32-bit DMA addresses, we can use that as the
+ * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
+ * PCI DMA addresses on a 64-bit system).  There might be a problem on 32-bit
+ * extended systems (where the DMA address can be bigger than 32-bits),
+ * if we allow the PCI dma mask to be bigger than 32-bits.  So don't do that.
+ */
+static int xhci_insert_segment_mapping(struct radix_tree_root *trb_address_map,
+               struct xhci_ring *ring,
+               struct xhci_segment *seg,
+               gfp_t mem_flags)
+{
+       unsigned long key;
+       int ret;
+
+       key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+       /* Skip any segments that were already added. */
+       if (radix_tree_lookup(trb_address_map, key))
+               return 0;
+
+       ret = radix_tree_maybe_preload(mem_flags);
+       if (ret)
+               return ret;
+       ret = radix_tree_insert(trb_address_map,
+                       key, ring);
+       radix_tree_preload_end();
+       return ret;
+}
+
+static void xhci_remove_segment_mapping(struct radix_tree_root *trb_address_map,
+               struct xhci_segment *seg)
+{
+       unsigned long key;
+
+       key = (unsigned long)(seg->dma >> TRB_SEGMENT_SHIFT);
+       if (radix_tree_lookup(trb_address_map, key))
+               radix_tree_delete(trb_address_map, key);
+}
+
+static int xhci_update_stream_segment_mapping(
+               struct radix_tree_root *trb_address_map,
+               struct xhci_ring *ring,
+               struct xhci_segment *first_seg,
+               struct xhci_segment *last_seg,
+               gfp_t mem_flags)
+{
+       struct xhci_segment *seg;
+       struct xhci_segment *failed_seg;
+       int ret;
+
+       if (WARN_ON_ONCE(trb_address_map == NULL))
+               return 0;
+
+       seg = first_seg;
+       do {
+               ret = xhci_insert_segment_mapping(trb_address_map,
+                               ring, seg, mem_flags);
+               if (ret)
+                       goto remove_streams;
+               if (seg == last_seg)
+                       return 0;
+               seg = seg->next;
+       } while (seg != first_seg);
+
+       return 0;
+
+remove_streams:
+       failed_seg = seg;
+       seg = first_seg;
+       do {
+               xhci_remove_segment_mapping(trb_address_map, seg);
+               if (seg == failed_seg)
+                       return ret;
+               seg = seg->next;
+       } while (seg != first_seg);
+
+       return ret;
+}
+
+static void xhci_remove_stream_mapping(struct xhci_ring *ring)
+{
+       struct xhci_segment *seg;
+
+       if (WARN_ON_ONCE(ring->trb_address_map == NULL))
+               return;
+
+       seg = ring->first_seg;
+       do {
+               xhci_remove_segment_mapping(ring->trb_address_map, seg);
+               seg = seg->next;
+       } while (seg != ring->first_seg);
+}
+
+static int xhci_update_stream_mapping(struct xhci_ring *ring, gfp_t mem_flags)
+{
+       return xhci_update_stream_segment_mapping(ring->trb_address_map, ring,
+                       ring->first_seg, ring->last_seg, mem_flags);
+}
+
 /* XXX: Do we need the hcd structure in all these functions? */
 void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring)
 {
        if (!ring)
                return;
 
-       if (ring->first_seg)
+       if (ring->first_seg) {
+               if (ring->type == TYPE_STREAM)
+                       xhci_remove_stream_mapping(ring);
                xhci_free_segments_for_ring(xhci, ring->first_seg);
+       }
 
        kfree(ring);
 }
@@ -349,6 +475,21 @@ int xhci_ring_expansion(struct xhci_hcd *xhci, struct xhci_ring *ring,
        if (ret)
                return -ENOMEM;
 
+       if (ring->type == TYPE_STREAM)
+               ret = xhci_update_stream_segment_mapping(ring->trb_address_map,
+                                               ring, first, last, flags);
+       if (ret) {
+               struct xhci_segment *next;
+               do {
+                       next = first->next;
+                       xhci_segment_free(xhci, first);
+                       if (first == last)
+                               break;
+                       first = next;
+               } while (true);
+               return ret;
+       }
+
        xhci_link_rings(xhci, ring, first, last, num_segs);
        xhci_dbg_trace(xhci, trace_xhci_dbg_ring_expansion,
                        "ring expansion succeed, now has %d segments",
@@ -434,12 +575,12 @@ static void xhci_free_stream_ctx(struct xhci_hcd *xhci,
                struct xhci_stream_ctx *stream_ctx, dma_addr_t dma)
 {
        struct device *dev = xhci_to_hcd(xhci)->self.controller;
+       size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
 
-       if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
-               dma_free_coherent(dev,
-                               sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
+       if (size > MEDIUM_STREAM_ARRAY_SIZE)
+               dma_free_coherent(dev, size,
                                stream_ctx, dma);
-       else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
+       else if (size <= SMALL_STREAM_ARRAY_SIZE)
                return dma_pool_free(xhci->small_streams_pool,
                                stream_ctx, dma);
        else
@@ -462,12 +603,12 @@ static struct xhci_stream_ctx *xhci_alloc_stream_ctx(struct xhci_hcd *xhci,
                gfp_t mem_flags)
 {
        struct device *dev = xhci_to_hcd(xhci)->self.controller;
+       size_t size = sizeof(struct xhci_stream_ctx) * num_stream_ctxs;
 
-       if (num_stream_ctxs > MEDIUM_STREAM_ARRAY_SIZE)
-               return dma_alloc_coherent(dev,
-                               sizeof(struct xhci_stream_ctx)*num_stream_ctxs,
+       if (size > MEDIUM_STREAM_ARRAY_SIZE)
+               return dma_alloc_coherent(dev, size,
                                dma, mem_flags);
-       else if (num_stream_ctxs <= SMALL_STREAM_ARRAY_SIZE)
+       else if (size <= SMALL_STREAM_ARRAY_SIZE)
                return dma_pool_alloc(xhci->small_streams_pool,
                                mem_flags, dma);
        else
@@ -510,36 +651,6 @@ struct xhci_ring *xhci_stream_id_to_ring(
  * The number of stream contexts in the stream context array may be bigger than
  * the number of streams the driver wants to use.  This is because the number of
  * stream context array entries must be a power of two.
- *
- * We need a radix tree for mapping physical addresses of TRBs to which stream
- * ID they belong to.  We need to do this because the host controller won't tell
- * us which stream ring the TRB came from.  We could store the stream ID in an
- * event data TRB, but that doesn't help us for the cancellation case, since the
- * endpoint may stop before it reaches that event data TRB.
- *
- * The radix tree maps the upper portion of the TRB DMA address to a ring
- * segment that has the same upper portion of DMA addresses.  For example, say I
- * have segments of size 1KB, that are always 64-byte aligned.  A segment may
- * start at 0x10c91000 and end at 0x10c913f0.  If I use the upper 10 bits, the
- * key to the stream ID is 0x43244.  I can use the DMA address of the TRB to
- * pass the radix tree a key to get the right stream ID:
- *
- *     0x10c90fff >> 10 = 0x43243
- *     0x10c912c0 >> 10 = 0x43244
- *     0x10c91400 >> 10 = 0x43245
- *
- * Obviously, only those TRBs with DMA addresses that are within the segment
- * will make the radix tree return the stream ID for that ring.
- *
- * Caveats for the radix tree:
- *
- * The radix tree uses an unsigned long as a key pair.  On 32-bit systems, an
- * unsigned long will be 32-bits; on a 64-bit system an unsigned long will be
- * 64-bits.  Since we only request 32-bit DMA addresses, we can use that as the
- * key on 32-bit or 64-bit systems (it would also be fine if we asked for 64-bit
- * PCI DMA addresses on a 64-bit system).  There might be a problem on 32-bit
- * extended systems (where the DMA address can be bigger than 32-bits),
- * if we allow the PCI dma mask to be bigger than 32-bits.  So don't do that.
  */
 struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
                unsigned int num_stream_ctxs,
@@ -548,7 +659,6 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
        struct xhci_stream_info *stream_info;
        u32 cur_stream;
        struct xhci_ring *cur_ring;
-       unsigned long key;
        u64 addr;
        int ret;
 
@@ -603,6 +713,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
                if (!cur_ring)
                        goto cleanup_rings;
                cur_ring->stream_id = cur_stream;
+               cur_ring->trb_address_map = &stream_info->trb_address_map;
                /* Set deq ptr, cycle bit, and stream context type */
                addr = cur_ring->first_seg->dma |
                        SCT_FOR_CTX(SCT_PRI_TR) |
@@ -612,10 +723,7 @@ struct xhci_stream_info *xhci_alloc_stream_info(struct xhci_hcd *xhci,
                xhci_dbg(xhci, "Setting stream %d ring ptr to 0x%08llx\n",
                                cur_stream, (unsigned long long) addr);
 
-               key = (unsigned long)
-                       (cur_ring->first_seg->dma >> TRB_SEGMENT_SHIFT);
-               ret = radix_tree_insert(&stream_info->trb_address_map,
-                               key, cur_ring);
+               ret = xhci_update_stream_mapping(cur_ring, mem_flags);
                if (ret) {
                        xhci_ring_free(xhci, cur_ring);
                        stream_info->stream_rings[cur_stream] = NULL;
@@ -635,9 +743,6 @@ cleanup_rings:
        for (cur_stream = 1; cur_stream < num_streams; cur_stream++) {
                cur_ring = stream_info->stream_rings[cur_stream];
                if (cur_ring) {
-                       addr = cur_ring->first_seg->dma;
-                       radix_tree_delete(&stream_info->trb_address_map,
-                                       addr >> TRB_SEGMENT_SHIFT);
                        xhci_ring_free(xhci, cur_ring);
                        stream_info->stream_rings[cur_stream] = NULL;
                }
@@ -698,7 +803,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
 {
        int cur_stream;
        struct xhci_ring *cur_ring;
-       dma_addr_t addr;
 
        if (!stream_info)
                return;
@@ -707,9 +811,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
                        cur_stream++) {
                cur_ring = stream_info->stream_rings[cur_stream];
                if (cur_ring) {
-                       addr = cur_ring->first_seg->dma;
-                       radix_tree_delete(&stream_info->trb_address_map,
-                                       addr >> TRB_SEGMENT_SHIFT);
                        xhci_ring_free(xhci, cur_ring);
                        stream_info->stream_rings[cur_stream] = NULL;
                }
@@ -1711,7 +1812,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 
        if (xhci->lpm_command)
                xhci_free_command(xhci, xhci->lpm_command);
-       xhci->cmd_ring_reserved_trbs = 0;
        if (xhci->cmd_ring)
                xhci_ring_free(xhci, xhci->cmd_ring);
        xhci->cmd_ring = NULL;
@@ -1776,6 +1876,7 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
        }
 
 no_bw:
+       xhci->cmd_ring_reserved_trbs = 0;
        xhci->num_usb2_ports = 0;
        xhci->num_usb3_ports = 0;
        xhci->num_active_eps = 0;
@@ -2274,11 +2375,12 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
        /*
         * Initialize the ring segment pool.  The ring must be a contiguous
         * structure comprised of TRBs.  The TRBs must be 16 byte aligned,
-        * however, the command ring segment needs 64-byte aligned segments,
-        * so we pick the greater alignment need.
+        * however, the command ring segment needs 64-byte aligned segments
+        * and our use of dma addresses in the trb_address_map radix tree needs
+        * TRB_SEGMENT_SIZE alignment, so we pick the greater alignment need.
         */
        xhci->segment_pool = dma_pool_create("xHCI ring segments", dev,
-                       TRB_SEGMENT_SIZE, 64, xhci->page_size);
+                       TRB_SEGMENT_SIZE, TRB_SEGMENT_SIZE, xhci->page_size);
 
        /* See Table 46 and Note on Figure 55 */
        xhci->device_pool = dma_pool_create("xHCI input/output contexts", dev,
index 04f986d9234f6de450888fb5178d14e3ea30dda0..47390e369cd402f776c20bbb35b4c37223f04057 100644 (file)
@@ -190,6 +190,10 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
        struct usb_hcd *hcd;
 
        driver = (struct hc_driver *)id->driver_data;
+
+       /* Prevent runtime suspending between USB-2 and USB-3 initialization */
+       pm_runtime_get_noresume(&dev->dev);
+
        /* Register the USB 2.0 roothub.
         * FIXME: USB core must know to register the USB 2.0 roothub first.
         * This is sort of silly, because we could just set the HCD driver flags
@@ -199,7 +203,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
        retval = usb_hcd_pci_probe(dev, id);
 
        if (retval)
-               return retval;
+               goto put_runtime_pm;
 
        /* USB 2.0 roothub is stored in the PCI device now. */
        hcd = dev_get_drvdata(&dev->dev);
@@ -222,11 +226,11 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                goto put_usb3_hcd;
        /* Roothub already marked as USB 3.0 speed */
 
-       /* We know the LPM timeout algorithms for this host, let the USB core
-        * enable and disable LPM for devices under the USB 3.0 roothub.
-        */
-       if (xhci->quirks & XHCI_LPM_SUPPORT)
-               hcd_to_bus(xhci->shared_hcd)->root_hub->lpm_capable = 1;
+       if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+               xhci->shared_hcd->can_do_streams = 1;
+
+       /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */
+       pm_runtime_put_noidle(&dev->dev);
 
        return 0;
 
@@ -234,6 +238,8 @@ put_usb3_hcd:
        usb_put_hcd(xhci->shared_hcd);
 dealloc_usb2_hcd:
        usb_hcd_pci_remove(dev);
+put_runtime_pm:
+       pm_runtime_put_noidle(&dev->dev);
        return retval;
 }
 
index 8abda5c73ca1e846aaff80a2e6096dff73d04853..151901ce1ba933598b09cd8d01a077a7b66c1cc9 100644 (file)
@@ -158,6 +158,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
         */
        *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci;
 
+       if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
+               xhci->shared_hcd->can_do_streams = 1;
+
        ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
        if (ret)
                goto put_usb3_hcd;
@@ -226,6 +229,7 @@ static const struct dev_pm_ops xhci_plat_pm_ops = {
 
 #ifdef CONFIG_OF
 static const struct of_device_id usb_xhci_of_match[] = {
+       { .compatible = "generic-xhci" },
        { .compatible = "xhci-platform" },
        { },
 };
index 0ed64eb68e48e226c0176c6e8c9c3f5247111c73..5f926bea5ab1d87a54054ad68555ea31bd62cb63 100644 (file)
@@ -546,9 +546,9 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
                struct xhci_dequeue_state *state)
 {
        struct xhci_virt_device *dev = xhci->devs[slot_id];
+       struct xhci_virt_ep *ep = &dev->eps[ep_index];
        struct xhci_ring *ep_ring;
        struct xhci_generic_trb *trb;
-       struct xhci_ep_ctx *ep_ctx;
        dma_addr_t addr;
 
        ep_ring = xhci_triad_to_transfer_ring(xhci, slot_id,
@@ -573,8 +573,16 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
        /* Dig out the cycle state saved by the xHC during the stop ep cmd */
        xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
                        "Finding endpoint context");
-       ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
-       state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq);
+       /* 4.6.9 the css flag is written to the stream context for streams */
+       if (ep->ep_state & EP_HAS_STREAMS) {
+               struct xhci_stream_ctx *ctx =
+                       &ep->stream_info->stream_ctx_array[stream_id];
+               state->new_cycle_state = 0x1 & le64_to_cpu(ctx->stream_ring);
+       } else {
+               struct xhci_ep_ctx *ep_ctx
+                       = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
+               state->new_cycle_state = 0x1 & le64_to_cpu(ep_ctx->deq);
+       }
 
        state->new_deq_ptr = cur_td->last_trb;
        xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
@@ -892,6 +900,57 @@ remove_finished_td:
        /* Return to the event handler with xhci->lock re-acquired */
 }
 
+static void xhci_kill_ring_urbs(struct xhci_hcd *xhci, struct xhci_ring *ring)
+{
+       struct xhci_td *cur_td;
+
+       while (!list_empty(&ring->td_list)) {
+               cur_td = list_first_entry(&ring->td_list,
+                               struct xhci_td, td_list);
+               list_del_init(&cur_td->td_list);
+               if (!list_empty(&cur_td->cancelled_td_list))
+                       list_del_init(&cur_td->cancelled_td_list);
+               xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+       }
+}
+
+static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci,
+               int slot_id, int ep_index)
+{
+       struct xhci_td *cur_td;
+       struct xhci_virt_ep *ep;
+       struct xhci_ring *ring;
+
+       ep = &xhci->devs[slot_id]->eps[ep_index];
+       if ((ep->ep_state & EP_HAS_STREAMS) ||
+                       (ep->ep_state & EP_GETTING_NO_STREAMS)) {
+               int stream_id;
+
+               for (stream_id = 0; stream_id < ep->stream_info->num_streams;
+                               stream_id++) {
+                       xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+                                       "Killing URBs for slot ID %u, ep index %u, stream %u",
+                                       slot_id, ep_index, stream_id + 1);
+                       xhci_kill_ring_urbs(xhci,
+                                       ep->stream_info->stream_rings[stream_id]);
+               }
+       } else {
+               ring = ep->ring;
+               if (!ring)
+                       return;
+               xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
+                               "Killing URBs for slot ID %u, ep index %u",
+                               slot_id, ep_index);
+               xhci_kill_ring_urbs(xhci, ring);
+       }
+       while (!list_empty(&ep->cancelled_td_list)) {
+               cur_td = list_first_entry(&ep->cancelled_td_list,
+                               struct xhci_td, cancelled_td_list);
+               list_del_init(&cur_td->cancelled_td_list);
+               xhci_giveback_urb_in_irq(xhci, cur_td, -ESHUTDOWN);
+       }
+}
+
 /* Watchdog timer function for when a stop endpoint command fails to complete.
  * In this case, we assume the host controller is broken or dying or dead.  The
  * host may still be completing some other events, so we have to be careful to
@@ -915,9 +974,6 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
 {
        struct xhci_hcd *xhci;
        struct xhci_virt_ep *ep;
-       struct xhci_virt_ep *temp_ep;
-       struct xhci_ring *ring;
-       struct xhci_td *cur_td;
        int ret, i, j;
        unsigned long flags;
 
@@ -974,34 +1030,8 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
        for (i = 0; i < MAX_HC_SLOTS; i++) {
                if (!xhci->devs[i])
                        continue;
-               for (j = 0; j < 31; j++) {
-                       temp_ep = &xhci->devs[i]->eps[j];
-                       ring = temp_ep->ring;
-                       if (!ring)
-                               continue;
-                       xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-                                       "Killing URBs for slot ID %u, "
-                                       "ep index %u", i, j);
-                       while (!list_empty(&ring->td_list)) {
-                               cur_td = list_first_entry(&ring->td_list,
-                                               struct xhci_td,
-                                               td_list);
-                               list_del_init(&cur_td->td_list);
-                               if (!list_empty(&cur_td->cancelled_td_list))
-                                       list_del_init(&cur_td->cancelled_td_list);
-                               xhci_giveback_urb_in_irq(xhci, cur_td,
-                                               -ESHUTDOWN);
-                       }
-                       while (!list_empty(&temp_ep->cancelled_td_list)) {
-                               cur_td = list_first_entry(
-                                               &temp_ep->cancelled_td_list,
-                                               struct xhci_td,
-                                               cancelled_td_list);
-                               list_del_init(&cur_td->cancelled_td_list);
-                               xhci_giveback_urb_in_irq(xhci, cur_td,
-                                               -ESHUTDOWN);
-                       }
-               }
+               for (j = 0; j < 31; j++)
+                       xhci_kill_endpoint_urbs(xhci, i, j);
        }
        spin_unlock_irqrestore(&xhci->lock, flags);
        xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
@@ -1073,17 +1103,18 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
        unsigned int stream_id;
        struct xhci_ring *ep_ring;
        struct xhci_virt_device *dev;
+       struct xhci_virt_ep *ep;
        struct xhci_ep_ctx *ep_ctx;
        struct xhci_slot_ctx *slot_ctx;
 
        ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
        stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2]));
        dev = xhci->devs[slot_id];
+       ep = &dev->eps[ep_index];
 
        ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id);
        if (!ep_ring) {
-               xhci_warn(xhci, "WARN Set TR deq ptr command for "
-                               "freed stream ID %u\n",
+               xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n",
                                stream_id);
                /* XXX: Harmless??? */
                dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING;
@@ -1099,12 +1130,10 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
 
                switch (cmd_comp_code) {
                case COMP_TRB_ERR:
-                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because "
-                                       "of stream ID configuration\n");
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because of stream ID configuration\n");
                        break;
                case COMP_CTX_STATE:
-                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due "
-                                       "to incorrect slot or ep state.\n");
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed due to incorrect slot or ep state.\n");
                        ep_state = le32_to_cpu(ep_ctx->ep_info);
                        ep_state &= EP_STATE_MASK;
                        slot_state = le32_to_cpu(slot_ctx->dev_state);
@@ -1114,13 +1143,12 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
                                        slot_state, ep_state);
                        break;
                case COMP_EBADSLT:
-                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because "
-                                       "slot %u was not enabled.\n", slot_id);
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd failed because slot %u was not enabled.\n",
+                                       slot_id);
                        break;
                default:
-                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown "
-                                       "completion code of %u.\n",
-                                 cmd_comp_code);
+                       xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown completion code of %u.\n",
+                                       cmd_comp_code);
                        break;
                }
                /* OK what do we do now?  The endpoint state is hosed, and we
@@ -1130,23 +1158,28 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
                 * cancelling URBs, which might not be an error...
                 */
        } else {
+               u64 deq;
+               /* 4.6.10 deq ptr is written to the stream ctx for streams */
+               if (ep->ep_state & EP_HAS_STREAMS) {
+                       struct xhci_stream_ctx *ctx =
+                               &ep->stream_info->stream_ctx_array[stream_id];
+                       deq = le64_to_cpu(ctx->stream_ring) & SCTX_DEQ_MASK;
+               } else {
+                       deq = le64_to_cpu(ep_ctx->deq) & ~EP_CTX_CYCLE_MASK;
+               }
                xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-                       "Successful Set TR Deq Ptr cmd, deq = @%08llx",
-                        le64_to_cpu(ep_ctx->deq));
-               if (xhci_trb_virt_to_dma(dev->eps[ep_index].queued_deq_seg,
-                                        dev->eps[ep_index].queued_deq_ptr) ==
-                   (le64_to_cpu(ep_ctx->deq) & ~(EP_CTX_CYCLE_MASK))) {
+                       "Successful Set TR Deq Ptr cmd, deq = @%08llx", deq);
+               if (xhci_trb_virt_to_dma(ep->queued_deq_seg,
+                                        ep->queued_deq_ptr) == deq) {
                        /* Update the ring's dequeue segment and dequeue pointer
                         * to reflect the new position.
                         */
                        update_ring_for_set_deq_completion(xhci, dev,
                                ep_ring, ep_index);
                } else {
-                       xhci_warn(xhci, "Mismatch between completed Set TR Deq "
-                                       "Ptr command & xHCI internal state.\n");
+                       xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n");
                        xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n",
-                                       dev->eps[ep_index].queued_deq_seg,
-                                       dev->eps[ep_index].queued_deq_ptr);
+                                 ep->queued_deq_seg, ep->queued_deq_ptr);
                }
        }
 
@@ -4070,6 +4103,7 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
        u32 trb_slot_id = SLOT_ID_FOR_TRB(slot_id);
        u32 trb_ep_index = EP_ID_FOR_TRB(ep_index);
        u32 trb_stream_id = STREAM_ID_FOR_TRB(stream_id);
+       u32 trb_sct = 0;
        u32 type = TRB_TYPE(TRB_SET_DEQ);
        struct xhci_virt_ep *ep;
 
@@ -4088,7 +4122,9 @@ static int queue_set_tr_deq(struct xhci_hcd *xhci, int slot_id,
        }
        ep->queued_deq_seg = deq_seg;
        ep->queued_deq_ptr = deq_ptr;
-       return queue_command(xhci, lower_32_bits(addr) | cycle_state,
+       if (stream_id)
+               trb_sct = SCT_FOR_TRB(SCT_PRI_TR);
+       return queue_command(xhci, lower_32_bits(addr) | trb_sct | cycle_state,
                        upper_32_bits(addr), trb_stream_id,
                        trb_slot_id | trb_ep_index | type, false);
 }
index 924a6ccdb622777a6fc71b21dfb6a0ee0792f536..8fe4e124ddd49f17fb3ac6d2088cedc15758d7b2 100644 (file)
@@ -390,6 +390,10 @@ static int xhci_try_enable_msi(struct usb_hcd *hcd)
        }
 
  legacy_irq:
+       if (!strlen(hcd->irq_descr))
+               snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
+                        hcd->driver->description, hcd->self.busnum);
+
        /* fall back to legacy interrupt*/
        ret = request_irq(pdev->irq, &usb_hcd_irq, IRQF_SHARED,
                        hcd->irq_descr, hcd);
@@ -2678,6 +2682,20 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
        return ret;
 }
 
+static void xhci_check_bw_drop_ep_streams(struct xhci_hcd *xhci,
+       struct xhci_virt_device *vdev, int i)
+{
+       struct xhci_virt_ep *ep = &vdev->eps[i];
+
+       if (ep->ep_state & EP_HAS_STREAMS) {
+               xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on set_interface, freeing streams.\n",
+                               xhci_get_endpoint_address(i));
+               xhci_free_stream_info(xhci, ep->stream_info);
+               ep->stream_info = NULL;
+               ep->ep_state &= ~EP_HAS_STREAMS;
+       }
+}
+
 /* Called after one or more calls to xhci_add_endpoint() or
  * xhci_drop_endpoint().  If this call fails, the USB core is expected
  * to call xhci_reset_bandwidth().
@@ -2742,8 +2760,10 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
        /* Free any rings that were dropped, but not changed. */
        for (i = 1; i < 31; ++i) {
                if ((le32_to_cpu(ctrl_ctx->drop_flags) & (1 << (i + 1))) &&
-                   !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1))))
+                   !(le32_to_cpu(ctrl_ctx->add_flags) & (1 << (i + 1)))) {
                        xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+                       xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
+               }
        }
        xhci_zero_in_ctx(xhci, virt_dev);
        /*
@@ -2759,6 +2779,7 @@ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev)
                if (virt_dev->eps[i].ring) {
                        xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
                }
+               xhci_check_bw_drop_ep_streams(xhci, virt_dev, i);
                virt_dev->eps[i].ring = virt_dev->eps[i].new_ring;
                virt_dev->eps[i].new_ring = NULL;
        }
@@ -2954,7 +2975,7 @@ static int xhci_check_streams_endpoint(struct xhci_hcd *xhci,
        ret = xhci_check_args(xhci_to_hcd(xhci), udev, ep, 1, true, __func__);
        if (ret <= 0)
                return -EINVAL;
-       if (ep->ss_ep_comp.bmAttributes == 0) {
+       if (usb_ss_max_streams(&ep->ss_ep_comp) == 0) {
                xhci_warn(xhci, "WARN: SuperSpeed Endpoint Companion"
                                " descriptor for ep 0x%x does not support streams\n",
                                ep->desc.bEndpointAddress);
@@ -3121,6 +3142,12 @@ int xhci_alloc_streams(struct usb_hcd *hcd, struct usb_device *udev,
        xhci_dbg(xhci, "Driver wants %u stream IDs (including stream 0).\n",
                        num_streams);
 
+       /* MaxPSASize value 0 (2 streams) means streams are not supported */
+       if (HCC_MAX_PSA(xhci->hcc_params) < 4) {
+               xhci_dbg(xhci, "xHCI controller does not support streams.\n");
+               return -ENOSYS;
+       }
+
        config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
        if (!config_cmd) {
                xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
@@ -3519,6 +3546,8 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
                struct xhci_virt_ep *ep = &virt_dev->eps[i];
 
                if (ep->ep_state & EP_HAS_STREAMS) {
+                       xhci_warn(xhci, "WARN: endpoint 0x%02x has streams on device reset, freeing streams.\n",
+                                       xhci_get_endpoint_address(i));
                        xhci_free_stream_info(xhci, ep->stream_info);
                        ep->stream_info = NULL;
                        ep->ep_state &= ~EP_HAS_STREAMS;
index 58ed9d088e635c4e7d7ebeaa027da6f126f848eb..d280e9213d08614002030573afcb2b93a5bbe9c8 100644 (file)
@@ -703,6 +703,7 @@ struct xhci_ep_ctx {
 
 /* deq bitmasks */
 #define EP_CTX_CYCLE_MASK              (1 << 0)
+#define SCTX_DEQ_MASK                  (~0xfL)
 
 
 /**
@@ -1118,9 +1119,10 @@ enum xhci_setup_dev {
 #define TRB_TO_SUSPEND_PORT(p)         (((p) & (1 << 23)) >> 23)
 #define LAST_EP_INDEX                  30
 
-/* Set TR Dequeue Pointer command TRB fields */
+/* Set TR Dequeue Pointer command TRB fields, 6.4.3.9 */
 #define TRB_TO_STREAM_ID(p)            ((((p) & (0xffff << 16)) >> 16))
 #define STREAM_ID_FOR_TRB(p)           ((((p)) & 0xffff) << 16)
+#define SCT_FOR_TRB(p)                 (((p) << 1) & 0x7)
 
 
 /* Port Status Change Event TRB fields */
@@ -1341,6 +1343,7 @@ struct xhci_ring {
        unsigned int            num_trbs_free_temp;
        enum xhci_ring_type     type;
        bool                    last_td_was_short;
+       struct radix_tree_root  *trb_address_map;
 };
 
 struct xhci_erst_entry {
index ba5f70f92888774c5ef900d44c6f8ad941e6b4e9..1bca274dc3b5273029799908f9499b5b4e61091b 100644 (file)
@@ -128,7 +128,6 @@ config USB_IDMOUSE
 
 config USB_FTDI_ELAN
        tristate "Elan PCMCIA CardBus Adapter USB Client"
-       default M
        help
          ELAN's Uxxx series of adapters are USB to PCMCIA CardBus adapters.
          Currently only the U132 adapter is available.
index de98906f786d08ebac625b3cc8213de5d79cf68e..06b5d77cd9ade2aba61d5e79d308ae6afdc7e45b 100644 (file)
@@ -2123,8 +2123,8 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
        u8 tmp8, tmp82, ramtype;
        int bw = 0;
        char *ramtypetext1 = NULL;
-       const char *ramtypetext2[] = {  "SDR SDRAM", "SDR SGRAM",
-                                       "DDR SDRAM", "DDR SGRAM" };
+       static const char ram_datarate[4] = {'S', 'S', 'D', 'D'};
+       static const char ram_dynamictype[4] = {'D', 'G', 'D', 'G'};
        static const int busSDR[4]  = {64, 64, 128, 128};
        static const int busDDR[4]  = {32, 32,  64,  64};
        static const int busDDRA[4] = {64+32, 64+32 , (64+32)*2, (64+32)*2};
@@ -2156,8 +2156,10 @@ sisusb_get_ramconfig(struct sisusb_usb_data *sisusb)
                break;
        }
 
-       dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %s, bus width %d\n", (sisusb->vramsize >> 20), ramtypetext1,
-                       ramtypetext2[ramtype], bw);
+
+       dev_info(&sisusb->sisusb_dev->dev, "%dMB %s %cDR S%cRAM, bus width %d\n",
+                sisusb->vramsize >> 20, ramtypetext1,
+                ram_datarate[ramtype], ram_dynamictype[ramtype], bw);
 }
 
 static int
index 78eb4ff33269ae875d75400e250ea855107f25ba..bdef0d6eb91d059b5b47325902ee29298a426221 100644 (file)
 enum led_type {
        DELCOM_VISUAL_SIGNAL_INDICATOR,
        DREAM_CHEEKY_WEBMAIL_NOTIFIER,
+       RISO_KAGAKU_LED
 };
 
+/* the Webmail LED made by RISO KAGAKU CORP. decodes a color index
+   internally, we want to keep the red+green+blue sysfs api, so we decode
+   from 1-bit RGB to the riso kagaku color index according to this table... */
+
+static unsigned const char riso_kagaku_tbl[] = {
+/* R+2G+4B -> riso kagaku color index */
+       [0] = 0, /* black   */
+       [1] = 2, /* red     */
+       [2] = 1, /* green   */
+       [3] = 5, /* yellow  */
+       [4] = 3, /* blue    */
+       [5] = 6, /* magenta */
+       [6] = 4, /* cyan    */
+       [7] = 7  /* white   */
+};
+
+#define RISO_KAGAKU_IX(r,g,b) riso_kagaku_tbl[((r)?1:0)+((g)?2:0)+((b)?4:0)]
+
 /* table of devices that work with this driver */
 static const struct usb_device_id id_table[] = {
        { USB_DEVICE(0x0fc5, 0x1223),
@@ -32,6 +51,8 @@ static const struct usb_device_id id_table[] = {
                        .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
        { USB_DEVICE(0x1d34, 0x000a),
                        .driver_info = DREAM_CHEEKY_WEBMAIL_NOTIFIER },
+       { USB_DEVICE(0x1294, 0x1320),
+                       .driver_info = RISO_KAGAKU_LED },
        { },
 };
 MODULE_DEVICE_TABLE(usb, id_table);
@@ -48,6 +69,7 @@ static void change_color(struct usb_led *led)
 {
        int retval = 0;
        unsigned char *buffer;
+       int actlength;
 
        buffer = kmalloc(8, GFP_KERNEL);
        if (!buffer) {
@@ -104,6 +126,18 @@ static void change_color(struct usb_led *led)
                                        2000);
                break;
 
+       case RISO_KAGAKU_LED:
+               buffer[0] = RISO_KAGAKU_IX(led->red, led->green, led->blue);
+               buffer[1] = 0;
+               buffer[2] = 0;
+               buffer[3] = 0;
+               buffer[4] = 0;
+
+               retval = usb_interrupt_msg(led->udev,
+                       usb_sndctrlpipe(led->udev, 2),
+                       buffer, 5, &actlength, 1000 /*ms timeout*/);
+               break;
+
        default:
                dev_err(&led->udev->dev, "unknown device type %d\n", led->type);
        }
index 688dc8bb192dce102caa1f481c3043bd03783019..8b789792f6fa4601c01e4b0e7dee707bee6912f7 100644 (file)
@@ -43,6 +43,7 @@ config USB_MUSB_HOST
 config USB_MUSB_GADGET
        bool "Gadget only mode"
        depends on USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC
+       depends on HAS_DMA
        help
          Select this when you want to use MUSB in gadget mode only,
          thereby the host feature will be regressed.
@@ -50,6 +51,7 @@ config USB_MUSB_GADGET
 config USB_MUSB_DUAL_ROLE
        bool "Dual Role mode"
        depends on ((USB=y || USB=USB_MUSB_HDRC) && (USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC))
+       depends on HAS_DMA
        help
          This is the default mode of working of MUSB controller where
          both host and gadget features are enabled.
index 239ad0b1ceb6d15ddff5fa3741ca97796e70e067..07576907e2c615a40c9e8fd81c4d9a70dcad5265 100644 (file)
@@ -438,7 +438,6 @@ void musb_hnp_stop(struct musb *musb)
 static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                u8 devctl)
 {
-       struct usb_otg *otg = musb->xceiv->otg;
        irqreturn_t handled = IRQ_NONE;
 
        dev_dbg(musb->controller, "<== DevCtl=%02x, int_usb=0x%x\n", devctl,
@@ -656,7 +655,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                break;
                case OTG_STATE_B_PERIPHERAL:
                        musb_g_suspend(musb);
-                       musb->is_active = otg->gadget->b_hnp_enable;
+                       musb->is_active = musb->g.b_hnp_enable;
                        if (musb->is_active) {
                                musb->xceiv->state = OTG_STATE_B_WAIT_ACON;
                                dev_dbg(musb->controller, "HNP: Setting timer for b_ase0_brst\n");
@@ -672,7 +671,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                        break;
                case OTG_STATE_A_HOST:
                        musb->xceiv->state = OTG_STATE_A_SUSPEND;
-                       musb->is_active = otg->host->b_hnp_enable;
+                       musb->is_active = musb->hcd->self.b_hnp_enable;
                        break;
                case OTG_STATE_B_HOST:
                        /* Transition to B_PERIPHERAL, see 6.8.2.6 p 44 */
index f88929609baca527e19044fb3d60fe581fa58893..7b8bbf53127e114c5615fa7c90ae75a0485f6779 100644 (file)
@@ -39,6 +39,7 @@ struct cppi41_dma_channel {
        u32 transferred;
        u32 packet_sz;
        struct list_head tx_check;
+       struct work_struct dma_completion;
 };
 
 #define MUSB_DMA_NUM_CHANNELS 15
@@ -112,6 +113,18 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
        return true;
 }
 
+static bool is_isoc(struct musb_hw_ep *hw_ep, bool in)
+{
+       if (in && hw_ep->in_qh) {
+               if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC)
+                       return true;
+       } else if (hw_ep->out_qh) {
+               if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC)
+                       return true;
+       }
+       return false;
+}
+
 static void cppi41_dma_callback(void *private_data);
 
 static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
@@ -119,7 +132,8 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
        struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
        struct musb *musb = hw_ep->musb;
 
-       if (!cppi41_channel->prog_len) {
+       if (!cppi41_channel->prog_len ||
+           (cppi41_channel->channel.status == MUSB_DMA_STATUS_FREE)) {
 
                /* done, complete */
                cppi41_channel->channel.actual_len =
@@ -165,6 +179,32 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
        }
 }
 
+static void cppi_trans_done_work(struct work_struct *work)
+{
+       unsigned long flags;
+       struct cppi41_dma_channel *cppi41_channel =
+               container_of(work, struct cppi41_dma_channel, dma_completion);
+       struct cppi41_dma_controller *controller = cppi41_channel->controller;
+       struct musb *musb = controller->musb;
+       struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
+       bool empty;
+
+       if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) {
+               spin_lock_irqsave(&musb->lock, flags);
+               cppi41_trans_done(cppi41_channel);
+               spin_unlock_irqrestore(&musb->lock, flags);
+       } else {
+               empty = musb_is_tx_fifo_empty(hw_ep);
+               if (empty) {
+                       spin_lock_irqsave(&musb->lock, flags);
+                       cppi41_trans_done(cppi41_channel);
+                       spin_unlock_irqrestore(&musb->lock, flags);
+               } else {
+                       schedule_work(&cppi41_channel->dma_completion);
+               }
+       }
+}
+
 static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
 {
        struct cppi41_dma_controller *controller;
@@ -228,6 +268,14 @@ static void cppi41_dma_callback(void *private_data)
                        transferred < cppi41_channel->packet_sz)
                cppi41_channel->prog_len = 0;
 
+       if (!cppi41_channel->is_tx) {
+               if (is_isoc(hw_ep, 1))
+                       schedule_work(&cppi41_channel->dma_completion);
+               else
+                       cppi41_trans_done(cppi41_channel);
+               goto out;
+       }
+
        empty = musb_is_tx_fifo_empty(hw_ep);
        if (empty) {
                cppi41_trans_done(cppi41_channel);
@@ -264,6 +312,10 @@ static void cppi41_dma_callback(void *private_data)
                                goto out;
                        }
                }
+               if (is_isoc(hw_ep, 0)) {
+                       schedule_work(&cppi41_channel->dma_completion);
+                       goto out;
+               }
                list_add_tail(&cppi41_channel->tx_check,
                                &controller->early_tx_list);
                if (!hrtimer_active(&controller->early_tx)) {
@@ -448,12 +500,25 @@ static int cppi41_dma_channel_program(struct dma_channel *channel,
                                dma_addr_t dma_addr, u32 len)
 {
        int ret;
+       struct cppi41_dma_channel *cppi41_channel = channel->private_data;
+       int hb_mult = 0;
 
        BUG_ON(channel->status == MUSB_DMA_STATUS_UNKNOWN ||
                channel->status == MUSB_DMA_STATUS_BUSY);
 
+       if (is_host_active(cppi41_channel->controller->musb)) {
+               if (cppi41_channel->is_tx)
+                       hb_mult = cppi41_channel->hw_ep->out_qh->hb_mult;
+               else
+                       hb_mult = cppi41_channel->hw_ep->in_qh->hb_mult;
+       }
+
        channel->status = MUSB_DMA_STATUS_BUSY;
        channel->actual_len = 0;
+
+       if (hb_mult)
+               packet_sz = hb_mult * (packet_sz & 0x7FF);
+
        ret = cppi41_configure_channel(channel, packet_sz, mode, dma_addr, len);
        if (!ret)
                channel->status = MUSB_DMA_STATUS_FREE;
@@ -607,6 +672,8 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
                cppi41_channel->port_num = port;
                cppi41_channel->is_tx = is_tx;
                INIT_LIST_HEAD(&cppi41_channel->tx_check);
+               INIT_WORK(&cppi41_channel->dma_completion,
+                         cppi_trans_done_work);
 
                musb_dma = &cppi41_channel->channel;
                musb_dma->private_data = cppi41_channel;
index 7a109eae9b9acf3cd652259865f7c013376f7459..3372ded5def79853e75ac79ecc66b74d73c63542 100644 (file)
@@ -45,6 +45,8 @@
 #include <linux/of_irq.h>
 #include <linux/usb/of.h>
 
+#include <linux/debugfs.h>
+
 #include "musb_core.h"
 
 static const struct of_device_id musb_dsps_of_match[];
@@ -136,6 +138,26 @@ struct dsps_glue {
        unsigned long last_timer;    /* last timer data for each instance */
 
        struct dsps_context context;
+       struct debugfs_regset32 regset;
+       struct dentry *dbgfs_root;
+};
+
+static const struct debugfs_reg32 dsps_musb_regs[] = {
+       { "revision",           0x00 },
+       { "control",            0x14 },
+       { "status",             0x18 },
+       { "eoi",                0x24 },
+       { "intr0_stat",         0x30 },
+       { "intr1_stat",         0x34 },
+       { "intr0_set",          0x38 },
+       { "intr1_set",          0x3c },
+       { "txmode",             0x70 },
+       { "rxmode",             0x74 },
+       { "autoreq",            0xd0 },
+       { "srpfixtime",         0xd4 },
+       { "tdown",              0xd8 },
+       { "phy_utmi",           0xe0 },
+       { "mode",               0xe8 },
 };
 
 static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
@@ -368,6 +390,30 @@ out:
        return ret;
 }
 
+static int dsps_musb_dbg_init(struct musb *musb, struct dsps_glue *glue)
+{
+       struct dentry *root;
+       struct dentry *file;
+       char buf[128];
+
+       sprintf(buf, "%s.dsps", dev_name(musb->controller));
+       root = debugfs_create_dir(buf, NULL);
+       if (!root)
+               return -ENOMEM;
+       glue->dbgfs_root = root;
+
+       glue->regset.regs = dsps_musb_regs;
+       glue->regset.nregs = ARRAY_SIZE(dsps_musb_regs);
+       glue->regset.base = musb->ctrl_base;
+
+       file = debugfs_create_regset32("regdump", S_IRUGO, root, &glue->regset);
+       if (!file) {
+               debugfs_remove_recursive(root);
+               return -ENOMEM;
+       }
+       return 0;
+}
+
 static int dsps_musb_init(struct musb *musb)
 {
        struct device *dev = musb->controller;
@@ -377,6 +423,7 @@ static int dsps_musb_init(struct musb *musb)
        void __iomem *reg_base;
        struct resource *r;
        u32 rev, val;
+       int ret;
 
        r = platform_get_resource_byname(parent, IORESOURCE_MEM, "control");
        if (!r)
@@ -410,6 +457,10 @@ static int dsps_musb_init(struct musb *musb)
        val &= ~(1 << wrp->otg_disable);
        dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
 
+       ret = dsps_musb_dbg_init(musb, glue);
+       if (ret)
+               return ret;
+
        return 0;
 }
 
@@ -616,7 +667,7 @@ static int dsps_probe(struct platform_device *pdev)
        wrp = match->data;
 
        /* allocate glue */
-       glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+       glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
        if (!glue) {
                dev_err(&pdev->dev, "unable to allocate glue memory\n");
                return -ENOMEM;
@@ -644,7 +695,6 @@ err3:
        pm_runtime_put(&pdev->dev);
 err2:
        pm_runtime_disable(&pdev->dev);
-       kfree(glue);
        return ret;
 }
 
@@ -657,7 +707,9 @@ static int dsps_remove(struct platform_device *pdev)
        /* disable usbss clocks */
        pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
-       kfree(glue);
+
+       debugfs_remove_recursive(glue->dbgfs_root);
+
        return 0;
 }
 
index abb38c3833ef46a1f8041c8a5f95fa96138b1796..eb06291a40c8776153782edbbee0784231946e60 100644 (file)
@@ -1694,7 +1694,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                        | MUSB_RXCSR_RXPKTRDY);
                musb_writew(hw_ep->regs, MUSB_RXCSR, val);
 
-#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
+       defined(CONFIG_USB_TI_CPPI41_DMA)
                if (usb_pipeisoc(pipe)) {
                        struct usb_iso_packet_descriptor *d;
 
@@ -1707,10 +1708,30 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                        if (d->status != -EILSEQ && d->status != -EOVERFLOW)
                                d->status = 0;
 
-                       if (++qh->iso_idx >= urb->number_of_packets)
+                       if (++qh->iso_idx >= urb->number_of_packets) {
                                done = true;
-                       else
+                       } else {
+#if defined(CONFIG_USB_TI_CPPI41_DMA)
+                               struct dma_controller   *c;
+                               dma_addr_t *buf;
+                               u32 length, ret;
+
+                               c = musb->dma_controller;
+                               buf = (void *)
+                                       urb->iso_frame_desc[qh->iso_idx].offset
+                                       + (u32)urb->transfer_dma;
+
+                               length =
+                                       urb->iso_frame_desc[qh->iso_idx].length;
+
+                               val |= MUSB_RXCSR_DMAENAB;
+                               musb_writew(hw_ep->regs, MUSB_RXCSR, val);
+
+                               ret = c->channel_program(dma, qh->maxpacket,
+                                               0, (u32) buf, length);
+#endif
                                done = false;
+                       }
 
                } else  {
                /* done if urb buffer is full or short packet is recd */
@@ -1750,7 +1771,8 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                }
 
                /* we are expecting IN packets */
-#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA)
+#if defined(CONFIG_USB_INVENTRA_DMA) || defined(CONFIG_USB_UX500_DMA) || \
+       defined(CONFIG_USB_TI_CPPI41_DMA)
                if (dma) {
                        struct dma_controller   *c;
                        u16                     rx_count;
index 8aa59a2c5eb2485c823244012cc738ce5df7d127..d341c149a2f90c1372201b9a78aff8a89d4f3e6f 100644 (file)
@@ -37,7 +37,7 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/usb/musb-omap.h>
-#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/omap_control_phy.h>
 #include <linux/of_platform.h>
 
 #include "musb_core.h"
index 7d1451d5bbea961204376b23126e9545ed3509f8..416e0c8cf6ff165b2aec8d8d602852a359ec22ff 100644 (file)
@@ -75,27 +75,6 @@ config NOP_USB_XCEIV
          built-in with usb ip or which are autonomous and doesn't require any
          phy programming such as ISP1x04 etc.
 
-config OMAP_CONTROL_USB
-       tristate "OMAP CONTROL USB Driver"
-       depends on ARCH_OMAP2PLUS || COMPILE_TEST
-       help
-         Enable this to add support for the USB part present in the control
-         module. This driver has API to power on the USB2 PHY and to write to
-         the mailbox. The mailbox is present only in omap4 and the register to
-         power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
-         additional register to power on USB3 PHY.
-
-config OMAP_USB3
-       tristate "OMAP USB3 PHY Driver"
-       depends on ARCH_OMAP2PLUS || COMPILE_TEST
-       select OMAP_CONTROL_USB
-       select USB_PHY
-       help
-         Enable this to support the USB3 PHY that is part of SOC. This
-         driver takes care of all the PHY functionality apart from comparator.
-         This driver interacts with the "OMAP Control USB Driver" to power
-         on/off the PHY.
-
 config AM335X_CONTROL_USB
        tristate
 
index be58adae34960de4508596beb16066ae113348a3..f8fa719a31b9b9b8a86454b11a632992ceda7048 100644 (file)
@@ -13,11 +13,9 @@ obj-$(CONFIG_ISP1301_OMAP)           += phy-isp1301-omap.o
 obj-$(CONFIG_MV_U3D_PHY)               += phy-mv-u3d-usb.o
 obj-$(CONFIG_NOP_USB_XCEIV)            += phy-generic.o
 obj-$(CONFIG_TAHVO_USB)                        += phy-tahvo.o
-obj-$(CONFIG_OMAP_CONTROL_USB)         += phy-omap-control.o
 obj-$(CONFIG_AM335X_CONTROL_USB)       += phy-am335x-control.o
 obj-$(CONFIG_AM335X_PHY_USB)           += phy-am335x.o
 obj-$(CONFIG_OMAP_OTG)                 += phy-omap-otg.o
-obj-$(CONFIG_OMAP_USB3)                        += phy-omap-usb3.o
 obj-$(CONFIG_SAMSUNG_USBPHY)           += phy-samsung-usb.o
 obj-$(CONFIG_SAMSUNG_USB2PHY)          += phy-samsung-usb2.o
 obj-$(CONFIG_SAMSUNG_USB3PHY)          += phy-samsung-usb3.o
index 7aa314ef4a8ade13bac4ba73b41645d1b522dd84..c47e5a6edde28a1d35d64d6f375df401a8519709 100644 (file)
@@ -317,10 +317,12 @@ int otg_statemachine(struct otg_fsm *fsm)
                        otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
                break;
        case OTG_STATE_A_HOST:
-               if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
+               if (fsm->id || fsm->a_bus_drop)
+                       otg_set_state(fsm, OTG_STATE_A_WAIT_VFALL);
+               else if ((!fsm->a_bus_req || fsm->a_suspend_req_inf) &&
                                fsm->otg->host->b_hnp_enable)
                        otg_set_state(fsm, OTG_STATE_A_SUSPEND);
-               else if (fsm->id || !fsm->b_conn || fsm->a_bus_drop)
+               else if (!fsm->b_conn)
                        otg_set_state(fsm, OTG_STATE_A_WAIT_BCON);
                else if (!fsm->a_vbus_vld)
                        otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
@@ -346,8 +348,7 @@ int otg_statemachine(struct otg_fsm *fsm)
                        otg_set_state(fsm, OTG_STATE_A_VBUS_ERR);
                break;
        case OTG_STATE_A_WAIT_VFALL:
-               if (fsm->a_wait_vfall_tmout || fsm->id || fsm->a_bus_req ||
-                               (!fsm->a_sess_vld && !fsm->b_conn))
+               if (fsm->a_wait_vfall_tmout)
                        otg_set_state(fsm, OTG_STATE_A_IDLE);
                break;
        case OTG_STATE_A_VBUS_ERR:
index d204f745ed05e652ef168940dece89e2e16a8269..5b37b81f2ef6f30827eb87c8ee80a6af11bcd8f0 100644 (file)
@@ -1428,7 +1428,8 @@ static int __init msm_otg_probe(struct platform_device *pdev)
        motg->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
        if (!motg->phy.otg) {
                dev_err(&pdev->dev, "unable to allocate msm_otg\n");
-               return -ENOMEM;
+               ret = -ENOMEM;
+               goto free_motg;
        }
 
        motg->pdata = dev_get_platdata(&pdev->dev);
index b42897b6474c3347f11d2daebedf648a91bbd49a..c42bdf0c4a1f7322eed6ba4f7ce6b058f7c16fd9 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright 2012-2013 Freescale Semiconductor, Inc.
  * Copyright (C) 2012 Marek Vasut <marex@denx.de>
  * on behalf of DENX Software Engineering GmbH
  *
@@ -20,6 +20,9 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
 
 #define DRIVER_NAME "mxs_phy"
 
 #define HW_USBPHY_CTRL_SET                     0x34
 #define HW_USBPHY_CTRL_CLR                     0x38
 
+#define HW_USBPHY_DEBUG_SET                    0x54
+#define HW_USBPHY_DEBUG_CLR                    0x58
+
+#define HW_USBPHY_IP                           0x90
+#define HW_USBPHY_IP_SET                       0x94
+#define HW_USBPHY_IP_CLR                       0x98
+
 #define BM_USBPHY_CTRL_SFTRST                  BIT(31)
 #define BM_USBPHY_CTRL_CLKGATE                 BIT(30)
+#define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS       BIT(26)
+#define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE    BIT(25)
+#define BM_USBPHY_CTRL_ENVBUSCHG_WKUP          BIT(23)
+#define BM_USBPHY_CTRL_ENIDCHG_WKUP            BIT(22)
+#define BM_USBPHY_CTRL_ENDPDMCHG_WKUP          BIT(21)
+#define BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD       BIT(20)
+#define BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE       BIT(19)
+#define BM_USBPHY_CTRL_ENAUTO_PWRON_PLL                BIT(18)
 #define BM_USBPHY_CTRL_ENUTMILEVEL3            BIT(15)
 #define BM_USBPHY_CTRL_ENUTMILEVEL2            BIT(14)
 #define BM_USBPHY_CTRL_ENHOSTDISCONDETECT      BIT(1)
 
+#define BM_USBPHY_IP_FIX                       (BIT(17) | BIT(18))
+
+#define BM_USBPHY_DEBUG_CLKGATE                        BIT(30)
+
+/* Anatop Registers */
+#define ANADIG_ANA_MISC0                       0x150
+#define ANADIG_ANA_MISC0_SET                   0x154
+#define ANADIG_ANA_MISC0_CLR                   0x158
+
+#define ANADIG_USB1_VBUS_DET_STAT              0x1c0
+#define ANADIG_USB2_VBUS_DET_STAT              0x220
+
+#define ANADIG_USB1_LOOPBACK_SET               0x1e4
+#define ANADIG_USB1_LOOPBACK_CLR               0x1e8
+#define ANADIG_USB2_LOOPBACK_SET               0x244
+#define ANADIG_USB2_LOOPBACK_CLR               0x248
+
+#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG   BIT(12)
+#define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
+
+#define BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID        BIT(3)
+#define BM_ANADIG_USB2_VBUS_DET_STAT_VBUS_VALID        BIT(3)
+
+#define BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1  BIT(2)
+#define BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN     BIT(5)
+#define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1  BIT(2)
+#define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN     BIT(5)
+
+#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
+
+/* Do disconnection between PHY and controller without vbus */
+#define MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS   BIT(0)
+
+/*
+ * The PHY will be in messy if there is a wakeup after putting
+ * bus to suspend (set portsc.suspendM) but before setting PHY to low
+ * power mode (set portsc.phcd).
+ */
+#define MXS_PHY_ABNORMAL_IN_SUSPEND            BIT(1)
+
+/*
+ * The SOF sends too fast after resuming, it will cause disconnection
+ * between host and high speed device.
+ */
+#define MXS_PHY_SENDING_SOF_TOO_FAST           BIT(2)
+
+/*
+ * IC has bug fixes logic, they include
+ * MXS_PHY_ABNORMAL_IN_SUSPEND and MXS_PHY_SENDING_SOF_TOO_FAST
+ * which are described at above flags, the RTL will handle it
+ * according to different versions.
+ */
+#define MXS_PHY_NEED_IP_FIX                    BIT(3)
+
+struct mxs_phy_data {
+       unsigned int flags;
+};
+
+static const struct mxs_phy_data imx23_phy_data = {
+       .flags = MXS_PHY_ABNORMAL_IN_SUSPEND | MXS_PHY_SENDING_SOF_TOO_FAST,
+};
+
+static const struct mxs_phy_data imx6q_phy_data = {
+       .flags = MXS_PHY_SENDING_SOF_TOO_FAST |
+               MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+               MXS_PHY_NEED_IP_FIX,
+};
+
+static const struct mxs_phy_data imx6sl_phy_data = {
+       .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
+               MXS_PHY_NEED_IP_FIX,
+};
+
+static const struct of_device_id mxs_phy_dt_ids[] = {
+       { .compatible = "fsl,imx6sl-usbphy", .data = &imx6sl_phy_data, },
+       { .compatible = "fsl,imx6q-usbphy", .data = &imx6q_phy_data, },
+       { .compatible = "fsl,imx23-usbphy", .data = &imx23_phy_data, },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
+
 struct mxs_phy {
        struct usb_phy phy;
        struct clk *clk;
+       const struct mxs_phy_data *data;
+       struct regmap *regmap_anatop;
+       int port_id;
 };
 
-#define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
+static inline bool is_imx6q_phy(struct mxs_phy *mxs_phy)
+{
+       return mxs_phy->data == &imx6q_phy_data;
+}
+
+static inline bool is_imx6sl_phy(struct mxs_phy *mxs_phy)
+{
+       return mxs_phy->data == &imx6sl_phy_data;
+}
+
+/*
+ * PHY needs some 32K cycles to switch from 32K clock to
+ * bus (such as AHB/AXI, etc) clock.
+ */
+static void mxs_phy_clock_switch_delay(void)
+{
+       usleep_range(300, 400);
+}
 
 static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
 {
@@ -53,19 +172,105 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
        /* Power up the PHY */
        writel(0, base + HW_USBPHY_PWD);
 
-       /* enable FS/LS device */
-       writel(BM_USBPHY_CTRL_ENUTMILEVEL2 |
-              BM_USBPHY_CTRL_ENUTMILEVEL3,
+       /*
+        * USB PHY Ctrl Setting
+        * - Auto clock/power on
+        * - Enable full/low speed support
+        */
+       writel(BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
+               BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
+               BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
+               BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
+               BM_USBPHY_CTRL_ENAUTO_PWRON_PLL |
+               BM_USBPHY_CTRL_ENUTMILEVEL2 |
+               BM_USBPHY_CTRL_ENUTMILEVEL3,
               base + HW_USBPHY_CTRL_SET);
 
+       if (mxs_phy->data->flags & MXS_PHY_NEED_IP_FIX)
+               writel(BM_USBPHY_IP_FIX, base + HW_USBPHY_IP_SET);
+
        return 0;
 }
 
+/* Return true if the vbus is there */
+static bool mxs_phy_get_vbus_status(struct mxs_phy *mxs_phy)
+{
+       unsigned int vbus_value;
+
+       if (mxs_phy->port_id == 0)
+               regmap_read(mxs_phy->regmap_anatop,
+                       ANADIG_USB1_VBUS_DET_STAT,
+                       &vbus_value);
+       else if (mxs_phy->port_id == 1)
+               regmap_read(mxs_phy->regmap_anatop,
+                       ANADIG_USB2_VBUS_DET_STAT,
+                       &vbus_value);
+
+       if (vbus_value & BM_ANADIG_USB1_VBUS_DET_STAT_VBUS_VALID)
+               return true;
+       else
+               return false;
+}
+
+static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
+{
+       void __iomem *base = mxs_phy->phy.io_priv;
+       u32 reg;
+
+       if (disconnect)
+               writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
+                       base + HW_USBPHY_DEBUG_CLR);
+
+       if (mxs_phy->port_id == 0) {
+               reg = disconnect ? ANADIG_USB1_LOOPBACK_SET
+                       : ANADIG_USB1_LOOPBACK_CLR;
+               regmap_write(mxs_phy->regmap_anatop, reg,
+                       BM_ANADIG_USB1_LOOPBACK_UTMI_DIG_TST1 |
+                       BM_ANADIG_USB1_LOOPBACK_TSTI_TX_EN);
+       } else if (mxs_phy->port_id == 1) {
+               reg = disconnect ? ANADIG_USB2_LOOPBACK_SET
+                       : ANADIG_USB2_LOOPBACK_CLR;
+               regmap_write(mxs_phy->regmap_anatop, reg,
+                       BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1 |
+                       BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN);
+       }
+
+       if (!disconnect)
+               writel_relaxed(BM_USBPHY_DEBUG_CLKGATE,
+                       base + HW_USBPHY_DEBUG_SET);
+
+       /* Delay some time, and let Linestate be SE0 for controller */
+       if (disconnect)
+               usleep_range(500, 1000);
+}
+
+static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
+{
+       bool vbus_is_on = false;
+
+       /* If the SoCs don't need to disconnect line without vbus, quit */
+       if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
+               return;
+
+       /* If the SoCs don't have anatop, quit */
+       if (!mxs_phy->regmap_anatop)
+               return;
+
+       vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
+
+       if (on && !vbus_is_on)
+               __mxs_phy_disconnect_line(mxs_phy, true);
+       else
+               __mxs_phy_disconnect_line(mxs_phy, false);
+
+}
+
 static int mxs_phy_init(struct usb_phy *phy)
 {
        int ret;
        struct mxs_phy *mxs_phy = to_mxs_phy(phy);
 
+       mxs_phy_clock_switch_delay();
        ret = clk_prepare_enable(mxs_phy->clk);
        if (ret)
                return ret;
@@ -94,6 +299,7 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
                       x->io_priv + HW_USBPHY_CTRL_SET);
                clk_disable_unprepare(mxs_phy->clk);
        } else {
+               mxs_phy_clock_switch_delay();
                ret = clk_prepare_enable(mxs_phy->clk);
                if (ret)
                        return ret;
@@ -105,11 +311,28 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
        return 0;
 }
 
+static int mxs_phy_set_wakeup(struct usb_phy *x, bool enabled)
+{
+       struct mxs_phy *mxs_phy = to_mxs_phy(x);
+       u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
+                       BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
+                               BM_USBPHY_CTRL_ENIDCHG_WKUP;
+       if (enabled) {
+               mxs_phy_disconnect_line(mxs_phy, true);
+               writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_SET);
+       } else {
+               writel_relaxed(value, x->io_priv + HW_USBPHY_CTRL_CLR);
+               mxs_phy_disconnect_line(mxs_phy, false);
+       }
+
+       return 0;
+}
+
 static int mxs_phy_on_connect(struct usb_phy *phy,
                enum usb_device_speed speed)
 {
-       dev_dbg(phy->dev, "%s speed device has connected\n",
-               (speed == USB_SPEED_HIGH) ? "high" : "non-high");
+       dev_dbg(phy->dev, "%s device has connected\n",
+               (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
 
        if (speed == USB_SPEED_HIGH)
                writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
@@ -121,8 +344,8 @@ static int mxs_phy_on_connect(struct usb_phy *phy,
 static int mxs_phy_on_disconnect(struct usb_phy *phy,
                enum usb_device_speed speed)
 {
-       dev_dbg(phy->dev, "%s speed device has disconnected\n",
-               (speed == USB_SPEED_HIGH) ? "high" : "non-high");
+       dev_dbg(phy->dev, "%s device has disconnected\n",
+               (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
 
        if (speed == USB_SPEED_HIGH)
                writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
@@ -138,6 +361,9 @@ static int mxs_phy_probe(struct platform_device *pdev)
        struct clk *clk;
        struct mxs_phy *mxs_phy;
        int ret;
+       const struct of_device_id *of_id =
+                       of_match_device(mxs_phy_dt_ids, &pdev->dev);
+       struct device_node *np = pdev->dev.of_node;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        base = devm_ioremap_resource(&pdev->dev, res);
@@ -157,6 +383,22 @@ static int mxs_phy_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
+       /* Some SoCs don't have anatop registers */
+       if (of_get_property(np, "fsl,anatop", NULL)) {
+               mxs_phy->regmap_anatop = syscon_regmap_lookup_by_phandle
+                       (np, "fsl,anatop");
+               if (IS_ERR(mxs_phy->regmap_anatop)) {
+                       dev_dbg(&pdev->dev,
+                               "failed to find regmap for anatop\n");
+                       return PTR_ERR(mxs_phy->regmap_anatop);
+               }
+       }
+
+       ret = of_alias_get_id(np, "usbphy");
+       if (ret < 0)
+               dev_dbg(&pdev->dev, "failed to get alias id, errno %d\n", ret);
+       mxs_phy->port_id = ret;
+
        mxs_phy->phy.io_priv            = base;
        mxs_phy->phy.dev                = &pdev->dev;
        mxs_phy->phy.label              = DRIVER_NAME;
@@ -166,11 +408,15 @@ static int mxs_phy_probe(struct platform_device *pdev)
        mxs_phy->phy.notify_connect     = mxs_phy_on_connect;
        mxs_phy->phy.notify_disconnect  = mxs_phy_on_disconnect;
        mxs_phy->phy.type               = USB_PHY_TYPE_USB2;
+       mxs_phy->phy.set_wakeup         = mxs_phy_set_wakeup;
 
        mxs_phy->clk = clk;
+       mxs_phy->data = of_id->data;
 
        platform_set_drvdata(pdev, mxs_phy);
 
+       device_set_wakeup_capable(&pdev->dev, true);
+
        ret = usb_add_phy_dev(&mxs_phy->phy);
        if (ret)
                return ret;
@@ -187,11 +433,46 @@ static int mxs_phy_remove(struct platform_device *pdev)
        return 0;
 }
 
-static const struct of_device_id mxs_phy_dt_ids[] = {
-       { .compatible = "fsl,imx23-usbphy", },
-       { /* sentinel */ }
-};
-MODULE_DEVICE_TABLE(of, mxs_phy_dt_ids);
+#ifdef CONFIG_PM_SLEEP
+static void mxs_phy_enable_ldo_in_suspend(struct mxs_phy *mxs_phy, bool on)
+{
+       unsigned int reg = on ? ANADIG_ANA_MISC0_SET : ANADIG_ANA_MISC0_CLR;
+
+       /* If the SoCs don't have anatop, quit */
+       if (!mxs_phy->regmap_anatop)
+               return;
+
+       if (is_imx6q_phy(mxs_phy))
+               regmap_write(mxs_phy->regmap_anatop, reg,
+                       BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG);
+       else if (is_imx6sl_phy(mxs_phy))
+               regmap_write(mxs_phy->regmap_anatop,
+                       reg, BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL);
+}
+
+static int mxs_phy_system_suspend(struct device *dev)
+{
+       struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               mxs_phy_enable_ldo_in_suspend(mxs_phy, true);
+
+       return 0;
+}
+
+static int mxs_phy_system_resume(struct device *dev)
+{
+       struct mxs_phy *mxs_phy = dev_get_drvdata(dev);
+
+       if (device_may_wakeup(dev))
+               mxs_phy_enable_ldo_in_suspend(mxs_phy, false);
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static SIMPLE_DEV_PM_OPS(mxs_phy_pm, mxs_phy_system_suspend,
+               mxs_phy_system_resume);
 
 static struct platform_driver mxs_phy_driver = {
        .probe = mxs_phy_probe,
@@ -200,6 +481,7 @@ static struct platform_driver mxs_phy_driver = {
                .name = DRIVER_NAME,
                .owner  = THIS_MODULE,
                .of_match_table = mxs_phy_dt_ids,
+               .pm = &mxs_phy_pm,
         },
 };
 
diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c
deleted file mode 100644 (file)
index e725318..0000000
+++ /dev/null
@@ -1,319 +0,0 @@
-/*
- * omap-control-usb.c - The USB part of control module.
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.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.
- *
- * Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/usb/omap_control_usb.h>
-
-/**
- * omap_control_usb_phy_power - power on/off the phy using control module reg
- * @dev: the control module device
- * @on: 0 or 1, based on powering on or off the PHY
- */
-void omap_control_usb_phy_power(struct device *dev, int on)
-{
-       u32 val;
-       unsigned long rate;
-       struct omap_control_usb *control_usb;
-
-       if (IS_ERR(dev) || !dev) {
-               pr_err("%s: invalid device\n", __func__);
-               return;
-       }
-
-       control_usb = dev_get_drvdata(dev);
-       if (!control_usb) {
-               dev_err(dev, "%s: invalid control usb device\n", __func__);
-               return;
-       }
-
-       if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
-               return;
-
-       val = readl(control_usb->power);
-
-       switch (control_usb->type) {
-       case OMAP_CTRL_TYPE_USB2:
-               if (on)
-                       val &= ~OMAP_CTRL_DEV_PHY_PD;
-               else
-                       val |= OMAP_CTRL_DEV_PHY_PD;
-               break;
-
-       case OMAP_CTRL_TYPE_PIPE3:
-               rate = clk_get_rate(control_usb->sys_clk);
-               rate = rate/1000000;
-
-               if (on) {
-                       val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
-                                       OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
-                       val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
-                               OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
-                       val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
-               } else {
-                       val &= ~OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK;
-                       val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
-                               OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
-               }
-               break;
-
-       case OMAP_CTRL_TYPE_DRA7USB2:
-               if (on)
-                       val &= ~OMAP_CTRL_USB2_PHY_PD;
-               else
-                       val |= OMAP_CTRL_USB2_PHY_PD;
-               break;
-
-       case OMAP_CTRL_TYPE_AM437USB2:
-               if (on) {
-                       val &= ~(AM437X_CTRL_USB2_PHY_PD |
-                                       AM437X_CTRL_USB2_OTG_PD);
-                       val |= (AM437X_CTRL_USB2_OTGVDET_EN |
-                                       AM437X_CTRL_USB2_OTGSESSEND_EN);
-               } else {
-                       val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |
-                                       AM437X_CTRL_USB2_OTGSESSEND_EN);
-                       val |= (AM437X_CTRL_USB2_PHY_PD |
-                                        AM437X_CTRL_USB2_OTG_PD);
-               }
-               break;
-       default:
-               dev_err(dev, "%s: type %d not recognized\n",
-                                       __func__, control_usb->type);
-               break;
-       }
-
-       writel(val, control_usb->power);
-}
-EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
-
-/**
- * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
- * @ctrl_usb: struct omap_control_usb *
- *
- * Writes to the mailbox register to notify the usb core that a usb
- * device has been connected.
- */
-static void omap_control_usb_host_mode(struct omap_control_usb *ctrl_usb)
-{
-       u32 val;
-
-       val = readl(ctrl_usb->otghs_control);
-       val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
-       val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
-       writel(val, ctrl_usb->otghs_control);
-}
-
-/**
- * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
- * impedance
- * @ctrl_usb: struct omap_control_usb *
- *
- * Writes to the mailbox register to notify the usb core that it has been
- * connected to a usb host.
- */
-static void omap_control_usb_device_mode(struct omap_control_usb *ctrl_usb)
-{
-       u32 val;
-
-       val = readl(ctrl_usb->otghs_control);
-       val &= ~OMAP_CTRL_DEV_SESSEND;
-       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
-               OMAP_CTRL_DEV_VBUSVALID;
-       writel(val, ctrl_usb->otghs_control);
-}
-
-/**
- * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
- * impedance
- * @ctrl_usb: struct omap_control_usb *
- *
- * Writes to the mailbox register to notify the usb core it's now in
- * disconnected state.
- */
-static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
-{
-       u32 val;
-
-       val = readl(ctrl_usb->otghs_control);
-       val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
-       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
-       writel(val, ctrl_usb->otghs_control);
-}
-
-/**
- * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode
- * or device mode or to denote disconnected state
- * @dev: the control module device
- * @mode: The mode to which usb should be configured
- *
- * This is an API to write to the mailbox register to notify the usb core that
- * a usb device has been connected.
- */
-void omap_control_usb_set_mode(struct device *dev,
-       enum omap_control_usb_mode mode)
-{
-       struct omap_control_usb *ctrl_usb;
-
-       if (IS_ERR(dev) || !dev)
-               return;
-
-       ctrl_usb = dev_get_drvdata(dev);
-
-       if (!ctrl_usb) {
-               dev_err(dev, "Invalid control usb device\n");
-               return;
-       }
-
-       if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS)
-               return;
-
-       switch (mode) {
-       case USB_MODE_HOST:
-               omap_control_usb_host_mode(ctrl_usb);
-               break;
-       case USB_MODE_DEVICE:
-               omap_control_usb_device_mode(ctrl_usb);
-               break;
-       case USB_MODE_DISCONNECT:
-               omap_control_usb_set_sessionend(ctrl_usb);
-               break;
-       default:
-               dev_vdbg(dev, "invalid omap control usb mode\n");
-       }
-}
-EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
-
-#ifdef CONFIG_OF
-
-static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
-static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
-static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
-static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
-static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
-
-static const struct of_device_id omap_control_usb_id_table[] = {
-       {
-               .compatible = "ti,control-phy-otghs",
-               .data = &otghs_data,
-       },
-       {
-               .compatible = "ti,control-phy-usb2",
-               .data = &usb2_data,
-       },
-       {
-               .compatible = "ti,control-phy-pipe3",
-               .data = &pipe3_data,
-       },
-       {
-               .compatible = "ti,control-phy-dra7usb2",
-               .data = &dra7usb2_data,
-       },
-       {
-               .compatible = "ti,control-phy-am437usb2",
-               .data = &am437usb2_data,
-       },
-       {},
-};
-MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
-#endif
-
-
-static int omap_control_usb_probe(struct platform_device *pdev)
-{
-       struct resource *res;
-       const struct of_device_id *of_id;
-       struct omap_control_usb *control_usb;
-
-       of_id = of_match_device(of_match_ptr(omap_control_usb_id_table),
-                                                               &pdev->dev);
-       if (!of_id)
-               return -EINVAL;
-
-       control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
-               GFP_KERNEL);
-       if (!control_usb) {
-               dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
-               return -ENOMEM;
-       }
-
-       control_usb->dev = &pdev->dev;
-       control_usb->type = *(enum omap_control_usb_type *)of_id->data;
-
-       if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) {
-               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                       "otghs_control");
-               control_usb->otghs_control = devm_ioremap_resource(
-                       &pdev->dev, res);
-               if (IS_ERR(control_usb->otghs_control))
-                       return PTR_ERR(control_usb->otghs_control);
-       } else {
-               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                               "power");
-               control_usb->power = devm_ioremap_resource(&pdev->dev, res);
-               if (IS_ERR(control_usb->power)) {
-                       dev_err(&pdev->dev, "Couldn't get power register\n");
-                       return PTR_ERR(control_usb->power);
-               }
-       }
-
-       if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) {
-               control_usb->sys_clk = devm_clk_get(control_usb->dev,
-                       "sys_clkin");
-               if (IS_ERR(control_usb->sys_clk)) {
-                       pr_err("%s: unable to get sys_clkin\n", __func__);
-                       return -EINVAL;
-               }
-       }
-
-       dev_set_drvdata(control_usb->dev, control_usb);
-
-       return 0;
-}
-
-static struct platform_driver omap_control_usb_driver = {
-       .probe          = omap_control_usb_probe,
-       .driver         = {
-               .name   = "omap-control-usb",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(omap_control_usb_id_table),
-       },
-};
-
-static int __init omap_control_usb_init(void)
-{
-       return platform_driver_register(&omap_control_usb_driver);
-}
-subsys_initcall(omap_control_usb_init);
-
-static void __exit omap_control_usb_exit(void)
-{
-       platform_driver_unregister(&omap_control_usb_driver);
-}
-module_exit(omap_control_usb_exit);
-
-MODULE_ALIAS("platform: omap_control_usb");
-MODULE_AUTHOR("Texas Instruments Inc.");
-MODULE_DESCRIPTION("OMAP Control Module USB Driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c
deleted file mode 100644 (file)
index 0c6ba29..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP.
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.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.
- *
- * Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/usb/omap_usb.h>
-#include <linux/of.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/pm_runtime.h>
-#include <linux/delay.h>
-#include <linux/usb/omap_control_usb.h>
-#include <linux/of_platform.h>
-
-#define        PLL_STATUS              0x00000004
-#define        PLL_GO                  0x00000008
-#define        PLL_CONFIGURATION1      0x0000000C
-#define        PLL_CONFIGURATION2      0x00000010
-#define        PLL_CONFIGURATION3      0x00000014
-#define        PLL_CONFIGURATION4      0x00000020
-
-#define        PLL_REGM_MASK           0x001FFE00
-#define        PLL_REGM_SHIFT          0x9
-#define        PLL_REGM_F_MASK         0x0003FFFF
-#define        PLL_REGM_F_SHIFT        0x0
-#define        PLL_REGN_MASK           0x000001FE
-#define        PLL_REGN_SHIFT          0x1
-#define        PLL_SELFREQDCO_MASK     0x0000000E
-#define        PLL_SELFREQDCO_SHIFT    0x1
-#define        PLL_SD_MASK             0x0003FC00
-#define        PLL_SD_SHIFT            0x9
-#define        SET_PLL_GO              0x1
-#define        PLL_TICOPWDN            0x10000
-#define        PLL_LOCK                0x2
-#define        PLL_IDLE                0x1
-
-/*
- * This is an Empirical value that works, need to confirm the actual
- * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status
- * to be correctly reflected in the USB3PHY_PLL_STATUS register.
- */
-# define PLL_IDLE_TIME  100;
-
-struct usb_dpll_map {
-       unsigned long rate;
-       struct usb_dpll_params params;
-};
-
-static struct usb_dpll_map dpll_map[] = {
-       {12000000, {1250, 5, 4, 20, 0} },       /* 12 MHz */
-       {16800000, {3125, 20, 4, 20, 0} },      /* 16.8 MHz */
-       {19200000, {1172, 8, 4, 20, 65537} },   /* 19.2 MHz */
-       {20000000, {1000, 7, 4, 10, 0} },       /* 20 MHz */
-       {26000000, {1250, 12, 4, 20, 0} },      /* 26 MHz */
-       {38400000, {3125, 47, 4, 20, 92843} },  /* 38.4 MHz */
-};
-
-static struct usb_dpll_params *omap_usb3_get_dpll_params(unsigned long rate)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(dpll_map); i++) {
-               if (rate == dpll_map[i].rate)
-                       return &dpll_map[i].params;
-       }
-
-       return NULL;
-}
-
-static int omap_usb3_suspend(struct usb_phy *x, int suspend)
-{
-       struct omap_usb *phy = phy_to_omapusb(x);
-       int     val;
-       int timeout = PLL_IDLE_TIME;
-
-       if (suspend && !phy->is_suspended) {
-               val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
-               val |= PLL_IDLE;
-               omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
-
-               do {
-                       val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
-                       if (val & PLL_TICOPWDN)
-                               break;
-                       udelay(1);
-               } while (--timeout);
-
-               omap_control_usb_phy_power(phy->control_dev, 0);
-
-               phy->is_suspended       = 1;
-       } else if (!suspend && phy->is_suspended) {
-               phy->is_suspended       = 0;
-
-               val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
-               val &= ~PLL_IDLE;
-               omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
-
-               do {
-                       val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
-                       if (!(val & PLL_TICOPWDN))
-                               break;
-                       udelay(1);
-               } while (--timeout);
-       }
-
-       return 0;
-}
-
-static void omap_usb_dpll_relock(struct omap_usb *phy)
-{
-       u32             val;
-       unsigned long   timeout;
-
-       omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
-
-       timeout = jiffies + msecs_to_jiffies(20);
-       do {
-               val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
-               if (val & PLL_LOCK)
-                       break;
-       } while (!WARN_ON(time_after(jiffies, timeout)));
-}
-
-static int omap_usb_dpll_lock(struct omap_usb *phy)
-{
-       u32                     val;
-       unsigned long           rate;
-       struct usb_dpll_params *dpll_params;
-
-       rate = clk_get_rate(phy->sys_clk);
-       dpll_params = omap_usb3_get_dpll_params(rate);
-       if (!dpll_params) {
-               dev_err(phy->dev,
-                         "No DPLL configuration for %lu Hz SYS CLK\n", rate);
-               return -EINVAL;
-       }
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
-       val &= ~PLL_REGN_MASK;
-       val |= dpll_params->n << PLL_REGN_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
-       val &= ~PLL_SELFREQDCO_MASK;
-       val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
-       val &= ~PLL_REGM_MASK;
-       val |= dpll_params->m << PLL_REGM_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
-       val &= ~PLL_REGM_F_MASK;
-       val |= dpll_params->mf << PLL_REGM_F_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
-       val &= ~PLL_SD_MASK;
-       val |= dpll_params->sd << PLL_SD_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
-
-       omap_usb_dpll_relock(phy);
-
-       return 0;
-}
-
-static int omap_usb3_init(struct usb_phy *x)
-{
-       struct omap_usb *phy = phy_to_omapusb(x);
-       int ret;
-
-       ret = omap_usb_dpll_lock(phy);
-       if (ret)
-               return ret;
-
-       omap_control_usb_phy_power(phy->control_dev, 1);
-
-       return 0;
-}
-
-static int omap_usb3_probe(struct platform_device *pdev)
-{
-       struct omap_usb *phy;
-       struct resource *res;
-       struct device_node *node = pdev->dev.of_node;
-       struct device_node *control_node;
-       struct platform_device *control_pdev;
-
-       if (!node)
-               return -EINVAL;
-
-       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
-       if (!phy) {
-               dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n");
-               return -ENOMEM;
-       }
-
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
-       phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(phy->pll_ctrl_base))
-               return PTR_ERR(phy->pll_ctrl_base);
-
-       phy->dev                = &pdev->dev;
-
-       phy->phy.dev            = phy->dev;
-       phy->phy.label          = "omap-usb3";
-       phy->phy.init           = omap_usb3_init;
-       phy->phy.set_suspend    = omap_usb3_suspend;
-       phy->phy.type           = USB_PHY_TYPE_USB3;
-
-       phy->is_suspended       = 1;
-       phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
-       if (IS_ERR(phy->wkupclk)) {
-               dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
-               return PTR_ERR(phy->wkupclk);
-       }
-       clk_prepare(phy->wkupclk);
-
-       phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
-       if (IS_ERR(phy->optclk)) {
-               dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n");
-               return PTR_ERR(phy->optclk);
-       }
-       clk_prepare(phy->optclk);
-
-       phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin");
-       if (IS_ERR(phy->sys_clk)) {
-               pr_err("%s: unable to get sys_clkin\n", __func__);
-               return -EINVAL;
-       }
-
-       control_node = of_parse_phandle(node, "ctrl-module", 0);
-       if (!control_node) {
-               dev_err(&pdev->dev, "Failed to get control device phandle\n");
-               return -EINVAL;
-       }
-       control_pdev = of_find_device_by_node(control_node);
-       if (!control_pdev) {
-               dev_err(&pdev->dev, "Failed to get control device\n");
-               return -EINVAL;
-       }
-
-       phy->control_dev = &control_pdev->dev;
-
-       omap_control_usb_phy_power(phy->control_dev, 0);
-       usb_add_phy_dev(&phy->phy);
-
-       platform_set_drvdata(pdev, phy);
-
-       pm_runtime_enable(phy->dev);
-       pm_runtime_get(&pdev->dev);
-
-       return 0;
-}
-
-static int omap_usb3_remove(struct platform_device *pdev)
-{
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       clk_unprepare(phy->wkupclk);
-       clk_unprepare(phy->optclk);
-       usb_remove_phy(&phy->phy);
-       if (!pm_runtime_suspended(&pdev->dev))
-               pm_runtime_put(&pdev->dev);
-       pm_runtime_disable(&pdev->dev);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_RUNTIME
-
-static int omap_usb3_runtime_suspend(struct device *dev)
-{
-       struct platform_device  *pdev = to_platform_device(dev);
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       clk_disable(phy->wkupclk);
-       clk_disable(phy->optclk);
-
-       return 0;
-}
-
-static int omap_usb3_runtime_resume(struct device *dev)
-{
-       u32 ret = 0;
-       struct platform_device  *pdev = to_platform_device(dev);
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       ret = clk_enable(phy->optclk);
-       if (ret) {
-               dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
-               goto err1;
-       }
-
-       ret = clk_enable(phy->wkupclk);
-       if (ret) {
-               dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
-               goto err2;
-       }
-
-       return 0;
-
-err2:
-       clk_disable(phy->optclk);
-
-err1:
-       return ret;
-}
-
-static const struct dev_pm_ops omap_usb3_pm_ops = {
-       SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume,
-               NULL)
-};
-
-#define DEV_PM_OPS     (&omap_usb3_pm_ops)
-#else
-#define DEV_PM_OPS     NULL
-#endif
-
-#ifdef CONFIG_OF
-static const struct of_device_id omap_usb3_id_table[] = {
-       { .compatible = "ti,omap-usb3" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, omap_usb3_id_table);
-#endif
-
-static struct platform_driver omap_usb3_driver = {
-       .probe          = omap_usb3_probe,
-       .remove         = omap_usb3_remove,
-       .driver         = {
-               .name   = "omap-usb3",
-               .owner  = THIS_MODULE,
-               .pm     = DEV_PM_OPS,
-               .of_match_table = of_match_ptr(omap_usb3_id_table),
-       },
-};
-
-module_platform_driver(omap_usb3_driver);
-
-MODULE_ALIAS("platform: omap_usb3");
-MODULE_AUTHOR("Texas Instruments Inc.");
-MODULE_DESCRIPTION("OMAP USB3 phy driver");
-MODULE_LICENSE("GPL v2");
index 551e0a6c0e221d4b6bfd8e506044efc4aeafc71b..388d89f6b14117f82d5324976211bda5edec4130 100644 (file)
@@ -177,15 +177,15 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev)
        struct clk *clk;
        int retval;
 
-       pdata = dev_get_platdata(&pdev->dev);
+       pdata = dev_get_platdata(dev);
        if (!pdata) {
                dev_err(dev, "No platform data\n");
                return -EINVAL;
        }
 
-       clk = devm_clk_get(&pdev->dev, "usbhs");
+       clk = devm_clk_get(dev, "usbhs");
        if (IS_ERR(clk)) {
-               dev_err(&pdev->dev, "Can't get the clock\n");
+               dev_err(dev, "Can't get the clock\n");
                return PTR_ERR(clk);
        }
 
index 214172b68d5d7a54fa1d223c098fead15089fd7f..04778cf80d60cf0095a6311a0f19896554fb67ce 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/io.h>
 #include <linux/usb/musb-omap.h>
 #include <linux/usb/phy_companion.h>
-#include <linux/usb/omap_usb.h>
+#include <linux/phy/omap_usb.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/consumer.h>
 #include <linux/err.h>
index 217339dd7a902072ff9bab540a60f140aa49fd81..17ea3f271bd8f85e4bdbb2c51969302b9b12ccc4 100644 (file)
@@ -47,6 +47,8 @@ struct ulpi_info {
 static struct ulpi_info ulpi_ids[] = {
        ULPI_INFO(ULPI_ID(0x04cc, 0x1504), "NXP ISP1504"),
        ULPI_INFO(ULPI_ID(0x0424, 0x0006), "SMSC USB331x"),
+       ULPI_INFO(ULPI_ID(0x0424, 0x0007), "SMSC USB3320"),
+       ULPI_INFO(ULPI_ID(0x0451, 0x1507), "TI TUSB1210"),
 };
 
 static int ulpi_set_otg_flags(struct usb_phy *phy)
index 82371f61f23d5b3d95a047314dc9953ca3721ea4..2d72aa3564a31e9eeade77423ec16de3d743c706 100644 (file)
@@ -323,11 +323,11 @@ static int ch341_open(struct tty_struct *tty, struct usb_serial_port *port)
        if (r)
                goto out;
 
-       dev_dbg(&port->dev, "%s - submitting interrupt urb", __func__);
+       dev_dbg(&port->dev, "%s - submitting interrupt urb\n", __func__);
        r = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
        if (r) {
-               dev_err(&port->dev, "%s - failed submitting interrupt urb,"
-                       " error %d\n", __func__, r);
+               dev_err(&port->dev, "%s - failed to submit interrupt urb: %d\n",
+                       __func__, r);
                ch341_close(port);
                goto out;
        }
index 0ac3b3b3236c5f3e6982aa07f602a3b36afc15e7..2916dea3ede84a2a02f8ed58f8b31c18f0fefc1b 100644 (file)
@@ -220,7 +220,7 @@ static int cyberjack_write(struct tty_struct *tty,
                result = usb_submit_urb(port->write_urb, GFP_ATOMIC);
                if (result) {
                        dev_err(&port->dev,
-                               "%s - failed submitting write urb, error %d",
+                               "%s - failed submitting write urb, error %d\n",
                                __func__, result);
                        /* Throw away data. No better idea what to do with it. */
                        priv->wrfilled = 0;
index bccb1223143a37298dad934842b6733e99c38032..01bf533928192d6f2f4b77f44995bd63fd6d1be2 100644 (file)
@@ -279,7 +279,7 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate)
                         * the generic firmware, but are not used with
                         * NMEA and SiRF protocols */
                        dev_dbg(&port->dev,
-                               "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS",
+                               "%s - failed setting baud rate, unsupported speed of %d on Earthmate GPS\n",
                                __func__, new_rate);
                        return -1;
                }
@@ -1224,7 +1224,6 @@ static void cypress_write_int_callback(struct urb *urb)
        struct usb_serial_port *port = urb->context;
        struct cypress_private *priv = usb_get_serial_port_data(port);
        struct device *dev = &urb->dev->dev;
-       int result;
        int status = urb->status;
 
        switch (status) {
@@ -1239,21 +1238,9 @@ static void cypress_write_int_callback(struct urb *urb)
                        __func__, status);
                priv->write_urb_in_use = 0;
                return;
-       case -EPIPE: /* no break needed; clear halt and resubmit */
-               if (!priv->comm_is_ok)
-                       break;
-               usb_clear_halt(port->serial->dev, 0x02);
-               /* error in the urb, so we have to resubmit it */
-               dev_dbg(dev, "%s - nonzero write bulk status received: %d\n",
-                       __func__, status);
-               port->interrupt_out_urb->transfer_buffer_length = 1;
-               result = usb_submit_urb(port->interrupt_out_urb, GFP_ATOMIC);
-               if (!result)
-                       return;
-               dev_err(dev, "%s - failed resubmitting write urb, error %d\n",
-                       __func__, result);
-               cypress_set_dead(port);
-               break;
+       case -EPIPE:
+               /* Cannot call usb_clear_halt while in_interrupt */
+               /* FALLTHROUGH */
        default:
                dev_err(dev, "%s - unexpected nonzero write status received: %d\n",
                        __func__, status);
index b63ce023f96f1ab7284e218ca68219da5f952440..1bd192290b08df0fb697fa91f2517b6756680012 100644 (file)
@@ -332,9 +332,9 @@ void usb_serial_generic_process_read_urb(struct urb *urb)
         * stuff like 3G modems, so shortcircuit it in the 99.9999999% of
         * cases where the USB serial is not a console anyway.
         */
-       if (!port->port.console || !port->sysrq)
+       if (!port->port.console || !port->sysrq) {
                tty_insert_flip_string(&port->port, ch, urb->actual_length);
-       else {
+       else {
                for (i = 0; i < urb->actual_length; i++, ch++) {
                        if (!usb_serial_handle_sysrq_char(port, *ch))
                                tty_insert_flip_char(&port->port, *ch, TTY_NORMAL);
@@ -359,24 +359,38 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
 
        dev_dbg(&port->dev, "%s - urb %d, len %d\n", __func__, i,
                                                        urb->actual_length);
-
-       if (urb->status) {
-               dev_dbg(&port->dev, "%s - non-zero urb status: %d\n",
-                       __func__, urb->status);
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+                                                       __func__, urb->status);
                return;
+       case -EPIPE:
+               dev_err(&port->dev, "%s - urb stopped: %d\n",
+                                                       __func__, urb->status);
+               return;
+       default:
+               dev_err(&port->dev, "%s - nonzero urb status: %d\n",
+                                                       __func__, urb->status);
+               goto resubmit;
        }
 
        usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
        port->serial->type->process_read_urb(urb);
 
+resubmit:
        /* Throttle the device if requested by tty */
        spin_lock_irqsave(&port->lock, flags);
        port->throttled = port->throttle_req;
        if (!port->throttled) {
                spin_unlock_irqrestore(&port->lock, flags);
                usb_serial_generic_submit_read_urb(port, i, GFP_ATOMIC);
-       } else
+       } else {
                spin_unlock_irqrestore(&port->lock, flags);
+       }
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_read_bulk_callback);
 
@@ -384,29 +398,38 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
 {
        unsigned long flags;
        struct usb_serial_port *port = urb->context;
-       int status = urb->status;
        int i;
 
-       for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i)
+       for (i = 0; i < ARRAY_SIZE(port->write_urbs); ++i) {
                if (port->write_urbs[i] == urb)
                        break;
-
+       }
        spin_lock_irqsave(&port->lock, flags);
        port->tx_bytes -= urb->transfer_buffer_length;
        set_bit(i, &port->write_urbs_free);
        spin_unlock_irqrestore(&port->lock, flags);
 
-       if (status) {
-               dev_dbg(&port->dev, "%s - non-zero urb status: %d\n",
-                       __func__, status);
-
-               spin_lock_irqsave(&port->lock, flags);
-               kfifo_reset_out(&port->write_fifo);
-               spin_unlock_irqrestore(&port->lock, flags);
-       } else {
-               usb_serial_generic_write_start(port, GFP_ATOMIC);
+       switch (urb->status) {
+       case 0:
+               break;
+       case -ENOENT:
+       case -ECONNRESET:
+       case -ESHUTDOWN:
+               dev_dbg(&port->dev, "%s - urb stopped: %d\n",
+                                                       __func__, urb->status);
+               return;
+       case -EPIPE:
+               dev_err_console(port, "%s - urb stopped: %d\n",
+                                                       __func__, urb->status);
+               return;
+       default:
+               dev_err_console(port, "%s - nonzero urb status: %d\n",
+                                                       __func__, urb->status);
+               goto resubmit;
        }
 
+resubmit:
+       usb_serial_generic_write_start(port, GFP_ATOMIC);
        usb_serial_port_softint(port);
 }
 EXPORT_SYMBOL_GPL(usb_serial_generic_write_bulk_callback);
index d00dae17d520d8fffb744bc1c0f1d196f0344beb..5ad4a0fb4b26ff67380b00e5023ca24e77078266 100644 (file)
@@ -1151,7 +1151,7 @@ static ssize_t vcc_mode_store(struct device *dev,
                goto fail_store_vcc_mode;
        }
 
-       dev_dbg(dev, "%s: setting vcc_mode = %ld", __func__, v);
+       dev_dbg(dev, "%s: setting vcc_mode = %ld\n", __func__, v);
 
        if ((v != 3) && (v != 5)) {
                dev_err(dev, "%s - vcc_mode %ld is invalid\n", __func__, v);
index 265c6776b081747094a2b31f6e10994cf4ef247f..d3acaead5a81852fd4b60810be55199490ce114d 100644 (file)
@@ -397,17 +397,6 @@ static void        usa26_instat_callback(struct urb *urb)
 
        msg = (struct keyspan_usa26_portStatusMessage *)data;
 
-#if 0
-       dev_dbg(&urb->dev->dev,
-               "%s - port status: port %d cts %d dcd %d dsr %d ri %d toff %d txoff %d rxen %d cr %d",
-               __func__, msg->port, msg->hskia_cts, msg->gpia_dcd, msg->dsr,
-               msg->ri, msg->_txOff, msg->_txXoff, msg->rxEnabled,
-               msg->controlResponse);
-#endif
-
-       /* Now do something useful with the data */
-
-
        /* Check port number from message and retrieve private data */
        if (msg->port >= serial->num_ports) {
                dev_dbg(&urb->dev->dev, "%s - Unexpected port number %d\n", __func__, msg->port);
@@ -523,9 +512,6 @@ static void usa28_instat_callback(struct urb *urb)
                goto exit;
        }
 
-       /*dev_dbg(&urb->dev->dev, "%s %12ph", __func__, data);*/
-
-       /* Now do something useful with the data */
        msg = (struct keyspan_usa28_portStatusMessage *)data;
 
        /* Check port number from message and retrieve private data */
@@ -605,9 +591,6 @@ static void usa49_instat_callback(struct urb *urb)
                goto exit;
        }
 
-       /*dev_dbg(&urb->dev->dev, "%s: %11ph", __func__, data);*/
-
-       /* Now do something useful with the data */
        msg = (struct keyspan_usa49_portStatusMessage *)data;
 
        /* Check port number from message and retrieve private data */
@@ -1793,12 +1776,6 @@ static int keyspan_usa28_send_setup(struct usb_serial *serial,
        err = usb_submit_urb(this_urb, GFP_ATOMIC);
        if (err != 0)
                dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed\n", __func__);
-#if 0
-       else {
-               dev_dbg(&port->dev, "%s - usb_submit_urb(setup) OK %d bytes\n", __func__,
-                   this_urb->transfer_buffer_length);
-       }
-#endif
 
        return 0;
 }
@@ -1976,13 +1953,6 @@ static int keyspan_usa49_send_setup(struct usb_serial *serial,
        err = usb_submit_urb(this_urb, GFP_ATOMIC);
        if (err != 0)
                dev_dbg(&port->dev, "%s - usb_submit_urb(setup) failed (%d)\n", __func__, err);
-#if 0
-       else {
-               dev_dbg(&port->dev, "%s - usb_submit_urb(%d) OK %d bytes (end %d)\n", __func__,
-                       outcont_urb, this_urb->transfer_buffer_length,
-                       usb_pipeendpoint(this_urb->pipe));
-       }
-#endif
 
        return 0;
 }
index e972412b614b6e84082ca8afafcd9778c1ab701d..742d827f876cfe5dc10fe73783fdc5152f91ec8f 100644 (file)
@@ -189,7 +189,7 @@ exit:
        retval = usb_submit_urb(urb, GFP_ATOMIC);
        if (retval)
                dev_err(&port->dev,
-                       "%s - usb_submit_urb failed with result %d",
+                       "%s - usb_submit_urb failed with result %d\n",
                        __func__, retval);
 }
 
index c88cc4966b23f067e4b443d937f2ee5c15f5cfad..d7440b7557afde7a643dbf76f8e22eae517bf435 100644 (file)
@@ -201,7 +201,7 @@ static int klsi_105_get_line_state(struct usb_serial_port *port,
        else {
                status = get_unaligned_le16(status_buf);
 
-               dev_info(&port->serial->dev->dev, "read status %x %x",
+               dev_info(&port->serial->dev->dev, "read status %x %x\n",
                         status_buf[0], status_buf[1]);
 
                *line_state_p = klsi_105_status2linestate(status);
@@ -464,7 +464,7 @@ static void klsi_105_set_termios(struct tty_struct *tty,
                priv->cfg.baudrate = kl5kusb105a_sio_b115200;
                break;
        default:
-               dev_dbg(dev, "KLSI USB->Serial converter: unsupported baudrate request, using default of 9600");
+               dev_dbg(dev, "unsupported baudrate, using 9600\n");
                priv->cfg.baudrate = kl5kusb105a_sio_b9600;
                baud = 9600;
                break;
index 618c1c1f227e1c56783aea4f05aad0f31dde9bf1..fee242387f55cf9ea08d5eef2d1f5dff3679f6c1 100644 (file)
@@ -557,7 +557,8 @@ static int kobil_ioctl(struct tty_struct *tty,
                        );
 
                dev_dbg(&port->dev,
-                       "%s - Send reset_all_queues (FLUSH) URB returns: %i", __func__, result);
+                       "%s - Send reset_all_queues (FLUSH) URB returns: %i\n",
+                       __func__, result);
                kfree(transfer_buffer);
                return (result < 0) ? -EIO: 0;
        default:
index 4eb277225a771f5e0690dc91b04a84c8a7db110f..dfd728a263d29bbc522e20d52448c7da4c977fc9 100644 (file)
@@ -209,7 +209,7 @@ static int write_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
                                     index, NULL, 0, MOS_WDR_TIMEOUT);
        if (status < 0)
                dev_err(&usbdev->dev,
-                       "mos7720: usb_control_msg() failed: %d", status);
+                       "mos7720: usb_control_msg() failed: %d\n", status);
        return status;
 }
 
@@ -240,7 +240,7 @@ static int read_mos_reg(struct usb_serial *serial, unsigned int serial_portnum,
                *data = *buf;
        else if (status < 0)
                dev_err(&usbdev->dev,
-                       "mos7720: usb_control_msg() failed: %d", status);
+                       "mos7720: usb_control_msg() failed: %d\n", status);
        kfree(buf);
 
        return status;
@@ -399,7 +399,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
                              &mos_parport->deferred_urbs);
                spin_unlock_irqrestore(&mos_parport->listlock, flags);
                tasklet_schedule(&mos_parport->urb_tasklet);
-               dev_dbg(&usbdev->dev, "tasklet scheduled");
+               dev_dbg(&usbdev->dev, "tasklet scheduled\n");
                return 0;
        }
 
@@ -418,7 +418,7 @@ static int write_parport_reg_nonblock(struct mos7715_parport *mos_parport,
        mutex_unlock(&serial->disc_mutex);
        if (ret_val) {
                dev_err(&usbdev->dev,
-                       "%s: submit_urb() failed: %d", __func__, ret_val);
+                       "%s: submit_urb() failed: %d\n", __func__, ret_val);
                spin_lock_irqsave(&mos_parport->listlock, flags);
                list_del(&urbtrack->urblist_entry);
                spin_unlock_irqrestore(&mos_parport->listlock, flags);
@@ -656,7 +656,7 @@ static size_t parport_mos7715_write_compat(struct parport *pp,
        parport_epilogue(pp);
        if (retval) {
                dev_err(&mos_parport->serial->dev->dev,
-                       "mos7720: usb_bulk_msg() failed: %d", retval);
+                       "mos7720: usb_bulk_msg() failed: %d\n", retval);
                return 0;
        }
        return actual_len;
@@ -875,7 +875,7 @@ static void mos7715_interrupt_callback(struct urb *urb)
        if (!(iir & 0x01)) {    /* serial port interrupt pending */
                switch (iir & 0x0f) {
                case SERIAL_IIR_RLS:
-                       dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n\n");
+                       dev_dbg(dev, "Serial Port: Receiver status error or address bit detected in 9-bit mode\n");
                        break;
                case SERIAL_IIR_CTI:
                        dev_dbg(dev, "Serial Port: Receiver time out\n");
index e9d967ff521b5713fdd2dcc6d0d4bcddeabad061..393be562d875da89b573f124712c5959ea663247 100644 (file)
@@ -522,11 +522,11 @@ static void mos7840_set_led_callback(struct urb *urb)
        case -ENOENT:
        case -ESHUTDOWN:
                /* This urb is terminated, clean up */
-               dev_dbg(&urb->dev->dev, "%s - urb shutting down with status: %d",
+               dev_dbg(&urb->dev->dev, "%s - urb shutting down: %d\n",
                        __func__, urb->status);
                break;
        default:
-               dev_dbg(&urb->dev->dev, "%s - nonzero urb status received: %d",
+               dev_dbg(&urb->dev->dev, "%s - nonzero urb status: %d\n",
                        __func__, urb->status);
        }
 }
index 7725ed261ed6abb6de36655b62e48a2c9ca345f7..504f5bff79c0c27d3171de45da2b8678fd0acc03 100644 (file)
@@ -372,7 +372,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
                                 device_port, data, 2, QT2_USB_TIMEOUT);
 
        if (status < 0) {
-               dev_err(&port->dev, "%s - open port failed %i", __func__,
+               dev_err(&port->dev, "%s - open port failed %i\n", __func__,
                        status);
                kfree(data);
                return status;
index 4ec04f73c800f7ce8f2b64ff63c506c8ef113f51..ef0dbf0703c574eb62d1e2ce3be49e5186258aee 100644 (file)
@@ -220,9 +220,9 @@ static int spcp8x5_get_msr(struct usb_serial_port *port, u8 *status)
                              GET_UART_STATUS, GET_UART_STATUS_TYPE,
                              0, GET_UART_STATUS_MSR, buf, 1, 100);
        if (ret < 0)
-               dev_err(&port->dev, "failed to get modem status: %d", ret);
+               dev_err(&port->dev, "failed to get modem status: %d\n", ret);
 
-       dev_dbg(&port->dev, "0xc0:0x22:0:6  %d - 0x02%x", ret, *buf);
+       dev_dbg(&port->dev, "0xc0:0x22:0:6  %d - 0x02%x\n", ret, *buf);
        *status = *buf;
        kfree(buf);
 
@@ -342,8 +342,7 @@ static void spcp8x5_set_termios(struct tty_struct *tty,
        case 1000000:
                        buf[0] = 0x0b;  break;
        default:
-               dev_err(&port->dev, "spcp825 driver does not support the "
-                       "baudrate requested, using default of 9600.\n");
+               dev_err(&port->dev, "unsupported baudrate, using 9600\n");
        }
 
        /* Set Data Length : 00:5bit, 01:6bit, 10:7bit, 11:8bit */
index 9fa7dd413e8361ddef978763dee1f979916063d0..8fceec7298e007919f8585a93e9298d2256e6bb7 100644 (file)
@@ -74,9 +74,7 @@ static void symbol_int_callback(struct urb *urb)
                tty_insert_flip_string(&port->port, &data[1], data_length);
                tty_flip_buffer_push(&port->port);
        } else {
-               dev_dbg(&port->dev,
-                       "Improper amount of data received from the device, "
-                       "%d bytes", urb->actual_length);
+               dev_dbg(&port->dev, "%s - short packet\n", __func__);
        }
 
 exit:
index ec7cea58566327a398e29eef2883ffb1f82c4967..3dd3ff8c50d3b718775ab7d6df9796304129bf49 100644 (file)
@@ -293,7 +293,7 @@ static int ti_startup(struct usb_serial *serial)
        int status;
 
        dev_dbg(&dev->dev,
-               "%s - product 0x%4X, num configurations %d, configuration value %d",
+               "%s - product 0x%4X, num configurations %d, configuration value %d\n",
                __func__, le16_to_cpu(dev->descriptor.idProduct),
                dev->descriptor.bNumConfigurations,
                dev->actconfig->desc.bConfigurationValue);
@@ -803,7 +803,7 @@ static void ti_set_termios(struct tty_struct *tty,
                tty_encode_baud_rate(tty, baud, baud);
 
        dev_dbg(&port->dev,
-               "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d",
+               "%s - BaudRate=%d, wBaudRate=%d, wFlags=0x%04X, bDataBits=%d, bParity=%d, bStopBits=%d, cXon=%d, cXoff=%d, bUartMode=%d\n",
                __func__, baud, config->wBaudRate, config->wFlags,
                config->bDataBits, config->bParity, config->bStopBits,
                config->cXon, config->cXoff, config->bUartMode);
index 7c9dc28640bb9654c9d3b6a6a31e70112ba69692..81fc0dfcfdcf6a073dc408ae7203747b1cd6167b 100644 (file)
@@ -868,7 +868,7 @@ static int usb_serial_probe(struct usb_interface *interface,
        max_endpoints = max(max_endpoints, (int)serial->num_ports);
        serial->num_port_pointers = max_endpoints;
 
-       dev_dbg(ddev, "setting up %d port structures for this device", max_endpoints);
+       dev_dbg(ddev, "setting up %d port structure(s)\n", max_endpoints);
        for (i = 0; i < max_endpoints; ++i) {
                port = kzalloc(sizeof(struct usb_serial_port), GFP_KERNEL);
                if (!port)
@@ -923,9 +923,8 @@ static int usb_serial_probe(struct usb_interface *interface,
                port = serial->port[i];
                if (kfifo_alloc(&port->write_fifo, PAGE_SIZE, GFP_KERNEL))
                        goto probe_error;
-               buffer_size = serial->type->bulk_out_size;
-               if (!buffer_size)
-                       buffer_size = usb_endpoint_maxp(endpoint);
+               buffer_size = max_t(int, serial->type->bulk_out_size,
+                                               usb_endpoint_maxp(endpoint));
                port->bulk_out_size = buffer_size;
                port->bulk_out_endpointAddress = endpoint->bEndpointAddress;
 
@@ -1034,7 +1033,7 @@ static int usb_serial_probe(struct usb_interface *interface,
        for (i = 0; i < num_ports; ++i) {
                port = serial->port[i];
                dev_set_name(&port->dev, "ttyUSB%d", port->minor);
-               dev_dbg(ddev, "registering %s", dev_name(&port->dev));
+               dev_dbg(ddev, "registering %s\n", dev_name(&port->dev));
                device_enable_async_suspend(&port->dev);
 
                retval = device_add(&port->dev);
@@ -1161,9 +1160,9 @@ static int usb_serial_reset_resume(struct usb_interface *intf)
        usb_serial_unpoison_port_urbs(serial);
 
        serial->suspending = 0;
-       if (serial->type->reset_resume)
+       if (serial->type->reset_resume) {
                rv = serial->type->reset_resume(serial);
-       else {
+       else {
                rv = -EOPNOTSUPP;
                intf->needs_binding = 1;
        }
@@ -1338,9 +1337,9 @@ static int usb_serial_register(struct usb_serial_driver *driver)
        if (retval) {
                pr_err("problem %d when registering driver %s\n", retval, driver->description);
                list_del(&driver->driver_list);
-       } else
+       } else {
                pr_info("USB Serial support registered for %s\n", driver->description);
-
+       }
        mutex_unlock(&table_lock);
        return retval;
 }
index 1dd0604d1911d683015bedfe28ab86cdd42e8db4..13b5bfbaf9510b7a5a5a884961f5986bce99cb64 100644 (file)
@@ -204,7 +204,7 @@ config USB_STORAGE_ENE_UB6250
 
 config USB_UAS
        tristate "USB Attached SCSI"
-       depends on SCSI && BROKEN
+       depends on SCSI && USB_STORAGE
        help
          The USB Attached SCSI protocol is supported by some USB
          storage devices.  It permits higher performance by supporting
diff --git a/drivers/usb/storage/uas-detect.h b/drivers/usb/storage/uas-detect.h
new file mode 100644 (file)
index 0000000..bb05b98
--- /dev/null
@@ -0,0 +1,96 @@
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include "usb.h"
+
+static int uas_is_interface(struct usb_host_interface *intf)
+{
+       return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE &&
+               intf->desc.bInterfaceSubClass == USB_SC_SCSI &&
+               intf->desc.bInterfaceProtocol == USB_PR_UAS);
+}
+
+static int uas_isnt_supported(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       dev_warn(&udev->dev, "The driver for the USB controller %s does not "
+                       "support scatter-gather which is\n",
+                       hcd->driver->description);
+       dev_warn(&udev->dev, "required by the UAS driver. Please try an"
+                       "alternative USB controller if you wish to use UAS.\n");
+       return -ENODEV;
+}
+
+static int uas_find_uas_alt_setting(struct usb_interface *intf)
+{
+       int i;
+       struct usb_device *udev = interface_to_usbdev(intf);
+       int sg_supported = udev->bus->sg_tablesize != 0;
+
+       for (i = 0; i < intf->num_altsetting; i++) {
+               struct usb_host_interface *alt = &intf->altsetting[i];
+
+               if (uas_is_interface(alt)) {
+                       if (!sg_supported)
+                               return uas_isnt_supported(udev);
+                       return alt->desc.bAlternateSetting;
+               }
+       }
+
+       return -ENODEV;
+}
+
+static int uas_find_endpoints(struct usb_host_interface *alt,
+                             struct usb_host_endpoint *eps[])
+{
+       struct usb_host_endpoint *endpoint = alt->endpoint;
+       unsigned i, n_endpoints = alt->desc.bNumEndpoints;
+
+       for (i = 0; i < n_endpoints; i++) {
+               unsigned char *extra = endpoint[i].extra;
+               int len = endpoint[i].extralen;
+               while (len >= 3) {
+                       if (extra[1] == USB_DT_PIPE_USAGE) {
+                               unsigned pipe_id = extra[2];
+                               if (pipe_id > 0 && pipe_id < 5)
+                                       eps[pipe_id - 1] = &endpoint[i];
+                               break;
+                       }
+                       len -= extra[0];
+                       extra += extra[0];
+               }
+       }
+
+       if (!eps[0] || !eps[1] || !eps[2] || !eps[3])
+               return -ENODEV;
+
+       return 0;
+}
+
+static int uas_use_uas_driver(struct usb_interface *intf,
+                             const struct usb_device_id *id)
+{
+       struct usb_host_endpoint *eps[4] = { };
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+       unsigned long flags = id->driver_info;
+       int r, alt;
+
+       usb_stor_adjust_quirks(udev, &flags);
+
+       if (flags & US_FL_IGNORE_UAS)
+               return 0;
+
+       if (udev->speed >= USB_SPEED_SUPER && !hcd->can_do_streams)
+               return 0;
+
+       alt = uas_find_uas_alt_setting(intf);
+       if (alt < 0)
+               return 0;
+
+       r = uas_find_endpoints(&intf->altsetting[alt], eps);
+       if (r < 0)
+               return 0;
+
+       return 1;
+}
index d966b59f7d7b264fcd712963e97f487fee563266..a7ac97cc59495f7a7eea8e304d432624185205a9 100644 (file)
@@ -2,6 +2,7 @@
  * USB Attached SCSI
  * Note that this is not the same as the USB Mass Storage driver
  *
+ * Copyright Hans de Goede <hdegoede@redhat.com> for Red Hat, Inc. 2013
  * Copyright Matthew Wilcox for Intel Corp, 2010
  * Copyright Sarah Sharp for Intel Corp, 2010
  *
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/usb.h>
+#include <linux/usb_usual.h>
 #include <linux/usb/hcd.h>
 #include <linux/usb/storage.h>
 #include <linux/usb/uas.h>
 
 #include <scsi/scsi.h>
+#include <scsi/scsi_eh.h>
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
 
+#include "uas-detect.h"
+
 /*
  * The r00-r01c specs define this version of the SENSE IU data structure.
  * It's still in use by several different firmware releases.
@@ -45,12 +50,17 @@ struct uas_dev_info {
        struct usb_anchor sense_urbs;
        struct usb_anchor data_urbs;
        int qdepth, resetting;
-       struct response_ui response;
+       struct response_iu response;
        unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
        unsigned use_streams:1;
        unsigned uas_sense_old:1;
+       unsigned running_task:1;
+       unsigned shutdown:1;
        struct scsi_cmnd *cmnd;
        spinlock_t lock;
+       struct work_struct work;
+       struct list_head inflight_list;
+       struct list_head dead_list;
 };
 
 enum {
@@ -85,103 +95,117 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                                struct uas_dev_info *devinfo, gfp_t gfp);
 static void uas_do_work(struct work_struct *work);
 static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller);
+static void uas_free_streams(struct uas_dev_info *devinfo);
+static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *caller);
 
-static DECLARE_WORK(uas_work, uas_do_work);
-static DEFINE_SPINLOCK(uas_work_lock);
-static LIST_HEAD(uas_work_list);
-
+/* Must be called with devinfo->lock held, will temporary unlock the lock */
 static void uas_unlink_data_urbs(struct uas_dev_info *devinfo,
-                                struct uas_cmd_info *cmdinfo)
+                                struct uas_cmd_info *cmdinfo,
+                                unsigned long *lock_flags)
 {
-       unsigned long flags;
-
        /*
         * The UNLINK_DATA_URBS flag makes sure uas_try_complete
         * (called by urb completion) doesn't release cmdinfo
         * underneath us.
         */
-       spin_lock_irqsave(&devinfo->lock, flags);
        cmdinfo->state |= UNLINK_DATA_URBS;
-       spin_unlock_irqrestore(&devinfo->lock, flags);
+       spin_unlock_irqrestore(&devinfo->lock, *lock_flags);
 
        if (cmdinfo->data_in_urb)
                usb_unlink_urb(cmdinfo->data_in_urb);
        if (cmdinfo->data_out_urb)
                usb_unlink_urb(cmdinfo->data_out_urb);
 
-       spin_lock_irqsave(&devinfo->lock, flags);
+       spin_lock_irqsave(&devinfo->lock, *lock_flags);
        cmdinfo->state &= ~UNLINK_DATA_URBS;
-       spin_unlock_irqrestore(&devinfo->lock, flags);
 }
 
 static void uas_do_work(struct work_struct *work)
 {
+       struct uas_dev_info *devinfo =
+               container_of(work, struct uas_dev_info, work);
        struct uas_cmd_info *cmdinfo;
-       struct uas_cmd_info *temp;
-       struct list_head list;
        unsigned long flags;
        int err;
 
-       spin_lock_irq(&uas_work_lock);
-       list_replace_init(&uas_work_list, &list);
-       spin_unlock_irq(&uas_work_lock);
-
-       list_for_each_entry_safe(cmdinfo, temp, &list, list) {
+       spin_lock_irqsave(&devinfo->lock, flags);
+       list_for_each_entry(cmdinfo, &devinfo->inflight_list, list) {
                struct scsi_pointer *scp = (void *)cmdinfo;
-               struct scsi_cmnd *cmnd = container_of(scp,
-                                                       struct scsi_cmnd, SCp);
-               struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
-               spin_lock_irqsave(&devinfo->lock, flags);
-               err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
+               struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd,
+                                                     SCp);
+
+               if (!(cmdinfo->state & IS_IN_WORK_LIST))
+                       continue;
+
+               err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
                if (!err)
                        cmdinfo->state &= ~IS_IN_WORK_LIST;
-               spin_unlock_irqrestore(&devinfo->lock, flags);
-               if (err) {
-                       list_del(&cmdinfo->list);
-                       spin_lock_irq(&uas_work_lock);
-                       list_add_tail(&cmdinfo->list, &uas_work_list);
-                       spin_unlock_irq(&uas_work_lock);
-                       schedule_work(&uas_work);
-               }
+               else
+                       schedule_work(&devinfo->work);
        }
+       spin_unlock_irqrestore(&devinfo->lock, flags);
 }
 
-static void uas_abort_work(struct uas_dev_info *devinfo)
+static void uas_mark_cmd_dead(struct uas_dev_info *devinfo,
+                             struct uas_cmd_info *cmdinfo,
+                             int result, const char *caller)
+{
+       struct scsi_pointer *scp = (void *)cmdinfo;
+       struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp);
+
+       uas_log_cmd_state(cmnd, caller);
+       WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));
+       WARN_ON_ONCE(cmdinfo->state & COMMAND_ABORTED);
+       cmdinfo->state |= COMMAND_ABORTED;
+       cmdinfo->state &= ~IS_IN_WORK_LIST;
+       cmnd->result = result << 16;
+       list_move_tail(&cmdinfo->list, &devinfo->dead_list);
+}
+
+static void uas_abort_inflight(struct uas_dev_info *devinfo, int result,
+                              const char *caller)
 {
        struct uas_cmd_info *cmdinfo;
        struct uas_cmd_info *temp;
-       struct list_head list;
        unsigned long flags;
 
-       spin_lock_irq(&uas_work_lock);
-       list_replace_init(&uas_work_list, &list);
-       spin_unlock_irq(&uas_work_lock);
+       spin_lock_irqsave(&devinfo->lock, flags);
+       list_for_each_entry_safe(cmdinfo, temp, &devinfo->inflight_list, list)
+               uas_mark_cmd_dead(devinfo, cmdinfo, result, caller);
+       spin_unlock_irqrestore(&devinfo->lock, flags);
+}
+
+static void uas_add_work(struct uas_cmd_info *cmdinfo)
+{
+       struct scsi_pointer *scp = (void *)cmdinfo;
+       struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd, SCp);
+       struct uas_dev_info *devinfo = cmnd->device->hostdata;
+
+       WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));
+       cmdinfo->state |= IS_IN_WORK_LIST;
+       schedule_work(&devinfo->work);
+}
+
+static void uas_zap_dead(struct uas_dev_info *devinfo)
+{
+       struct uas_cmd_info *cmdinfo;
+       struct uas_cmd_info *temp;
+       unsigned long flags;
 
        spin_lock_irqsave(&devinfo->lock, flags);
-       list_for_each_entry_safe(cmdinfo, temp, &list, list) {
+       list_for_each_entry_safe(cmdinfo, temp, &devinfo->dead_list, list) {
                struct scsi_pointer *scp = (void *)cmdinfo;
-               struct scsi_cmnd *cmnd = container_of(scp,
-                                                       struct scsi_cmnd, SCp);
-               struct uas_dev_info *di = (void *)cmnd->device->hostdata;
-
-               if (di == devinfo) {
-                       cmdinfo->state |= COMMAND_ABORTED;
-                       cmdinfo->state &= ~IS_IN_WORK_LIST;
-                       if (devinfo->resetting) {
-                               /* uas_stat_cmplt() will not do that
-                                * when a device reset is in
-                                * progress */
-                               cmdinfo->state &= ~COMMAND_INFLIGHT;
-                       }
-                       uas_try_complete(cmnd, __func__);
-               } else {
-                       /* not our uas device, relink into list */
-                       list_del(&cmdinfo->list);
-                       spin_lock_irq(&uas_work_lock);
-                       list_add_tail(&cmdinfo->list, &uas_work_list);
-                       spin_unlock_irq(&uas_work_lock);
-               }
+               struct scsi_cmnd *cmnd = container_of(scp, struct scsi_cmnd,
+                                                     SCp);
+               uas_log_cmd_state(cmnd, __func__);
+               WARN_ON_ONCE(!(cmdinfo->state & COMMAND_ABORTED));
+               /* all urbs are killed, clear inflight bits */
+               cmdinfo->state &= ~(COMMAND_INFLIGHT |
+                                   DATA_IN_URB_INFLIGHT |
+                                   DATA_OUT_URB_INFLIGHT);
+               uas_try_complete(cmnd, __func__);
        }
+       devinfo->running_task = 0;
        spin_unlock_irqrestore(&devinfo->lock, flags);
 }
 
@@ -259,20 +283,19 @@ static int uas_try_complete(struct scsi_cmnd *cmnd, const char *caller)
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
        struct uas_dev_info *devinfo = (void *)cmnd->device->hostdata;
 
-       WARN_ON(!spin_is_locked(&devinfo->lock));
+       WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));
        if (cmdinfo->state & (COMMAND_INFLIGHT |
                              DATA_IN_URB_INFLIGHT |
                              DATA_OUT_URB_INFLIGHT |
                              UNLINK_DATA_URBS))
                return -EBUSY;
-       BUG_ON(cmdinfo->state & COMMAND_COMPLETED);
+       WARN_ON_ONCE(cmdinfo->state & COMMAND_COMPLETED);
        cmdinfo->state |= COMMAND_COMPLETED;
        usb_free_urb(cmdinfo->data_in_urb);
        usb_free_urb(cmdinfo->data_out_urb);
-       if (cmdinfo->state & COMMAND_ABORTED) {
+       if (cmdinfo->state & COMMAND_ABORTED)
                scmd_printk(KERN_INFO, cmnd, "abort completed\n");
-               cmnd->result = DID_ABORT << 16;
-       }
+       list_del(&cmdinfo->list);
        cmnd->scsi_done(cmnd);
        return 0;
 }
@@ -286,11 +309,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
        cmdinfo->state |= direction | SUBMIT_STATUS_URB;
        err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
        if (err) {
-               spin_lock(&uas_work_lock);
-               list_add_tail(&cmdinfo->list, &uas_work_list);
-               cmdinfo->state |= IS_IN_WORK_LIST;
-               spin_unlock(&uas_work_lock);
-               schedule_work(&uas_work);
+               uas_add_work(cmdinfo);
        }
 }
 
@@ -298,14 +317,20 @@ static void uas_stat_cmplt(struct urb *urb)
 {
        struct iu *iu = urb->transfer_buffer;
        struct Scsi_Host *shost = urb->context;
-       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
        struct scsi_cmnd *cmnd;
        struct uas_cmd_info *cmdinfo;
        unsigned long flags;
        u16 tag;
 
        if (urb->status) {
-               dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
+               if (urb->status == -ENOENT) {
+                       dev_err(&urb->dev->dev, "stat urb: killed, stream %d\n",
+                               urb->stream_id);
+               } else {
+                       dev_err(&urb->dev->dev, "stat urb: status %d\n",
+                               urb->status);
+               }
                usb_free_urb(urb);
                return;
        }
@@ -324,6 +349,9 @@ static void uas_stat_cmplt(struct urb *urb)
 
        if (!cmnd) {
                if (iu->iu_id == IU_ID_RESPONSE) {
+                       if (!devinfo->running_task)
+                               dev_warn(&urb->dev->dev,
+                                   "stat urb: recv unexpected response iu\n");
                        /* store results for uas_eh_task_mgmt() */
                        memcpy(&devinfo->response, iu, sizeof(devinfo->response));
                }
@@ -346,17 +374,25 @@ static void uas_stat_cmplt(struct urb *urb)
                        uas_sense(urb, cmnd);
                if (cmnd->result != 0) {
                        /* cancel data transfers on error */
-                       spin_unlock_irqrestore(&devinfo->lock, flags);
-                       uas_unlink_data_urbs(devinfo, cmdinfo);
-                       spin_lock_irqsave(&devinfo->lock, flags);
+                       uas_unlink_data_urbs(devinfo, cmdinfo, &flags);
                }
                cmdinfo->state &= ~COMMAND_INFLIGHT;
                uas_try_complete(cmnd, __func__);
                break;
        case IU_ID_READ_READY:
+               if (!cmdinfo->data_in_urb ||
+                               (cmdinfo->state & DATA_IN_URB_INFLIGHT)) {
+                       scmd_printk(KERN_ERR, cmnd, "unexpected read rdy\n");
+                       break;
+               }
                uas_xfer_data(urb, cmnd, SUBMIT_DATA_IN_URB);
                break;
        case IU_ID_WRITE_READY:
+               if (!cmdinfo->data_out_urb ||
+                               (cmdinfo->state & DATA_OUT_URB_INFLIGHT)) {
+                       scmd_printk(KERN_ERR, cmnd, "unexpected write rdy\n");
+                       break;
+               }
                uas_xfer_data(urb, cmnd, SUBMIT_DATA_OUT_URB);
                break;
        default:
@@ -383,8 +419,15 @@ static void uas_data_cmplt(struct urb *urb)
                sdb = scsi_out(cmnd);
                cmdinfo->state &= ~DATA_OUT_URB_INFLIGHT;
        }
-       BUG_ON(sdb == NULL);
-       if (urb->status) {
+       if (sdb == NULL) {
+               WARN_ON_ONCE(1);
+       } else if (urb->status) {
+               if (urb->status != -ECONNRESET) {
+                       uas_log_cmd_state(cmnd, __func__);
+                       scmd_printk(KERN_ERR, cmnd,
+                               "data cmplt err %d stream %d\n",
+                               urb->status, urb->stream_id);
+               }
                /* error: no data transfered */
                sdb->resid = sdb->length;
        } else {
@@ -394,6 +437,17 @@ static void uas_data_cmplt(struct urb *urb)
        spin_unlock_irqrestore(&devinfo->lock, flags);
 }
 
+static void uas_cmd_cmplt(struct urb *urb)
+{
+       struct scsi_cmnd *cmnd = urb->context;
+
+       if (urb->status) {
+               uas_log_cmd_state(cmnd, __func__);
+               scmd_printk(KERN_ERR, cmnd, "cmd cmplt err %d\n", urb->status);
+       }
+       usb_free_urb(urb);
+}
+
 static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
                                      unsigned int pipe, u16 stream_id,
                                      struct scsi_cmnd *cmnd,
@@ -408,8 +462,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
                goto out;
        usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,
                          uas_data_cmplt, cmnd);
-       if (devinfo->use_streams)
-               urb->stream_id = stream_id;
+       urb->stream_id = stream_id;
        urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
        urb->sg = sdb->table.sgl;
  out:
@@ -442,7 +495,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
 }
 
 static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
-                                       struct scsi_cmnd *cmnd, u16 stream_id)
+                                       struct scsi_cmnd *cmnd)
 {
        struct usb_device *udev = devinfo->udev;
        struct scsi_device *sdev = cmnd->device;
@@ -472,7 +525,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
        memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len);
 
        usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu) + len,
-                                                       usb_free_urb, NULL);
+                                                       uas_cmd_cmplt, cmnd);
        urb->transfer_flags |= URB_FREE_BUFFER;
  out:
        return urb;
@@ -512,13 +565,17 @@ static int uas_submit_task_urb(struct scsi_cmnd *cmnd, gfp_t gfp,
        }
 
        usb_fill_bulk_urb(urb, udev, devinfo->cmd_pipe, iu, sizeof(*iu),
-                         usb_free_urb, NULL);
+                         uas_cmd_cmplt, cmnd);
        urb->transfer_flags |= URB_FREE_BUFFER;
 
+       usb_anchor_urb(urb, &devinfo->cmd_urbs);
        err = usb_submit_urb(urb, gfp);
-       if (err)
+       if (err) {
+               usb_unanchor_urb(urb);
+               uas_log_cmd_state(cmnd, __func__);
+               scmd_printk(KERN_ERR, cmnd, "task submission err %d\n", err);
                goto err;
-       usb_anchor_urb(urb, &devinfo->cmd_urbs);
+       }
 
        return 0;
 
@@ -533,38 +590,43 @@ err:
  * daft to me.
  */
 
-static int uas_submit_sense_urb(struct Scsi_Host *shost,
-                               gfp_t gfp, unsigned int stream)
+static struct urb *uas_submit_sense_urb(struct scsi_cmnd *cmnd,
+                                       gfp_t gfp, unsigned int stream)
 {
-       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
        struct urb *urb;
+       int err;
 
        urb = uas_alloc_sense_urb(devinfo, gfp, shost, stream);
        if (!urb)
-               return SCSI_MLQUEUE_DEVICE_BUSY;
-       if (usb_submit_urb(urb, gfp)) {
+               return NULL;
+       usb_anchor_urb(urb, &devinfo->sense_urbs);
+       err = usb_submit_urb(urb, gfp);
+       if (err) {
+               usb_unanchor_urb(urb);
+               uas_log_cmd_state(cmnd, __func__);
                shost_printk(KERN_INFO, shost,
-                            "sense urb submission failure\n");
+                            "sense urb submission error %d stream %d\n",
+                            err, stream);
                usb_free_urb(urb);
-               return SCSI_MLQUEUE_DEVICE_BUSY;
+               return NULL;
        }
-       usb_anchor_urb(urb, &devinfo->sense_urbs);
-       return 0;
+       return urb;
 }
 
 static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                           struct uas_dev_info *devinfo, gfp_t gfp)
 {
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
+       struct urb *urb;
        int err;
 
-       WARN_ON(!spin_is_locked(&devinfo->lock));
+       WARN_ON_ONCE(!spin_is_locked(&devinfo->lock));
        if (cmdinfo->state & SUBMIT_STATUS_URB) {
-               err = uas_submit_sense_urb(cmnd->device->host, gfp,
-                                          cmdinfo->stream);
-               if (err) {
-                       return err;
-               }
+               urb = uas_submit_sense_urb(cmnd, gfp, cmdinfo->stream);
+               if (!urb)
+                       return SCSI_MLQUEUE_DEVICE_BUSY;
                cmdinfo->state &= ~SUBMIT_STATUS_URB;
        }
 
@@ -578,14 +640,18 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
        }
 
        if (cmdinfo->state & SUBMIT_DATA_IN_URB) {
-               if (usb_submit_urb(cmdinfo->data_in_urb, gfp)) {
+               usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs);
+               err = usb_submit_urb(cmdinfo->data_in_urb, gfp);
+               if (err) {
+                       usb_unanchor_urb(cmdinfo->data_in_urb);
+                       uas_log_cmd_state(cmnd, __func__);
                        scmd_printk(KERN_INFO, cmnd,
-                                       "data in urb submission failure\n");
+                               "data in urb submission error %d stream %d\n",
+                               err, cmdinfo->data_in_urb->stream_id);
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                cmdinfo->state &= ~SUBMIT_DATA_IN_URB;
                cmdinfo->state |= DATA_IN_URB_INFLIGHT;
-               usb_anchor_urb(cmdinfo->data_in_urb, &devinfo->data_urbs);
        }
 
        if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
@@ -598,33 +664,37 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
        }
 
        if (cmdinfo->state & SUBMIT_DATA_OUT_URB) {
-               if (usb_submit_urb(cmdinfo->data_out_urb, gfp)) {
+               usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs);
+               err = usb_submit_urb(cmdinfo->data_out_urb, gfp);
+               if (err) {
+                       usb_unanchor_urb(cmdinfo->data_out_urb);
+                       uas_log_cmd_state(cmnd, __func__);
                        scmd_printk(KERN_INFO, cmnd,
-                                       "data out urb submission failure\n");
+                               "data out urb submission error %d stream %d\n",
+                               err, cmdinfo->data_out_urb->stream_id);
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
                cmdinfo->state &= ~SUBMIT_DATA_OUT_URB;
                cmdinfo->state |= DATA_OUT_URB_INFLIGHT;
-               usb_anchor_urb(cmdinfo->data_out_urb, &devinfo->data_urbs);
        }
 
        if (cmdinfo->state & ALLOC_CMD_URB) {
-               cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd,
-                                                    cmdinfo->stream);
+               cmdinfo->cmd_urb = uas_alloc_cmd_urb(devinfo, gfp, cmnd);
                if (!cmdinfo->cmd_urb)
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                cmdinfo->state &= ~ALLOC_CMD_URB;
        }
 
        if (cmdinfo->state & SUBMIT_CMD_URB) {
-               usb_get_urb(cmdinfo->cmd_urb);
-               if (usb_submit_urb(cmdinfo->cmd_urb, gfp)) {
+               usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs);
+               err = usb_submit_urb(cmdinfo->cmd_urb, gfp);
+               if (err) {
+                       usb_unanchor_urb(cmdinfo->cmd_urb);
+                       uas_log_cmd_state(cmnd, __func__);
                        scmd_printk(KERN_INFO, cmnd,
-                                       "cmd urb submission failure\n");
+                                   "cmd urb submission error %d\n", err);
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
-               usb_anchor_urb(cmdinfo->cmd_urb, &devinfo->cmd_urbs);
-               usb_put_urb(cmdinfo->cmd_urb);
                cmdinfo->cmd_urb = NULL;
                cmdinfo->state &= ~SUBMIT_CMD_URB;
                cmdinfo->state |= COMMAND_INFLIGHT;
@@ -644,18 +714,22 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
 
        BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
 
+       spin_lock_irqsave(&devinfo->lock, flags);
+
        if (devinfo->resetting) {
                cmnd->result = DID_ERROR << 16;
                cmnd->scsi_done(cmnd);
+               spin_unlock_irqrestore(&devinfo->lock, flags);
                return 0;
        }
 
-       spin_lock_irqsave(&devinfo->lock, flags);
        if (devinfo->cmnd) {
                spin_unlock_irqrestore(&devinfo->lock, flags);
                return SCSI_MLQUEUE_DEVICE_BUSY;
        }
 
+       memset(cmdinfo, 0, sizeof(*cmdinfo));
+
        if (blk_rq_tagged(cmnd->request)) {
                cmdinfo->stream = cmnd->request->tag + 2;
        } else {
@@ -692,13 +766,10 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
                        spin_unlock_irqrestore(&devinfo->lock, flags);
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                }
-               spin_lock(&uas_work_lock);
-               list_add_tail(&cmdinfo->list, &uas_work_list);
-               cmdinfo->state |= IS_IN_WORK_LIST;
-               spin_unlock(&uas_work_lock);
-               schedule_work(&uas_work);
+               uas_add_work(cmdinfo);
        }
 
+       list_add_tail(&cmdinfo->list, &devinfo->inflight_list);
        spin_unlock_irqrestore(&devinfo->lock, flags);
        return 0;
 }
@@ -709,46 +780,78 @@ static int uas_eh_task_mgmt(struct scsi_cmnd *cmnd,
                            const char *fname, u8 function)
 {
        struct Scsi_Host *shost = cmnd->device->host;
-       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
-       u16 tag = devinfo->qdepth - 1;
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
+       u16 tag = devinfo->qdepth;
        unsigned long flags;
+       struct urb *sense_urb;
+       int result = SUCCESS;
 
        spin_lock_irqsave(&devinfo->lock, flags);
+
+       if (devinfo->resetting) {
+               spin_unlock_irqrestore(&devinfo->lock, flags);
+               return FAILED;
+       }
+
+       if (devinfo->running_task) {
+               shost_printk(KERN_INFO, shost,
+                            "%s: %s: error already running a task\n",
+                            __func__, fname);
+               spin_unlock_irqrestore(&devinfo->lock, flags);
+               return FAILED;
+       }
+
+       devinfo->running_task = 1;
        memset(&devinfo->response, 0, sizeof(devinfo->response));
-       if (uas_submit_sense_urb(shost, GFP_ATOMIC, tag)) {
+       sense_urb = uas_submit_sense_urb(cmnd, GFP_NOIO,
+                                        devinfo->use_streams ? tag : 0);
+       if (!sense_urb) {
                shost_printk(KERN_INFO, shost,
                             "%s: %s: submit sense urb failed\n",
                             __func__, fname);
+               devinfo->running_task = 0;
                spin_unlock_irqrestore(&devinfo->lock, flags);
                return FAILED;
        }
-       if (uas_submit_task_urb(cmnd, GFP_ATOMIC, function, tag)) {
+       if (uas_submit_task_urb(cmnd, GFP_NOIO, function, tag)) {
                shost_printk(KERN_INFO, shost,
                             "%s: %s: submit task mgmt urb failed\n",
                             __func__, fname);
+               devinfo->running_task = 0;
                spin_unlock_irqrestore(&devinfo->lock, flags);
+               usb_kill_urb(sense_urb);
                return FAILED;
        }
        spin_unlock_irqrestore(&devinfo->lock, flags);
 
        if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 3000) == 0) {
+               /*
+                * Note we deliberately do not clear running_task here. If we
+                * allow new tasks to be submitted, there is no way to figure
+                * out if a received response_iu is for the failed task or for
+                * the new one. A bus-reset will eventually clear running_task.
+                */
                shost_printk(KERN_INFO, shost,
                             "%s: %s timed out\n", __func__, fname);
                return FAILED;
        }
+
+       spin_lock_irqsave(&devinfo->lock, flags);
+       devinfo->running_task = 0;
        if (be16_to_cpu(devinfo->response.tag) != tag) {
                shost_printk(KERN_INFO, shost,
                             "%s: %s failed (wrong tag %d/%d)\n", __func__,
                             fname, be16_to_cpu(devinfo->response.tag), tag);
-               return FAILED;
-       }
-       if (devinfo->response.response_code != RC_TMF_COMPLETE) {
+               result = FAILED;
+       } else if (devinfo->response.response_code != RC_TMF_COMPLETE) {
                shost_printk(KERN_INFO, shost,
                             "%s: %s failed (rc 0x%x)\n", __func__,
                             fname, devinfo->response.response_code);
-               return FAILED;
+               result = FAILED;
        }
-       return SUCCESS;
+       spin_unlock_irqrestore(&devinfo->lock, flags);
+
+       return result;
 }
 
 static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
@@ -758,22 +861,19 @@ static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
        unsigned long flags;
        int ret;
 
-       uas_log_cmd_state(cmnd, __func__);
        spin_lock_irqsave(&devinfo->lock, flags);
-       cmdinfo->state |= COMMAND_ABORTED;
-       if (cmdinfo->state & IS_IN_WORK_LIST) {
-               spin_lock(&uas_work_lock);
-               list_del(&cmdinfo->list);
-               cmdinfo->state &= ~IS_IN_WORK_LIST;
-               spin_unlock(&uas_work_lock);
+
+       if (devinfo->resetting) {
+               spin_unlock_irqrestore(&devinfo->lock, flags);
+               return FAILED;
        }
+
+       uas_mark_cmd_dead(devinfo, cmdinfo, DID_ABORT, __func__);
        if (cmdinfo->state & COMMAND_INFLIGHT) {
                spin_unlock_irqrestore(&devinfo->lock, flags);
                ret = uas_eh_task_mgmt(cmnd, "ABORT TASK", TMF_ABORT_TASK);
        } else {
-               spin_unlock_irqrestore(&devinfo->lock, flags);
-               uas_unlink_data_urbs(devinfo, cmdinfo);
-               spin_lock_irqsave(&devinfo->lock, flags);
+               uas_unlink_data_urbs(devinfo, cmdinfo, &flags);
                uas_try_complete(cmnd, __func__);
                spin_unlock_irqrestore(&devinfo->lock, flags);
                ret = SUCCESS;
@@ -795,14 +895,25 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
        struct usb_device *udev = devinfo->udev;
        int err;
 
+       err = usb_lock_device_for_reset(udev, devinfo->intf);
+       if (err) {
+               shost_printk(KERN_ERR, sdev->host,
+                            "%s FAILED to get lock err %d\n", __func__, err);
+               return FAILED;
+       }
+
+       shost_printk(KERN_INFO, sdev->host, "%s start\n", __func__);
        devinfo->resetting = 1;
-       uas_abort_work(devinfo);
+       uas_abort_inflight(devinfo, DID_RESET, __func__);
        usb_kill_anchored_urbs(&devinfo->cmd_urbs);
        usb_kill_anchored_urbs(&devinfo->sense_urbs);
        usb_kill_anchored_urbs(&devinfo->data_urbs);
+       uas_zap_dead(devinfo);
        err = usb_reset_device(udev);
        devinfo->resetting = 0;
 
+       usb_unlock_device(udev);
+
        if (err) {
                shost_printk(KERN_INFO, sdev->host, "%s FAILED\n", __func__);
                return FAILED;
@@ -814,7 +925,25 @@ static int uas_eh_bus_reset_handler(struct scsi_cmnd *cmnd)
 
 static int uas_slave_alloc(struct scsi_device *sdev)
 {
-       sdev->hostdata = (void *)sdev->host->hostdata[0];
+       sdev->hostdata = (void *)sdev->host->hostdata;
+
+       /* USB has unusual DMA-alignment requirements: Although the
+        * starting address of each scatter-gather element doesn't matter,
+        * the length of each element except the last must be divisible
+        * by the Bulk maxpacket value.  There's currently no way to
+        * express this by block-layer constraints, so we'll cop out
+        * and simply require addresses to be aligned at 512-byte
+        * boundaries.  This is okay since most block I/O involves
+        * hardware sectors that are multiples of 512 bytes in length,
+        * and since host controllers up through USB 2.0 have maxpacket
+        * values no larger than 512.
+        *
+        * But it doesn't suffice for Wireless USB, where Bulk maxpacket
+        * values can be as large as 2048.  To make that work properly
+        * will require changes to the block layer.
+        */
+       blk_queue_update_dma_alignment(sdev->request_queue, (512 - 1));
+
        return 0;
 }
 
@@ -822,7 +951,7 @@ static int uas_slave_configure(struct scsi_device *sdev)
 {
        struct uas_dev_info *devinfo = sdev->hostdata;
        scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
-       scsi_activate_tcq(sdev, devinfo->qdepth - 3);
+       scsi_activate_tcq(sdev, devinfo->qdepth - 2);
        return 0;
 }
 
@@ -843,7 +972,14 @@ static struct scsi_host_template uas_host_template = {
        .ordered_tag = 1,
 };
 
+#define UNUSUAL_DEV(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax, \
+                   vendorName, productName, useProtocol, useTransport, \
+                   initFunction, flags) \
+{ USB_DEVICE_VER(id_vendor, id_product, bcdDeviceMin, bcdDeviceMax), \
+       .driver_info = (flags) }
+
 static struct usb_device_id uas_usb_ids[] = {
+#      include "unusual_uas.h"
        { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_BULK) },
        { USB_INTERFACE_INFO(USB_CLASS_MASS_STORAGE, USB_SC_SCSI, USB_PR_UAS) },
        /* 0xaa is a prototype device I happen to have access to */
@@ -852,105 +988,55 @@ static struct usb_device_id uas_usb_ids[] = {
 };
 MODULE_DEVICE_TABLE(usb, uas_usb_ids);
 
-static int uas_is_interface(struct usb_host_interface *intf)
-{
-       return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE &&
-               intf->desc.bInterfaceSubClass == USB_SC_SCSI &&
-               intf->desc.bInterfaceProtocol == USB_PR_UAS);
-}
-
-static int uas_isnt_supported(struct usb_device *udev)
-{
-       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
-
-       dev_warn(&udev->dev, "The driver for the USB controller %s does not "
-                       "support scatter-gather which is\n",
-                       hcd->driver->description);
-       dev_warn(&udev->dev, "required by the UAS driver. Please try an"
-                       "alternative USB controller if you wish to use UAS.\n");
-       return -ENODEV;
-}
+#undef UNUSUAL_DEV
 
 static int uas_switch_interface(struct usb_device *udev,
-                                               struct usb_interface *intf)
+                               struct usb_interface *intf)
 {
-       int i;
-       int sg_supported = udev->bus->sg_tablesize != 0;
-
-       for (i = 0; i < intf->num_altsetting; i++) {
-               struct usb_host_interface *alt = &intf->altsetting[i];
-
-               if (uas_is_interface(alt)) {
-                       if (!sg_supported)
-                               return uas_isnt_supported(udev);
-                       return usb_set_interface(udev,
-                                               alt->desc.bInterfaceNumber,
-                                               alt->desc.bAlternateSetting);
-               }
-       }
+       int alt;
+
+       alt = uas_find_uas_alt_setting(intf);
+       if (alt < 0)
+               return alt;
 
-       return -ENODEV;
+       return usb_set_interface(udev,
+                       intf->altsetting[0].desc.bInterfaceNumber, alt);
 }
 
-static void uas_configure_endpoints(struct uas_dev_info *devinfo)
+static int uas_configure_endpoints(struct uas_dev_info *devinfo)
 {
        struct usb_host_endpoint *eps[4] = { };
-       struct usb_interface *intf = devinfo->intf;
        struct usb_device *udev = devinfo->udev;
-       struct usb_host_endpoint *endpoint = intf->cur_altsetting->endpoint;
-       unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints;
+       int r;
 
        devinfo->uas_sense_old = 0;
        devinfo->cmnd = NULL;
 
-       for (i = 0; i < n_endpoints; i++) {
-               unsigned char *extra = endpoint[i].extra;
-               int len = endpoint[i].extralen;
-               while (len > 1) {
-                       if (extra[1] == USB_DT_PIPE_USAGE) {
-                               unsigned pipe_id = extra[2];
-                               if (pipe_id > 0 && pipe_id < 5)
-                                       eps[pipe_id - 1] = &endpoint[i];
-                               break;
-                       }
-                       len -= extra[0];
-                       extra += extra[0];
-               }
-       }
+       r = uas_find_endpoints(devinfo->intf->cur_altsetting, eps);
+       if (r)
+               return r;
 
-       /*
-        * Assume that if we didn't find a control pipe descriptor, we're
-        * using a device with old firmware that happens to be set up like
-        * this.
-        */
-       if (!eps[0]) {
-               devinfo->cmd_pipe = usb_sndbulkpipe(udev, 1);
-               devinfo->status_pipe = usb_rcvbulkpipe(udev, 1);
-               devinfo->data_in_pipe = usb_rcvbulkpipe(udev, 2);
-               devinfo->data_out_pipe = usb_sndbulkpipe(udev, 2);
-
-               eps[1] = usb_pipe_endpoint(udev, devinfo->status_pipe);
-               eps[2] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
-               eps[3] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
-       } else {
-               devinfo->cmd_pipe = usb_sndbulkpipe(udev,
-                                               eps[0]->desc.bEndpointAddress);
-               devinfo->status_pipe = usb_rcvbulkpipe(udev,
-                                               eps[1]->desc.bEndpointAddress);
-               devinfo->data_in_pipe = usb_rcvbulkpipe(udev,
-                                               eps[2]->desc.bEndpointAddress);
-               devinfo->data_out_pipe = usb_sndbulkpipe(udev,
-                                               eps[3]->desc.bEndpointAddress);
-       }
+       devinfo->cmd_pipe = usb_sndbulkpipe(udev,
+                                           usb_endpoint_num(&eps[0]->desc));
+       devinfo->status_pipe = usb_rcvbulkpipe(udev,
+                                           usb_endpoint_num(&eps[1]->desc));
+       devinfo->data_in_pipe = usb_rcvbulkpipe(udev,
+                                           usb_endpoint_num(&eps[2]->desc));
+       devinfo->data_out_pipe = usb_sndbulkpipe(udev,
+                                           usb_endpoint_num(&eps[3]->desc));
 
-       devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1, 3, 256,
-                                                               GFP_KERNEL);
-       if (devinfo->qdepth < 0) {
+       if (udev->speed != USB_SPEED_SUPER) {
                devinfo->qdepth = 256;
                devinfo->use_streams = 0;
        } else {
+               devinfo->qdepth = usb_alloc_streams(devinfo->intf, eps + 1,
+                                                   3, 256, GFP_KERNEL);
+               if (devinfo->qdepth < 0)
+                       return devinfo->qdepth;
                devinfo->use_streams = 1;
        }
+
+       return 0;
 }
 
 static void uas_free_streams(struct uas_dev_info *devinfo)
@@ -964,30 +1050,23 @@ static void uas_free_streams(struct uas_dev_info *devinfo)
        usb_free_streams(devinfo->intf, eps, 3, GFP_KERNEL);
 }
 
-/*
- * XXX: What I'd like to do here is register a SCSI host for each USB host in
- * the system.  Follow usb-storage's design of registering a SCSI host for
- * each USB device for the moment.  Can implement this by walking up the
- * USB hierarchy until we find a USB host.
- */
 static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
-       int result;
-       struct Scsi_Host *shost;
+       int result = -ENOMEM;
+       struct Scsi_Host *shost = NULL;
        struct uas_dev_info *devinfo;
        struct usb_device *udev = interface_to_usbdev(intf);
 
-       if (uas_switch_interface(udev, intf))
+       if (!uas_use_uas_driver(intf, id))
                return -ENODEV;
 
-       devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL);
-       if (!devinfo)
-               return -ENOMEM;
+       if (uas_switch_interface(udev, intf))
+               return -ENODEV;
 
-       result = -ENOMEM;
-       shost = scsi_host_alloc(&uas_host_template, sizeof(void *));
+       shost = scsi_host_alloc(&uas_host_template,
+                               sizeof(struct uas_dev_info));
        if (!shost)
-               goto free;
+               goto set_alt0;
 
        shost->max_cmd_len = 16 + 252;
        shost->max_id = 1;
@@ -995,33 +1074,40 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
        shost->max_channel = 0;
        shost->sg_tablesize = udev->bus->sg_tablesize;
 
+       devinfo = (struct uas_dev_info *)shost->hostdata;
        devinfo->intf = intf;
        devinfo->udev = udev;
        devinfo->resetting = 0;
+       devinfo->running_task = 0;
+       devinfo->shutdown = 0;
        init_usb_anchor(&devinfo->cmd_urbs);
        init_usb_anchor(&devinfo->sense_urbs);
        init_usb_anchor(&devinfo->data_urbs);
        spin_lock_init(&devinfo->lock);
-       uas_configure_endpoints(devinfo);
+       INIT_WORK(&devinfo->work, uas_do_work);
+       INIT_LIST_HEAD(&devinfo->inflight_list);
+       INIT_LIST_HEAD(&devinfo->dead_list);
 
-       result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 3);
+       result = uas_configure_endpoints(devinfo);
        if (result)
-               goto free;
+               goto set_alt0;
 
-       result = scsi_add_host(shost, &intf->dev);
+       result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);
        if (result)
-               goto deconfig_eps;
+               goto free_streams;
 
-       shost->hostdata[0] = (unsigned long)devinfo;
+       result = scsi_add_host(shost, &intf->dev);
+       if (result)
+               goto free_streams;
 
        scsi_scan_host(shost);
        usb_set_intfdata(intf, shost);
        return result;
 
-deconfig_eps:
+free_streams:
        uas_free_streams(devinfo);
- free:
-       kfree(devinfo);
+set_alt0:
+       usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
        if (shost)
                scsi_host_put(shost);
        return result;
@@ -1029,45 +1115,146 @@ deconfig_eps:
 
 static int uas_pre_reset(struct usb_interface *intf)
 {
-/* XXX: Need to return 1 if it's not our device in error handling */
+       struct Scsi_Host *shost = usb_get_intfdata(intf);
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
+       unsigned long flags;
+
+       if (devinfo->shutdown)
+               return 0;
+
+       /* Block new requests */
+       spin_lock_irqsave(shost->host_lock, flags);
+       scsi_block_requests(shost);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       /* Wait for any pending requests to complete */
+       flush_work(&devinfo->work);
+       if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) {
+               shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__);
+               return 1;
+       }
+
+       uas_free_streams(devinfo);
+
        return 0;
 }
 
 static int uas_post_reset(struct usb_interface *intf)
 {
-/* XXX: Need to return 1 if it's not our device in error handling */
+       struct Scsi_Host *shost = usb_get_intfdata(intf);
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
+       unsigned long flags;
+
+       if (devinfo->shutdown)
+               return 0;
+
+       if (uas_configure_endpoints(devinfo) != 0) {
+               shost_printk(KERN_ERR, shost,
+                            "%s: alloc streams error after reset", __func__);
+               return 1;
+       }
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       scsi_report_bus_reset(shost, 0);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       scsi_unblock_requests(shost);
+
+       return 0;
+}
+
+static int uas_suspend(struct usb_interface *intf, pm_message_t message)
+{
+       struct Scsi_Host *shost = usb_get_intfdata(intf);
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
+
+       /* Wait for any pending requests to complete */
+       flush_work(&devinfo->work);
+       if (usb_wait_anchor_empty_timeout(&devinfo->sense_urbs, 5000) == 0) {
+               shost_printk(KERN_ERR, shost, "%s: timed out\n", __func__);
+               return -ETIME;
+       }
+
+       return 0;
+}
+
+static int uas_resume(struct usb_interface *intf)
+{
+       return 0;
+}
+
+static int uas_reset_resume(struct usb_interface *intf)
+{
+       struct Scsi_Host *shost = usb_get_intfdata(intf);
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
+       unsigned long flags;
+
+       if (uas_configure_endpoints(devinfo) != 0) {
+               shost_printk(KERN_ERR, shost,
+                            "%s: alloc streams error after reset", __func__);
+               return -EIO;
+       }
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       scsi_report_bus_reset(shost, 0);
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
        return 0;
 }
 
 static void uas_disconnect(struct usb_interface *intf)
 {
        struct Scsi_Host *shost = usb_get_intfdata(intf);
-       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
 
        devinfo->resetting = 1;
-       uas_abort_work(devinfo);
+       cancel_work_sync(&devinfo->work);
+       uas_abort_inflight(devinfo, DID_NO_CONNECT, __func__);
        usb_kill_anchored_urbs(&devinfo->cmd_urbs);
        usb_kill_anchored_urbs(&devinfo->sense_urbs);
        usb_kill_anchored_urbs(&devinfo->data_urbs);
+       uas_zap_dead(devinfo);
        scsi_remove_host(shost);
        uas_free_streams(devinfo);
-       kfree(devinfo);
+       scsi_host_put(shost);
 }
 
 /*
- * XXX: Should this plug into libusual so we can auto-upgrade devices from
- * Bulk-Only to UAS?
+ * Put the device back in usb-storage mode on shutdown, as some BIOS-es
+ * hang on reboot when the device is still in uas mode. Note the reset is
+ * necessary as some devices won't revert to usb-storage mode without it.
  */
+static void uas_shutdown(struct device *dev)
+{
+       struct usb_interface *intf = to_usb_interface(dev);
+       struct usb_device *udev = interface_to_usbdev(intf);
+       struct Scsi_Host *shost = usb_get_intfdata(intf);
+       struct uas_dev_info *devinfo = (struct uas_dev_info *)shost->hostdata;
+
+       if (system_state != SYSTEM_RESTART)
+               return;
+
+       devinfo->shutdown = 1;
+       uas_free_streams(devinfo);
+       usb_set_interface(udev, intf->altsetting[0].desc.bInterfaceNumber, 0);
+       usb_reset_device(udev);
+}
+
 static struct usb_driver uas_driver = {
        .name = "uas",
        .probe = uas_probe,
        .disconnect = uas_disconnect,
        .pre_reset = uas_pre_reset,
        .post_reset = uas_post_reset,
+       .suspend = uas_suspend,
+       .resume = uas_resume,
+       .reset_resume = uas_reset_resume,
+       .drvwrap.driver.shutdown = uas_shutdown,
        .id_table = uas_usb_ids,
 };
 
 module_usb_driver(uas_driver);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Matthew Wilcox and Sarah Sharp");
+MODULE_AUTHOR(
+       "Hans de Goede <hdegoede@redhat.com>, Matthew Wilcox and Sarah Sharp");
index adbeb255616afd32d8dc9dcaa244bdafd6ed46c2..f4a82291894ab2964754ac6eb06b1c7dfb4b3978 100644 (file)
@@ -2086,6 +2086,11 @@ UNUSUAL_DEV( 0xed10, 0x7636, 0x0001, 0x0001,
                "Digital MP3 Audio Player",
                USB_SC_DEVICE, USB_PR_DEVICE, NULL, US_FL_NOT_LOCKABLE ),
 
+/* Unusual uas devices */
+#if IS_ENABLED(CONFIG_USB_UAS)
+#include "unusual_uas.h"
+#endif
+
 /* Control/Bulk transport for all SubClass values */
 USUAL_DEV(USB_SC_RBC, USB_PR_CB),
 USUAL_DEV(USB_SC_8020, USB_PR_CB),
diff --git a/drivers/usb/storage/unusual_uas.h b/drivers/usb/storage/unusual_uas.h
new file mode 100644 (file)
index 0000000..7244444
--- /dev/null
@@ -0,0 +1,52 @@
+/* Driver for USB Attached SCSI devices - Unusual Devices File
+ *
+ *   (c) 2013 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on the same file for the usb-storage driver, which is:
+ *   (c) 2000-2002 Matthew Dharm (mdharm-usb@one-eyed-alien.net)
+ *   (c) 2000 Adam J. Richter (adam@yggdrasil.com), Yggdrasil Computing, Inc.
+ *
+ * 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, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * IMPORTANT NOTE: This file must be included in another file which defines
+ * a UNUSUAL_DEV macro before this file is included.
+ */
+
+/*
+ * If you edit this file, please try to keep it sorted first by VendorID,
+ * then by ProductID.
+ *
+ * If you want to add an entry for this file, be sure to include the
+ * following information:
+ *     - a patch that adds the entry for your device, including your
+ *       email address right above the entry (plus maybe a brief
+ *       explanation of the reason for the entry),
+ *     - lsusb -v output for the device
+ * Send your submission to Hans de Goede <hdegoede@redhat.com>
+ * and don't forget to CC: the USB development list <linux-usb@vger.kernel.org>
+ */
+
+/*
+ * This is an example entry for the US_FL_IGNORE_UAS flag. Once we have an
+ * actual entry using US_FL_IGNORE_UAS this entry should be removed.
+ *
+ * UNUSUAL_DEV(  0xabcd, 0x1234, 0x0100, 0x0100,
+ *             "Example",
+ *             "Storage with broken UAS",
+ *             USB_SC_DEVICE, USB_PR_DEVICE, NULL,
+ *             US_FL_IGNORE_UAS),
+ */
index 1c0b89f2a138aa2046abf81a57affcac6b9300a3..f1c96261a50196694689688cc041e38ef1c41a87 100644 (file)
 #include "sierra_ms.h"
 #include "option_ms.h"
 
+#if IS_ENABLED(CONFIG_USB_UAS)
+#include "uas-detect.h"
+#endif
+
 /* Some informational data */
 MODULE_AUTHOR("Matthew Dharm <mdharm-usb@one-eyed-alien.net>");
 MODULE_DESCRIPTION("USB Mass Storage driver for Linux");
@@ -459,14 +463,14 @@ static int associate_dev(struct us_data *us, struct usb_interface *intf)
 #define TOLOWER(x) ((x) | 0x20)
 
 /* Adjust device flags based on the "quirks=" module parameter */
-static void adjust_quirks(struct us_data *us)
+void usb_stor_adjust_quirks(struct usb_device *udev, unsigned long *fflags)
 {
        char *p;
-       u16 vid = le16_to_cpu(us->pusb_dev->descriptor.idVendor);
-       u16 pid = le16_to_cpu(us->pusb_dev->descriptor.idProduct);
+       u16 vid = le16_to_cpu(udev->descriptor.idVendor);
+       u16 pid = le16_to_cpu(udev->descriptor.idProduct);
        unsigned f = 0;
        unsigned int mask = (US_FL_SANE_SENSE | US_FL_BAD_SENSE |
-                       US_FL_FIX_CAPACITY |
+                       US_FL_FIX_CAPACITY | US_FL_IGNORE_UAS |
                        US_FL_CAPACITY_HEURISTICS | US_FL_IGNORE_DEVICE |
                        US_FL_NOT_LOCKABLE | US_FL_MAX_SECTORS_64 |
                        US_FL_CAPACITY_OK | US_FL_IGNORE_RESIDUE |
@@ -537,14 +541,18 @@ static void adjust_quirks(struct us_data *us)
                case 's':
                        f |= US_FL_SINGLE_LUN;
                        break;
+               case 'u':
+                       f |= US_FL_IGNORE_UAS;
+                       break;
                case 'w':
                        f |= US_FL_NO_WP_DETECT;
                        break;
                /* Ignore unrecognized flag characters */
                }
        }
-       us->fflags = (us->fflags & ~mask) | f;
+       *fflags = (*fflags & ~mask) | f;
 }
+EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks);
 
 /* Get the unusual_devs entries and the string descriptors */
 static int get_device_info(struct us_data *us, const struct usb_device_id *id,
@@ -564,7 +572,7 @@ static int get_device_info(struct us_data *us, const struct usb_device_id *id,
                        idesc->bInterfaceProtocol :
                        unusual_dev->useTransport;
        us->fflags = id->driver_info;
-       adjust_quirks(us);
+       usb_stor_adjust_quirks(us->pusb_dev, &us->fflags);
 
        if (us->fflags & US_FL_IGNORE_DEVICE) {
                dev_info(pdev, "device ignored\n");
@@ -1035,6 +1043,12 @@ static int storage_probe(struct usb_interface *intf,
        int result;
        int size;
 
+       /* If uas is enabled and this device can do uas then ignore it. */
+#if IS_ENABLED(CONFIG_USB_UAS)
+       if (uas_use_uas_driver(intf, id))
+               return -ENXIO;
+#endif
+
        /*
         * If the device isn't standard (is handled by a subdriver
         * module) then don't accept it.
index 75f70f04f37b89ba0ee63495298bc995da0024e4..307e339a9478682dc5b2709710bd5209cfef47ca 100644 (file)
@@ -201,4 +201,7 @@ extern int usb_stor_probe1(struct us_data **pus,
 extern int usb_stor_probe2(struct us_data *us);
 extern void usb_stor_disconnect(struct usb_interface *intf);
 
+extern void usb_stor_adjust_quirks(struct usb_device *dev,
+               unsigned long *fflags);
+
 #endif
index 3b959e83b28e4446085443b0e1a94ff3350f13b9..0677139c60654e1a350bce46f7f9e28634e87105 100644 (file)
@@ -284,7 +284,7 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc,
        struct device *dev = wusbhc->dev;
        struct wusb_dev *wusb_dev;
        struct wusb_port *port;
-       unsigned idx, devnum;
+       unsigned idx;
 
        mutex_lock(&wusbhc->mutex);
 
@@ -312,8 +312,6 @@ void wusbhc_devconnect_ack(struct wusbhc *wusbhc, struct wusb_dn_connect *dnc,
                goto error_unlock;
        }
 
-       devnum = idx + 2;
-
        /* Make sure we are using no crypto on that "virtual port" */
        wusbhc->set_ptk(wusbhc, idx, 0, NULL, 0);
 
index 368360f9a93ae1283780c3b30e230c6efd630b38..252c7bd9218afd5db373325b55b561c304ab0e88 100644 (file)
@@ -75,8 +75,6 @@ void __wa_destroy(struct wahc *wa)
        if (wa->dti_urb) {
                usb_kill_urb(wa->dti_urb);
                usb_put_urb(wa->dti_urb);
-               usb_kill_urb(wa->buf_in_urb);
-               usb_put_urb(wa->buf_in_urb);
        }
        kfree(wa->dti_buf);
        wa_nep_destroy(wa);
index a2ef84b8397e18efc96fcd413f583e88657a7af1..f2a8d29e17b9ad4eb2d29b6bc034ca438fe6d6f1 100644 (file)
@@ -125,7 +125,8 @@ struct wa_rpipe {
 
 enum wa_dti_state {
        WA_DTI_TRANSFER_RESULT_PENDING,
-       WA_DTI_ISOC_PACKET_STATUS_PENDING
+       WA_DTI_ISOC_PACKET_STATUS_PENDING,
+       WA_DTI_BUF_IN_DATA_PENDING
 };
 
 enum wa_quirks {
@@ -134,8 +135,20 @@ enum wa_quirks {
         * requests to be concatenated and not sent as separate packets.
         */
        WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC      = 0x01,
+       /*
+        * The Alereon HWA can be instructed to not send transfer notifications
+        * as an optimization.
+        */
+       WUSB_QUIRK_ALEREON_HWA_DISABLE_XFER_NOTIFICATIONS       = 0x02,
 };
 
+enum wa_vendor_specific_requests {
+       WA_REQ_ALEREON_DISABLE_XFER_NOTIFICATIONS = 0x4C,
+       WA_REQ_ALEREON_FEATURE_SET = 0x01,
+       WA_REQ_ALEREON_FEATURE_CLEAR = 0x00,
+};
+
+#define WA_MAX_BUF_IN_URBS     4
 /**
  * Instance of a HWA Host Controller
  *
@@ -206,7 +219,9 @@ struct wahc {
        u32 dti_isoc_xfer_in_progress;
        u8  dti_isoc_xfer_seg;
        struct urb *dti_urb;            /* URB for reading xfer results */
-       struct urb *buf_in_urb;         /* URB for reading data in */
+                                       /* URBs for reading data in */
+       struct urb buf_in_urbs[WA_MAX_BUF_IN_URBS];
+       int active_buf_in_urbs;         /* number of buf_in_urbs active. */
        struct edc dti_edc;             /* DTI error density counter */
        void *dti_buf;
        size_t dti_buf_size;
@@ -234,6 +249,7 @@ struct wahc {
 extern int wa_create(struct wahc *wa, struct usb_interface *iface,
        kernel_ulong_t);
 extern void __wa_destroy(struct wahc *wa);
+extern int wa_dti_start(struct wahc *wa);
 void wa_reset_all(struct wahc *wa);
 
 
@@ -275,6 +291,8 @@ static inline void wa_rpipe_init(struct wahc *wa)
 
 static inline void wa_init(struct wahc *wa)
 {
+       int index;
+
        edc_init(&wa->nep_edc);
        atomic_set(&wa->notifs_queued, 0);
        wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
@@ -288,6 +306,10 @@ static inline void wa_init(struct wahc *wa)
        INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run);
        wa->dto_in_use = 0;
        atomic_set(&wa->xfer_id_count, 1);
+       /* init the buf in URBs */
+       for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index)
+               usb_init_urb(&(wa->buf_in_urbs[index]));
+       wa->active_buf_in_urbs = 0;
 }
 
 /**
index 6ca80a4efc1bdd7c7c29af0019ac7acc9f968927..6d6da127f6de0578361eac94a11f841254d283cd 100644 (file)
@@ -298,7 +298,7 @@ static struct usb_wireless_ep_comp_descriptor *rpipe_epc_find(
                        break;
                }
                itr += hdr->bLength;
-               itr_size -= hdr->bDescriptorType;
+               itr_size -= hdr->bLength;
        }
 out:
        return epcd;
index 3cd96e936d77cbc552e8812feb252c94276571f3..c8e2a47d62a77eee093820f4eee1a3af613ed61a 100644 (file)
@@ -167,6 +167,8 @@ struct wa_xfer {
 
 static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer,
        struct wa_seg *seg, int curr_iso_frame);
+static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
+               int starting_index, enum wa_seg_status status);
 
 static inline void wa_xfer_init(struct wa_xfer *xfer)
 {
@@ -367,13 +369,13 @@ static unsigned __wa_xfer_is_done(struct wa_xfer *xfer)
                        break;
                case WA_SEG_ERROR:
                        xfer->result = seg->result;
-                       dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zu(0x%08zX)\n",
+                       dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zi(0x%08zX)\n",
                                xfer, wa_xfer_id(xfer), seg->index, seg->result,
                                seg->result);
                        goto out;
                case WA_SEG_ABORTED:
                        xfer->result = seg->result;
-                       dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zu(0x%08zX)\n",
+                       dev_dbg(dev, "xfer %p ID %08X#%u: ABORTED result %zi(0x%08zX)\n",
                                xfer, wa_xfer_id(xfer), seg->index, seg->result,
                                seg->result);
                        goto out;
@@ -389,6 +391,24 @@ out:
        return result;
 }
 
+/*
+ * Mark the given segment as done.  Return true if this completes the xfer.
+ * This should only be called for segs that have been submitted to an RPIPE.
+ * Delayed segs are not marked as submitted so they do not need to be marked
+ * as done when cleaning up.
+ *
+ * xfer->lock has to be locked
+ */
+static unsigned __wa_xfer_mark_seg_as_done(struct wa_xfer *xfer,
+       struct wa_seg *seg, enum wa_seg_status status)
+{
+       seg->status = status;
+       xfer->segs_done++;
+
+       /* check for done. */
+       return __wa_xfer_is_done(xfer);
+}
+
 /*
  * Search for a transfer list ID on the HCD's URB list
  *
@@ -416,12 +436,51 @@ out:
 
 struct wa_xfer_abort_buffer {
        struct urb urb;
+       struct wahc *wa;
        struct wa_xfer_abort cmd;
 };
 
 static void __wa_xfer_abort_cb(struct urb *urb)
 {
        struct wa_xfer_abort_buffer *b = urb->context;
+       struct wahc *wa = b->wa;
+
+       /*
+        * If the abort request URB failed, then the HWA did not get the abort
+        * command.  Forcibly clean up the xfer without waiting for a Transfer
+        * Result from the HWA.
+        */
+       if (urb->status < 0) {
+               struct wa_xfer *xfer;
+               struct device *dev = &wa->usb_iface->dev;
+
+               xfer = wa_xfer_get_by_id(wa, le32_to_cpu(b->cmd.dwTransferID));
+               dev_err(dev, "%s: Transfer Abort request failed. result: %d\n",
+                       __func__, urb->status);
+               if (xfer) {
+                       unsigned long flags;
+                       int done;
+                       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+                       dev_err(dev, "%s: cleaning up xfer %p ID 0x%08X.\n",
+                               __func__, xfer, wa_xfer_id(xfer));
+                       spin_lock_irqsave(&xfer->lock, flags);
+                       /* mark all segs as aborted. */
+                       wa_complete_remaining_xfer_segs(xfer, 0,
+                               WA_SEG_ABORTED);
+                       done = __wa_xfer_is_done(xfer);
+                       spin_unlock_irqrestore(&xfer->lock, flags);
+                       if (done)
+                               wa_xfer_completion(xfer);
+                       wa_xfer_delayed_run(rpipe);
+                       wa_xfer_put(xfer);
+               } else {
+                       dev_err(dev, "%s: xfer ID 0x%08X already gone.\n",
+                                __func__, le32_to_cpu(b->cmd.dwTransferID));
+               }
+       }
+
+       wa_put(wa);     /* taken in __wa_xfer_abort */
        usb_put_urb(&b->urb);
 }
 
@@ -449,6 +508,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer)
        b->cmd.bRequestType = WA_XFER_ABORT;
        b->cmd.wRPipe = rpipe->descr.wRPipeIndex;
        b->cmd.dwTransferID = wa_xfer_id_le32(xfer);
+       b->wa = wa_get(xfer->wa);
 
        usb_init_urb(&b->urb);
        usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev,
@@ -462,6 +522,7 @@ static int __wa_xfer_abort(struct wa_xfer *xfer)
 
 
 error_submit:
+       wa_put(xfer->wa);
        if (printk_ratelimit())
                dev_err(dev, "xfer %p: Can't submit abort request: %d\n",
                        xfer, result);
@@ -733,6 +794,8 @@ static void wa_seg_dto_cb(struct urb *urb)
                                seg->isoc_frame_offset + seg->isoc_frame_index);
 
                        /* resubmit the URB with the next isoc frame. */
+                       /* take a ref on resubmit. */
+                       wa_xfer_get(xfer);
                        result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
                        if (result < 0) {
                                dev_err(dev, "xfer 0x%08X#%u: DTO submit failed: %d\n",
@@ -760,9 +823,13 @@ static void wa_seg_dto_cb(struct urb *urb)
                goto error_default;
        }
 
+       /* taken when this URB was submitted. */
+       wa_xfer_put(xfer);
        return;
 
 error_dto_submit:
+       /* taken on resubmit attempt. */
+       wa_xfer_put(xfer);
 error_default:
        spin_lock_irqsave(&xfer->lock, flags);
        rpipe = xfer->ep->hcpriv;
@@ -772,12 +839,10 @@ error_default:
                wa_reset_all(wa);
        }
        if (seg->status != WA_SEG_ERROR) {
-               seg->status = WA_SEG_ERROR;
                seg->result = urb->status;
-               xfer->segs_done++;
                __wa_xfer_abort(xfer);
                rpipe_ready = rpipe_avail_inc(rpipe);
-               done = __wa_xfer_is_done(xfer);
+               done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR);
        }
        spin_unlock_irqrestore(&xfer->lock, flags);
        if (holding_dto) {
@@ -788,7 +853,8 @@ error_default:
                wa_xfer_completion(xfer);
        if (rpipe_ready)
                wa_xfer_delayed_run(rpipe);
-
+       /* taken when this URB was submitted. */
+       wa_xfer_put(xfer);
 }
 
 /*
@@ -842,12 +908,11 @@ static void wa_seg_iso_pack_desc_cb(struct urb *urb)
                }
                if (seg->status != WA_SEG_ERROR) {
                        usb_unlink_urb(seg->dto_urb);
-                       seg->status = WA_SEG_ERROR;
                        seg->result = urb->status;
-                       xfer->segs_done++;
                        __wa_xfer_abort(xfer);
                        rpipe_ready = rpipe_avail_inc(rpipe);
-                       done = __wa_xfer_is_done(xfer);
+                       done = __wa_xfer_mark_seg_as_done(xfer, seg,
+                                       WA_SEG_ERROR);
                }
                spin_unlock_irqrestore(&xfer->lock, flags);
                if (done)
@@ -855,6 +920,8 @@ static void wa_seg_iso_pack_desc_cb(struct urb *urb)
                if (rpipe_ready)
                        wa_xfer_delayed_run(rpipe);
        }
+       /* taken when this URB was submitted. */
+       wa_xfer_put(xfer);
 }
 
 /*
@@ -919,18 +986,18 @@ static void wa_seg_tr_cb(struct urb *urb)
                }
                usb_unlink_urb(seg->isoc_pack_desc_urb);
                usb_unlink_urb(seg->dto_urb);
-               seg->status = WA_SEG_ERROR;
                seg->result = urb->status;
-               xfer->segs_done++;
                __wa_xfer_abort(xfer);
                rpipe_ready = rpipe_avail_inc(rpipe);
-               done = __wa_xfer_is_done(xfer);
+               done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_ERROR);
                spin_unlock_irqrestore(&xfer->lock, flags);
                if (done)
                        wa_xfer_completion(xfer);
                if (rpipe_ready)
                        wa_xfer_delayed_run(rpipe);
        }
+       /* taken when this URB was submitted. */
+       wa_xfer_put(xfer);
 }
 
 /*
@@ -940,7 +1007,7 @@ static void wa_seg_tr_cb(struct urb *urb)
  */
 static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
        const unsigned int bytes_transferred,
-       const unsigned int bytes_to_transfer, unsigned int *out_num_sgs)
+       const unsigned int bytes_to_transfer, int *out_num_sgs)
 {
        struct scatterlist *out_sg;
        unsigned int bytes_processed = 0, offset_into_current_page_data = 0,
@@ -1094,14 +1161,13 @@ static int __wa_populate_dto_urb(struct wa_xfer *xfer,
  */
 static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
 {
-       int result, cnt, iso_frame_offset;
+       int result, cnt, isoc_frame_offset = 0;
        size_t alloc_size = sizeof(*xfer->seg[0])
                - sizeof(xfer->seg[0]->xfer_hdr) + xfer_hdr_size;
        struct usb_device *usb_dev = xfer->wa->usb_dev;
        const struct usb_endpoint_descriptor *dto_epd = xfer->wa->dto_epd;
        struct wa_seg *seg;
        size_t buf_itr, buf_size, buf_itr_size;
-       int isoc_frame_offset = 0;
 
        result = -ENOMEM;
        xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC);
@@ -1109,7 +1175,6 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
                goto error_segs_kzalloc;
        buf_itr = 0;
        buf_size = xfer->urb->transfer_buffer_length;
-       iso_frame_offset = 0;
        for (cnt = 0; cnt < xfer->segs; cnt++) {
                size_t iso_pkt_descr_size = 0;
                int seg_isoc_frame_count = 0, seg_isoc_size = 0;
@@ -1318,30 +1383,41 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
        /* default to done unless we encounter a multi-frame isoc segment. */
        *dto_done = 1;
 
+       /*
+        * Take a ref for each segment urb so the xfer cannot disappear until
+        * all of the callbacks run.
+        */
+       wa_xfer_get(xfer);
        /* submit the transfer request. */
+       seg->status = WA_SEG_SUBMITTED;
        result = usb_submit_urb(&seg->tr_urb, GFP_ATOMIC);
        if (result < 0) {
                pr_err("%s: xfer %p#%u: REQ submit failed: %d\n",
                       __func__, xfer, seg->index, result);
-               goto error_seg_submit;
+               wa_xfer_put(xfer);
+               goto error_tr_submit;
        }
        /* submit the isoc packet descriptor if present. */
        if (seg->isoc_pack_desc_urb) {
+               wa_xfer_get(xfer);
                result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC);
                seg->isoc_frame_index = 0;
                if (result < 0) {
                        pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n",
                               __func__, xfer, seg->index, result);
+                       wa_xfer_put(xfer);
                        goto error_iso_pack_desc_submit;
                }
        }
        /* submit the out data if this is an out request. */
        if (seg->dto_urb) {
                struct wahc *wa = xfer->wa;
+               wa_xfer_get(xfer);
                result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
                if (result < 0) {
                        pr_err("%s: xfer %p#%u: DTO submit failed: %d\n",
                               __func__, xfer, seg->index, result);
+                       wa_xfer_put(xfer);
                        goto error_dto_submit;
                }
                /*
@@ -1353,7 +1429,6 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
                        && (seg->isoc_frame_count > 1))
                        *dto_done = 0;
        }
-       seg->status = WA_SEG_SUBMITTED;
        rpipe_avail_dec(rpipe);
        return 0;
 
@@ -1361,7 +1436,7 @@ error_dto_submit:
        usb_unlink_urb(seg->isoc_pack_desc_urb);
 error_iso_pack_desc_submit:
        usb_unlink_urb(&seg->tr_urb);
-error_seg_submit:
+error_tr_submit:
        seg->status = WA_SEG_ERROR;
        seg->result = result;
        *dto_done = 1;
@@ -1393,6 +1468,12 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting)
                                 list_node);
                list_del(&seg->list_node);
                xfer = seg->xfer;
+               /*
+                * Get a reference to the xfer in case the callbacks for the
+                * URBs submitted by __wa_seg_submit attempt to complete
+                * the xfer before this function completes.
+                */
+               wa_xfer_get(xfer);
                result = __wa_seg_submit(rpipe, xfer, seg, &dto_done);
                /* release the dto resource if this RPIPE is done with it. */
                if (dto_done)
@@ -1401,13 +1482,23 @@ static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting)
                        xfer, wa_xfer_id(xfer), seg->index,
                        atomic_read(&rpipe->segs_available), result);
                if (unlikely(result < 0)) {
+                       int done;
+
                        spin_unlock_irqrestore(&rpipe->seg_lock, flags);
                        spin_lock_irqsave(&xfer->lock, flags);
                        __wa_xfer_abort(xfer);
+                       /*
+                        * This seg was marked as submitted when it was put on
+                        * the RPIPE seg_list.  Mark it done.
+                        */
                        xfer->segs_done++;
+                       done = __wa_xfer_is_done(xfer);
                        spin_unlock_irqrestore(&xfer->lock, flags);
+                       if (done)
+                               wa_xfer_completion(xfer);
                        spin_lock_irqsave(&rpipe->seg_lock, flags);
                }
+               wa_xfer_put(xfer);
        }
        /*
         * Mark this RPIPE as waiting if dto was not acquired, there are
@@ -1592,12 +1683,19 @@ static int wa_urb_enqueue_b(struct wa_xfer *xfer)
                dev_err(&(urb->dev->dev), "%s: error_xfer_setup\n", __func__);
                goto error_xfer_setup;
        }
+       /*
+        * Get a xfer reference since __wa_xfer_submit starts asynchronous
+        * operations that may try to complete the xfer before this function
+        * exits.
+        */
+       wa_xfer_get(xfer);
        result = __wa_xfer_submit(xfer);
        if (result < 0) {
                dev_err(&(urb->dev->dev), "%s: error_xfer_submit\n", __func__);
                goto error_xfer_submit;
        }
        spin_unlock_irqrestore(&xfer->lock, flags);
+       wa_xfer_put(xfer);
        return 0;
 
        /*
@@ -1623,6 +1721,7 @@ error_xfer_submit:
        spin_unlock_irqrestore(&xfer->lock, flags);
        if (done)
                wa_xfer_completion(xfer);
+       wa_xfer_put(xfer);
        /* return success since the completion routine will run. */
        return 0;
 }
@@ -1832,20 +1931,20 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
        /* check if it is safe to unlink. */
        spin_lock_irqsave(&wa->xfer_list_lock, flags);
        result = usb_hcd_check_unlink_urb(&(wa->wusb->usb_hcd), urb, status);
+       if ((result == 0) && urb->hcpriv) {
+               /*
+                * Get a xfer ref to prevent a race with wa_xfer_giveback
+                * cleaning up the xfer while we are working with it.
+                */
+               wa_xfer_get(urb->hcpriv);
+       }
        spin_unlock_irqrestore(&wa->xfer_list_lock, flags);
        if (result)
                return result;
 
        xfer = urb->hcpriv;
-       if (xfer == NULL) {
-               /*
-                * Nothing setup yet enqueue will see urb->status !=
-                * -EINPROGRESS (by hcd layer) and bail out with
-                * error, no need to do completion
-                */
-               BUG_ON(urb->status == -EINPROGRESS);
-               goto out;
-       }
+       if (xfer == NULL)
+               return -ENOENT;
        spin_lock_irqsave(&xfer->lock, flags);
        pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer));
        rpipe = xfer->ep->hcpriv;
@@ -1856,6 +1955,16 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
                result = -ENOENT;
                goto out_unlock;
        }
+       /*
+        * Check for done to avoid racing with wa_xfer_giveback and completing
+        * twice.
+        */
+       if (__wa_xfer_is_done(xfer)) {
+               pr_debug("%s: xfer %p id 0x%08X already done.\n", __func__,
+                       xfer, wa_xfer_id(xfer));
+               result = -ENOENT;
+               goto out_unlock;
+       }
        /* Check the delayed list -> if there, release and complete */
        spin_lock_irqsave(&wa->xfer_list_lock, flags2);
        if (!list_empty(&xfer->list_node) && xfer->seg == NULL)
@@ -1865,6 +1974,11 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
                goto out_unlock;        /* setup(), enqueue_b() completes */
        /* Ok, the xfer is in flight already, it's been setup and submitted.*/
        xfer_abort_pending = __wa_xfer_abort(xfer) >= 0;
+       /*
+        * grab the rpipe->seg_lock here to prevent racing with
+        * __wa_xfer_delayed_run.
+        */
+       spin_lock(&rpipe->seg_lock);
        for (cnt = 0; cnt < xfer->segs; cnt++) {
                seg = xfer->seg[cnt];
                pr_debug("%s: xfer id 0x%08X#%d status = %d\n",
@@ -1885,14 +1999,22 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
                         */
                        seg->status = WA_SEG_ABORTED;
                        seg->result = -ENOENT;
-                       spin_lock_irqsave(&rpipe->seg_lock, flags2);
                        list_del(&seg->list_node);
                        xfer->segs_done++;
-                       spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
                        break;
                case WA_SEG_DONE:
                case WA_SEG_ERROR:
                case WA_SEG_ABORTED:
+                       break;
+                       /*
+                        * The buf_in data for a segment in the
+                        * WA_SEG_DTI_PENDING state is actively being read.
+                        * Let wa_buf_in_cb handle it since it will be called
+                        * and will increment xfer->segs_done.  Cleaning up
+                        * here could cause wa_buf_in_cb to access the xfer
+                        * after it has been completed/freed.
+                        */
+               case WA_SEG_DTI_PENDING:
                        break;
                        /*
                         * In the states below, the HWA device already knows
@@ -1903,7 +2025,6 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
                         */
                case WA_SEG_SUBMITTED:
                case WA_SEG_PENDING:
-               case WA_SEG_DTI_PENDING:
                        /*
                         * Check if the abort was successfully sent.  This could
                         * be false if the HWA has been removed but we haven't
@@ -1917,6 +2038,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
                        break;
                }
        }
+       spin_unlock(&rpipe->seg_lock);
        xfer->result = urb->status;     /* -ENOENT or -ECONNRESET */
        done = __wa_xfer_is_done(xfer);
        spin_unlock_irqrestore(&xfer->lock, flags);
@@ -1924,11 +2046,12 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb, int status)
                wa_xfer_completion(xfer);
        if (rpipe_ready)
                wa_xfer_delayed_run(rpipe);
+       wa_xfer_put(xfer);
        return result;
 
 out_unlock:
        spin_unlock_irqrestore(&xfer->lock, flags);
-out:
+       wa_xfer_put(xfer);
        return result;
 
 dequeue_delayed:
@@ -1937,6 +2060,7 @@ dequeue_delayed:
        xfer->result = urb->status;
        spin_unlock_irqrestore(&xfer->lock, flags);
        wa_xfer_giveback(xfer);
+       wa_xfer_put(xfer);
        usb_put_urb(urb);               /* we got a ref in enqueue() */
        return 0;
 }
@@ -1996,15 +2120,17 @@ static int wa_xfer_status_to_errno(u8 status)
  * no other segment transfer results will be returned from the device.
  * Mark the remaining submitted or pending xfers as completed so that
  * the xfer will complete cleanly.
+ *
+ * xfer->lock must be held
+ *
  */
 static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
-               struct wa_seg *incoming_seg, enum wa_seg_status status)
+               int starting_index, enum wa_seg_status status)
 {
        int index;
        struct wa_rpipe *rpipe = xfer->ep->hcpriv;
 
-       for (index = incoming_seg->index + 1; index < xfer->segs_submitted;
-               index++) {
+       for (index = starting_index; index < xfer->segs_submitted; index++) {
                struct wa_seg *current_seg = xfer->seg[index];
 
                BUG_ON(current_seg == NULL);
@@ -2033,73 +2159,110 @@ static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
        }
 }
 
-/* Populate the wa->buf_in_urb based on the current isoc transfer state. */
-static void __wa_populate_buf_in_urb_isoc(struct wahc *wa, struct wa_xfer *xfer,
-       struct wa_seg *seg, int curr_iso_frame)
+/* Populate the given urb based on the current isoc transfer state. */
+static int __wa_populate_buf_in_urb_isoc(struct wahc *wa,
+       struct urb *buf_in_urb, struct wa_xfer *xfer, struct wa_seg *seg)
 {
-       BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);
+       int urb_start_frame = seg->isoc_frame_index + seg->isoc_frame_offset;
+       int seg_index, total_len = 0, urb_frame_index = urb_start_frame;
+       struct usb_iso_packet_descriptor *iso_frame_desc =
+                                               xfer->urb->iso_frame_desc;
+       const int dti_packet_size = usb_endpoint_maxp(wa->dti_epd);
+       int next_frame_contiguous;
+       struct usb_iso_packet_descriptor *iso_frame;
+
+       BUG_ON(buf_in_urb->status == -EINPROGRESS);
+
+       /*
+        * If the current frame actual_length is contiguous with the next frame
+        * and actual_length is a multiple of the DTI endpoint max packet size,
+        * combine the current frame with the next frame in a single URB.  This
+        * reduces the number of URBs that must be submitted in that case.
+        */
+       seg_index = seg->isoc_frame_index;
+       do {
+               next_frame_contiguous = 0;
+
+               iso_frame = &iso_frame_desc[urb_frame_index];
+               total_len += iso_frame->actual_length;
+               ++urb_frame_index;
+               ++seg_index;
+
+               if (seg_index < seg->isoc_frame_count) {
+                       struct usb_iso_packet_descriptor *next_iso_frame;
+
+                       next_iso_frame = &iso_frame_desc[urb_frame_index];
+
+                       if ((iso_frame->offset + iso_frame->actual_length) ==
+                               next_iso_frame->offset)
+                               next_frame_contiguous = 1;
+               }
+       } while (next_frame_contiguous
+                       && ((iso_frame->actual_length % dti_packet_size) == 0));
 
        /* this should always be 0 before a resubmit. */
-       wa->buf_in_urb->num_mapped_sgs  = 0;
-       wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma +
-               xfer->urb->iso_frame_desc[curr_iso_frame].offset;
-       wa->buf_in_urb->transfer_buffer_length =
-               xfer->urb->iso_frame_desc[curr_iso_frame].length;
-       wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-       wa->buf_in_urb->transfer_buffer = NULL;
-       wa->buf_in_urb->sg = NULL;
-       wa->buf_in_urb->num_sgs = 0;
-       wa->buf_in_urb->context = seg;
+       buf_in_urb->num_mapped_sgs      = 0;
+       buf_in_urb->transfer_dma = xfer->urb->transfer_dma +
+               iso_frame_desc[urb_start_frame].offset;
+       buf_in_urb->transfer_buffer_length = total_len;
+       buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       buf_in_urb->transfer_buffer = NULL;
+       buf_in_urb->sg = NULL;
+       buf_in_urb->num_sgs = 0;
+       buf_in_urb->context = seg;
+
+       /* return the number of frames included in this URB. */
+       return seg_index - seg->isoc_frame_index;
 }
 
-/* Populate the wa->buf_in_urb based on the current transfer state. */
-static int wa_populate_buf_in_urb(struct wahc *wa, struct wa_xfer *xfer,
+/* Populate the given urb based on the current transfer state. */
+static int wa_populate_buf_in_urb(struct urb *buf_in_urb, struct wa_xfer *xfer,
        unsigned int seg_idx, unsigned int bytes_transferred)
 {
        int result = 0;
        struct wa_seg *seg = xfer->seg[seg_idx];
 
-       BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);
+       BUG_ON(buf_in_urb->status == -EINPROGRESS);
        /* this should always be 0 before a resubmit. */
-       wa->buf_in_urb->num_mapped_sgs  = 0;
+       buf_in_urb->num_mapped_sgs      = 0;
 
        if (xfer->is_dma) {
-               wa->buf_in_urb->transfer_dma = xfer->urb->transfer_dma
+               buf_in_urb->transfer_dma = xfer->urb->transfer_dma
                        + (seg_idx * xfer->seg_size);
-               wa->buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
-               wa->buf_in_urb->transfer_buffer = NULL;
-               wa->buf_in_urb->sg = NULL;
-               wa->buf_in_urb->num_sgs = 0;
+               buf_in_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               buf_in_urb->transfer_buffer = NULL;
+               buf_in_urb->sg = NULL;
+               buf_in_urb->num_sgs = 0;
        } else {
                /* do buffer or SG processing. */
-               wa->buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
+               buf_in_urb->transfer_flags &= ~URB_NO_TRANSFER_DMA_MAP;
 
                if (xfer->urb->transfer_buffer) {
-                       wa->buf_in_urb->transfer_buffer =
+                       buf_in_urb->transfer_buffer =
                                xfer->urb->transfer_buffer
                                + (seg_idx * xfer->seg_size);
-                       wa->buf_in_urb->sg = NULL;
-                       wa->buf_in_urb->num_sgs = 0;
+                       buf_in_urb->sg = NULL;
+                       buf_in_urb->num_sgs = 0;
                } else {
                        /* allocate an SG list to store seg_size bytes
                                and copy the subset of the xfer->urb->sg
                                that matches the buffer subset we are
                                about to read. */
-                       wa->buf_in_urb->sg = wa_xfer_create_subset_sg(
+                       buf_in_urb->sg = wa_xfer_create_subset_sg(
                                xfer->urb->sg,
                                seg_idx * xfer->seg_size,
                                bytes_transferred,
-                               &(wa->buf_in_urb->num_sgs));
+                               &(buf_in_urb->num_sgs));
 
-                       if (!(wa->buf_in_urb->sg)) {
-                               wa->buf_in_urb->num_sgs = 0;
+                       if (!(buf_in_urb->sg)) {
+                               buf_in_urb->num_sgs     = 0;
                                result = -ENOMEM;
                        }
-                       wa->buf_in_urb->transfer_buffer = NULL;
+                       buf_in_urb->transfer_buffer = NULL;
                }
        }
-       wa->buf_in_urb->transfer_buffer_length = bytes_transferred;
-       wa->buf_in_urb->context = seg;
+       buf_in_urb->transfer_buffer_length = bytes_transferred;
+       buf_in_urb->context = seg;
 
        return result;
 }
@@ -2124,6 +2287,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
        u8 usb_status;
        unsigned rpipe_ready = 0;
        unsigned bytes_transferred = le32_to_cpu(xfer_result->dwTransferLength);
+       struct urb *buf_in_urb = &(wa->buf_in_urbs[0]);
 
        spin_lock_irqsave(&xfer->lock, flags);
        seg_idx = xfer_result->bTransferSegment & 0x7f;
@@ -2147,7 +2311,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
        }
        if (usb_status & 0x80) {
                seg->result = wa_xfer_status_to_errno(usb_status);
-               dev_err(dev, "DTI: xfer %p#:%08X:%u failed (0x%02x)\n",
+               dev_err(dev, "DTI: xfer %p 0x%08X:#%u failed (0x%02x)\n",
                        xfer, xfer->id, seg->index, usb_status);
                seg->status = ((usb_status & 0x7F) == WA_XFER_STATUS_ABORTED) ?
                        WA_SEG_ABORTED : WA_SEG_ERROR;
@@ -2162,7 +2326,8 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
         * transfers with data or below for no data, the xfer will complete.
         */
        if (xfer_result->bTransferSegment & 0x80)
-               wa_complete_remaining_xfer_segs(xfer, seg, WA_SEG_DONE);
+               wa_complete_remaining_xfer_segs(xfer, seg->index + 1,
+                       WA_SEG_DONE);
        if (usb_pipeisoc(xfer->urb->pipe)
                && (le32_to_cpu(xfer_result->dwNumOfPackets) > 0)) {
                /* set up WA state to read the isoc packet status next. */
@@ -2173,20 +2338,21 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
                        && (bytes_transferred > 0)) {
                /* IN data phase: read to buffer */
                seg->status = WA_SEG_DTI_PENDING;
-               result = wa_populate_buf_in_urb(wa, xfer, seg_idx,
+               result = wa_populate_buf_in_urb(buf_in_urb, xfer, seg_idx,
                        bytes_transferred);
                if (result < 0)
                        goto error_buf_in_populate;
-               result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC);
-               if (result < 0)
+               ++(wa->active_buf_in_urbs);
+               result = usb_submit_urb(buf_in_urb, GFP_ATOMIC);
+               if (result < 0) {
+                       --(wa->active_buf_in_urbs);
                        goto error_submit_buf_in;
+               }
        } else {
                /* OUT data phase or no data, complete it -- */
-               seg->status = WA_SEG_DONE;
                seg->result = bytes_transferred;
-               xfer->segs_done++;
                rpipe_ready = rpipe_avail_inc(rpipe);
-               done = __wa_xfer_is_done(xfer);
+               done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE);
        }
        spin_unlock_irqrestore(&xfer->lock, flags);
        if (done)
@@ -2205,15 +2371,15 @@ error_submit_buf_in:
                dev_err(dev, "xfer %p#%u: can't submit DTI data phase: %d\n",
                        xfer, seg_idx, result);
        seg->result = result;
-       kfree(wa->buf_in_urb->sg);
-       wa->buf_in_urb->sg = NULL;
+       kfree(buf_in_urb->sg);
+       buf_in_urb->sg = NULL;
 error_buf_in_populate:
        __wa_xfer_abort(xfer);
        seg->status = WA_SEG_ERROR;
 error_complete:
        xfer->segs_done++;
        rpipe_ready = rpipe_avail_inc(rpipe);
-       wa_complete_remaining_xfer_segs(xfer, seg, seg->status);
+       wa_complete_remaining_xfer_segs(xfer, seg->index + 1, seg->status);
        done = __wa_xfer_is_done(xfer);
        /*
         * queue work item to clear STALL for control endpoints.
@@ -2315,16 +2481,16 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb)
        for (seg_index = 0; seg_index < seg->isoc_frame_count; ++seg_index) {
                struct usb_iso_packet_descriptor *iso_frame_desc =
                        xfer->urb->iso_frame_desc;
-               const int urb_frame_index =
+               const int xfer_frame_index =
                        seg->isoc_frame_offset + seg_index;
 
-               iso_frame_desc[urb_frame_index].status =
+               iso_frame_desc[xfer_frame_index].status =
                        wa_xfer_status_to_errno(
                        le16_to_cpu(status_array[seg_index].PacketStatus));
-               iso_frame_desc[urb_frame_index].actual_length =
+               iso_frame_desc[xfer_frame_index].actual_length =
                        le16_to_cpu(status_array[seg_index].PacketLength);
                /* track the number of frames successfully transferred. */
-               if (iso_frame_desc[urb_frame_index].actual_length > 0) {
+               if (iso_frame_desc[xfer_frame_index].actual_length > 0) {
                        /* save the starting frame index for buf_in_urb. */
                        if (!data_frame_count)
                                first_frame_index = seg_index;
@@ -2333,30 +2499,64 @@ static int wa_process_iso_packet_status(struct wahc *wa, struct urb *urb)
        }
 
        if (xfer->is_inbound && data_frame_count) {
-               int result;
+               int result, total_frames_read = 0, urb_index = 0;
+               struct urb *buf_in_urb;
 
+               /* IN data phase: read to buffer */
+               seg->status = WA_SEG_DTI_PENDING;
+
+               /* start with the first frame with data. */
                seg->isoc_frame_index = first_frame_index;
-               /* submit a read URB for the first frame with data. */
-               __wa_populate_buf_in_urb_isoc(wa, xfer, seg,
-                       seg->isoc_frame_index + seg->isoc_frame_offset);
+               /* submit up to WA_MAX_BUF_IN_URBS read URBs. */
+               do {
+                       int urb_frame_index, urb_frame_count;
+                       struct usb_iso_packet_descriptor *iso_frame_desc;
+
+                       buf_in_urb = &(wa->buf_in_urbs[urb_index]);
+                       urb_frame_count = __wa_populate_buf_in_urb_isoc(wa,
+                               buf_in_urb, xfer, seg);
+                       /* advance frame index to start of next read URB. */
+                       seg->isoc_frame_index += urb_frame_count;
+                       total_frames_read += urb_frame_count;
+
+                       ++(wa->active_buf_in_urbs);
+                       result = usb_submit_urb(buf_in_urb, GFP_ATOMIC);
+
+                       /* skip 0-byte frames. */
+                       urb_frame_index =
+                               seg->isoc_frame_offset + seg->isoc_frame_index;
+                       iso_frame_desc =
+                               &(xfer->urb->iso_frame_desc[urb_frame_index]);
+                       while ((seg->isoc_frame_index <
+                                               seg->isoc_frame_count) &&
+                                (iso_frame_desc->actual_length == 0)) {
+                               ++(seg->isoc_frame_index);
+                               ++iso_frame_desc;
+                       }
+                       ++urb_index;
+
+               } while ((result == 0) && (urb_index < WA_MAX_BUF_IN_URBS)
+                               && (seg->isoc_frame_index <
+                                               seg->isoc_frame_count));
 
-               result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC);
                if (result < 0) {
+                       --(wa->active_buf_in_urbs);
                        dev_err(dev, "DTI Error: Could not submit buf in URB (%d)",
                                result);
                        wa_reset_all(wa);
-               } else if (data_frame_count > 1)
-                       /* If we need to read multiple frames, set DTI busy. */
+               } else if (data_frame_count > total_frames_read)
+                       /* If we need to read more frames, set DTI busy. */
                        dti_busy = 1;
        } else {
                /* OUT transfer or no more IN data, complete it -- */
-               seg->status = WA_SEG_DONE;
-               xfer->segs_done++;
                rpipe_ready = rpipe_avail_inc(rpipe);
-               done = __wa_xfer_is_done(xfer);
+               done = __wa_xfer_mark_seg_as_done(xfer, seg, WA_SEG_DONE);
        }
        spin_unlock_irqrestore(&xfer->lock, flags);
-       wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
+       if (dti_busy)
+               wa->dti_state = WA_DTI_BUF_IN_DATA_PENDING;
+       else
+               wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
        if (done)
                wa_xfer_completion(xfer);
        if (rpipe_ready)
@@ -2388,8 +2588,9 @@ static void wa_buf_in_cb(struct urb *urb)
        struct wahc *wa;
        struct device *dev;
        struct wa_rpipe *rpipe;
-       unsigned rpipe_ready = 0, seg_index, isoc_data_frame_count = 0;
+       unsigned rpipe_ready = 0, isoc_data_frame_count = 0;
        unsigned long flags;
+       int resubmit_dti = 0, active_buf_in_urbs;
        u8 done = 0;
 
        /* free the sg if it was used. */
@@ -2399,19 +2600,20 @@ static void wa_buf_in_cb(struct urb *urb)
        spin_lock_irqsave(&xfer->lock, flags);
        wa = xfer->wa;
        dev = &wa->usb_iface->dev;
+       --(wa->active_buf_in_urbs);
+       active_buf_in_urbs = wa->active_buf_in_urbs;
 
        if (usb_pipeisoc(xfer->urb->pipe)) {
+               struct usb_iso_packet_descriptor *iso_frame_desc =
+                       xfer->urb->iso_frame_desc;
+               int     seg_index;
+
                /*
-                * Find the next isoc frame with data.  Bail out after
-                * isoc_data_frame_count > 1 since there is no need to walk
-                * the entire frame array.  We just need to know if
-                * isoc_data_frame_count is 0, 1, or >1.
+                * Find the next isoc frame with data and count how many
+                * frames with data remain.
                 */
-               seg_index = seg->isoc_frame_index + 1;
-               while ((seg_index < seg->isoc_frame_count)
-                       && (isoc_data_frame_count <= 1)) {
-                       struct usb_iso_packet_descriptor *iso_frame_desc =
-                               xfer->urb->iso_frame_desc;
+               seg_index = seg->isoc_frame_index;
+               while (seg_index < seg->isoc_frame_count) {
                        const int urb_frame_index =
                                seg->isoc_frame_offset + seg_index;
 
@@ -2432,24 +2634,39 @@ static void wa_buf_in_cb(struct urb *urb)
 
                seg->result += urb->actual_length;
                if (isoc_data_frame_count > 0) {
-                       int result;
-                       /* submit a read URB for the first frame with data. */
-                       __wa_populate_buf_in_urb_isoc(wa, xfer, seg,
-                               seg->isoc_frame_index + seg->isoc_frame_offset);
-                       result = usb_submit_urb(wa->buf_in_urb, GFP_ATOMIC);
+                       int result, urb_frame_count;
+
+                       /* submit a read URB for the next frame with data. */
+                       urb_frame_count = __wa_populate_buf_in_urb_isoc(wa, urb,
+                                xfer, seg);
+                       /* advance index to start of next read URB. */
+                       seg->isoc_frame_index += urb_frame_count;
+                       ++(wa->active_buf_in_urbs);
+                       result = usb_submit_urb(urb, GFP_ATOMIC);
                        if (result < 0) {
+                               --(wa->active_buf_in_urbs);
                                dev_err(dev, "DTI Error: Could not submit buf in URB (%d)",
                                        result);
                                wa_reset_all(wa);
                        }
-               } else {
+                       /*
+                        * If we are in this callback and
+                        * isoc_data_frame_count > 0, it means that the dti_urb
+                        * submission was delayed in wa_dti_cb.  Once
+                        * we submit the last buf_in_urb, we can submit the
+                        * delayed dti_urb.
+                        */
+                         resubmit_dti = (isoc_data_frame_count ==
+                                                       urb_frame_count);
+               } else if (active_buf_in_urbs == 0) {
                        rpipe = xfer->ep->hcpriv;
-                       seg->status = WA_SEG_DONE;
-                       dev_dbg(dev, "xfer %p#%u: data in done (%zu bytes)\n",
-                               xfer, seg->index, seg->result);
-                       xfer->segs_done++;
+                       dev_dbg(dev,
+                               "xfer %p 0x%08X#%u: data in done (%zu bytes)\n",
+                               xfer, wa_xfer_id(xfer), seg->index,
+                               seg->result);
                        rpipe_ready = rpipe_avail_inc(rpipe);
-                       done = __wa_xfer_is_done(xfer);
+                       done = __wa_xfer_mark_seg_as_done(xfer, seg,
+                                       WA_SEG_DONE);
                }
                spin_unlock_irqrestore(&xfer->lock, flags);
                if (done)
@@ -2461,37 +2678,44 @@ static void wa_buf_in_cb(struct urb *urb)
        case -ENOENT:           /* as it was done by the who unlinked us */
                break;
        default:                /* Other errors ... */
+               /*
+                * Error on data buf read.  Only resubmit DTI if it hasn't
+                * already been done by previously hitting this error or by a
+                * successful completion of the previous buf_in_urb.
+                */
+               resubmit_dti = wa->dti_state != WA_DTI_TRANSFER_RESULT_PENDING;
                spin_lock_irqsave(&xfer->lock, flags);
                rpipe = xfer->ep->hcpriv;
                if (printk_ratelimit())
-                       dev_err(dev, "xfer %p#%u: data in error %d\n",
-                               xfer, seg->index, urb->status);
+                       dev_err(dev, "xfer %p 0x%08X#%u: data in error %d\n",
+                               xfer, wa_xfer_id(xfer), seg->index,
+                               urb->status);
                if (edc_inc(&wa->nep_edc, EDC_MAX_ERRORS,
                            EDC_ERROR_TIMEFRAME)){
                        dev_err(dev, "DTO: URB max acceptable errors "
                                "exceeded, resetting device\n");
                        wa_reset_all(wa);
                }
-               seg->status = WA_SEG_ERROR;
                seg->result = urb->status;
-               xfer->segs_done++;
                rpipe_ready = rpipe_avail_inc(rpipe);
-               __wa_xfer_abort(xfer);
-               done = __wa_xfer_is_done(xfer);
+               if (active_buf_in_urbs == 0)
+                       done = __wa_xfer_mark_seg_as_done(xfer, seg,
+                               WA_SEG_ERROR);
+               else
+                       __wa_xfer_abort(xfer);
                spin_unlock_irqrestore(&xfer->lock, flags);
                if (done)
                        wa_xfer_completion(xfer);
                if (rpipe_ready)
                        wa_xfer_delayed_run(rpipe);
        }
-       /*
-        * If we are in this callback and isoc_data_frame_count > 0, it means
-        * that the dti_urb submission was delayed in wa_dti_cb.  Once
-        * isoc_data_frame_count gets to 1, we can submit the deferred URB
-        * since the last buf_in_urb was just submitted.
-        */
-       if (isoc_data_frame_count == 1) {
-               int result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC);
+
+       if (resubmit_dti) {
+               int result;
+
+               wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
+
+               result = usb_submit_urb(wa->dti_urb, GFP_ATOMIC);
                if (result < 0) {
                        dev_err(dev, "DTI Error: Could not submit DTI URB (%d)\n",
                                result);
@@ -2561,11 +2785,15 @@ static void wa_dti_cb(struct urb *urb)
                                        xfer_result->hdr.bNotifyType);
                                break;
                        }
+                       xfer_id = le32_to_cpu(xfer_result->dwTransferID);
                        usb_status = xfer_result->bTransferStatus & 0x3f;
-                       if (usb_status == WA_XFER_STATUS_NOT_FOUND)
+                       if (usb_status == WA_XFER_STATUS_NOT_FOUND) {
                                /* taken care of already */
+                               dev_dbg(dev, "%s: xfer 0x%08X#%u not found.\n",
+                                       __func__, xfer_id,
+                                       xfer_result->bTransferSegment & 0x7f);
                                break;
-                       xfer_id = le32_to_cpu(xfer_result->dwTransferID);
+                       }
                        xfer = wa_xfer_get_by_id(wa, xfer_id);
                        if (xfer == NULL) {
                                /* FIXME: transaction not found. */
@@ -2613,6 +2841,54 @@ out:
        return;
 }
 
+/*
+ * Initialize the DTI URB for reading transfer result notifications and also
+ * the buffer-in URB, for reading buffers. Then we just submit the DTI URB.
+ */
+int wa_dti_start(struct wahc *wa)
+{
+       const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
+       struct device *dev = &wa->usb_iface->dev;
+       int result = -ENOMEM, index;
+
+       if (wa->dti_urb != NULL)        /* DTI URB already started */
+               goto out;
+
+       wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL);
+       if (wa->dti_urb == NULL) {
+               dev_err(dev, "Can't allocate DTI URB\n");
+               goto error_dti_urb_alloc;
+       }
+       usb_fill_bulk_urb(
+               wa->dti_urb, wa->usb_dev,
+               usb_rcvbulkpipe(wa->usb_dev, 0x80 | dti_epd->bEndpointAddress),
+               wa->dti_buf, wa->dti_buf_size,
+               wa_dti_cb, wa);
+
+       /* init the buf in URBs */
+       for (index = 0; index < WA_MAX_BUF_IN_URBS; ++index) {
+               usb_fill_bulk_urb(
+                       &(wa->buf_in_urbs[index]), wa->usb_dev,
+                       usb_rcvbulkpipe(wa->usb_dev,
+                               0x80 | dti_epd->bEndpointAddress),
+                       NULL, 0, wa_buf_in_cb, wa);
+       }
+       result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
+       if (result < 0) {
+               dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n",
+                       result);
+               goto error_dti_urb_submit;
+       }
+out:
+       return 0;
+
+error_dti_urb_submit:
+       usb_put_urb(wa->dti_urb);
+       wa->dti_urb = NULL;
+error_dti_urb_alloc:
+       return result;
+}
+EXPORT_SYMBOL_GPL(wa_dti_start);
 /*
  * Transfer complete notification
  *
@@ -2627,15 +2903,10 @@ out:
  * Follow up in wa_dti_cb(), as that's where the whole state
  * machine starts.
  *
- * So here we just initialize the DTI URB for reading transfer result
- * notifications and also the buffer-in URB, for reading buffers. Then
- * we just submit the DTI URB.
- *
  * @wa shall be referenced
  */
 void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
 {
-       int result;
        struct device *dev = &wa->usb_iface->dev;
        struct wa_notif_xfer *notif_xfer;
        const struct usb_endpoint_descriptor *dti_epd = wa->dti_epd;
@@ -2649,45 +2920,13 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
                        notif_xfer->bEndpoint, dti_epd->bEndpointAddress);
                goto error;
        }
-       if (wa->dti_urb != NULL)        /* DTI URB already started */
-               goto out;
 
-       wa->dti_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (wa->dti_urb == NULL) {
-               dev_err(dev, "Can't allocate DTI URB\n");
-               goto error_dti_urb_alloc;
-       }
-       usb_fill_bulk_urb(
-               wa->dti_urb, wa->usb_dev,
-               usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
-               wa->dti_buf, wa->dti_buf_size,
-               wa_dti_cb, wa);
+       /* attempt to start the DTI ep processing. */
+       if (wa_dti_start(wa) < 0)
+               goto error;
 
-       wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
-       if (wa->buf_in_urb == NULL) {
-               dev_err(dev, "Can't allocate BUF-IN URB\n");
-               goto error_buf_in_urb_alloc;
-       }
-       usb_fill_bulk_urb(
-               wa->buf_in_urb, wa->usb_dev,
-               usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
-               NULL, 0, wa_buf_in_cb, wa);
-       result = usb_submit_urb(wa->dti_urb, GFP_KERNEL);
-       if (result < 0) {
-               dev_err(dev, "DTI Error: Could not submit DTI URB (%d) resetting\n",
-                       result);
-               goto error_dti_urb_submit;
-       }
-out:
        return;
 
-error_dti_urb_submit:
-       usb_put_urb(wa->buf_in_urb);
-       wa->buf_in_urb = NULL;
-error_buf_in_urb_alloc:
-       usb_put_urb(wa->dti_urb);
-       wa->dti_urb = NULL;
-error_dti_urb_alloc:
 error:
        wa_reset_all(wa);
 }
diff --git a/include/linux/phy/omap_control_phy.h b/include/linux/phy/omap_control_phy.h
new file mode 100644 (file)
index 0000000..5450403
--- /dev/null
@@ -0,0 +1,89 @@
+/*
+ * omap_control_phy.h - Header file for the PHY part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __OMAP_CONTROL_PHY_H__
+#define __OMAP_CONTROL_PHY_H__
+
+enum omap_control_phy_type {
+       OMAP_CTRL_TYPE_OTGHS = 1,       /* Mailbox OTGHS_CONTROL */
+       OMAP_CTRL_TYPE_USB2,    /* USB2_PHY, power down in CONTROL_DEV_CONF */
+       OMAP_CTRL_TYPE_PIPE3,   /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
+       OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
+       OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
+};
+
+struct omap_control_phy {
+       struct device *dev;
+
+       u32 __iomem *otghs_control;
+       u32 __iomem *power;
+       u32 __iomem *power_aux;
+
+       struct clk *sys_clk;
+
+       enum omap_control_phy_type type;
+};
+
+enum omap_control_usb_mode {
+       USB_MODE_UNDEFINED = 0,
+       USB_MODE_HOST,
+       USB_MODE_DEVICE,
+       USB_MODE_DISCONNECT,
+};
+
+#define        OMAP_CTRL_DEV_PHY_PD            BIT(0)
+
+#define        OMAP_CTRL_DEV_AVALID            BIT(0)
+#define        OMAP_CTRL_DEV_BVALID            BIT(1)
+#define        OMAP_CTRL_DEV_VBUSVALID         BIT(2)
+#define        OMAP_CTRL_DEV_SESSEND           BIT(3)
+#define        OMAP_CTRL_DEV_IDDIG             BIT(4)
+
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK         0x003FC000
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT        0xE
+
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK        0xFFC00000
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT       0x16
+
+#define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON       0x3
+#define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
+
+#define OMAP_CTRL_USB2_PHY_PD          BIT(28)
+
+#define AM437X_CTRL_USB2_PHY_PD                BIT(0)
+#define AM437X_CTRL_USB2_OTG_PD                BIT(1)
+#define AM437X_CTRL_USB2_OTGVDET_EN    BIT(19)
+#define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20)
+
+#if IS_ENABLED(CONFIG_OMAP_CONTROL_PHY)
+void omap_control_phy_power(struct device *dev, int on);
+void omap_control_usb_set_mode(struct device *dev,
+                              enum omap_control_usb_mode mode);
+#else
+
+static inline void omap_control_phy_power(struct device *dev, int on)
+{
+}
+
+static inline void omap_control_usb_set_mode(struct device *dev,
+       enum omap_control_usb_mode mode)
+{
+}
+#endif
+
+#endif /* __OMAP_CONTROL_PHY_H__ */
diff --git a/include/linux/phy/omap_usb.h b/include/linux/phy/omap_usb.h
new file mode 100644 (file)
index 0000000..dc2c541
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * omap_usb.h -- omap usb2 phy header file
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef __DRIVERS_OMAP_USB2_H
+#define __DRIVERS_OMAP_USB2_H
+
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+
+struct usb_dpll_params {
+       u16     m;
+       u8      n;
+       u8      freq:3;
+       u8      sd;
+       u32     mf;
+};
+
+struct omap_usb {
+       struct usb_phy          phy;
+       struct phy_companion    *comparator;
+       void __iomem            *pll_ctrl_base;
+       void __iomem            *phy_base;
+       struct device           *dev;
+       struct device           *control_dev;
+       struct clk              *wkupclk;
+       struct clk              *optclk;
+       u8                      flags;
+};
+
+struct usb_phy_data {
+       const char *label;
+       u8 flags;
+};
+
+/* Driver Flags */
+#define OMAP_USB2_HAS_START_SRP (1 << 0)
+#define OMAP_USB2_HAS_SET_VBUS (1 << 1)
+#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT (1 << 2)
+
+#define        phy_to_omapusb(x)       container_of((x), struct omap_usb, phy)
+
+#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
+extern int omap_usb2_set_comparator(struct phy_companion *comparator);
+#else
+static inline int omap_usb2_set_comparator(struct phy_companion *comparator)
+{
+       return -ENODEV;
+}
+#endif
+
+static inline u32 omap_usb_readl(void __iomem *addr, unsigned offset)
+{
+       return __raw_readl(addr + offset);
+}
+
+static inline void omap_usb_writel(void __iomem *addr, unsigned offset,
+       u32 data)
+{
+       __raw_writel(data, addr + offset);
+}
+
+#endif /* __DRIVERS_OMAP_USB_H */
index 3f83459dbb20b557ec067b9d14d8dbf1f1e56ba3..e2f5ca96cddc521fcc62a868039c8361f4e48997 100644 (file)
@@ -149,8 +149,11 @@ struct phy *phy_get(struct device *dev, const char *string);
 struct phy *phy_optional_get(struct device *dev, const char *string);
 struct phy *devm_phy_get(struct device *dev, const char *string);
 struct phy *devm_phy_optional_get(struct device *dev, const char *string);
+struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
+                           const char *con_id);
 void phy_put(struct phy *phy);
 void devm_phy_put(struct device *dev, struct phy *phy);
+struct phy *of_phy_get(struct device_node *np, const char *con_id);
 struct phy *of_phy_simple_xlate(struct device *dev,
        struct of_phandle_args *args);
 struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
@@ -251,6 +254,13 @@ static inline struct phy *devm_phy_optional_get(struct device *dev,
        return ERR_PTR(-ENOSYS);
 }
 
+static inline struct phy *devm_of_phy_get(struct device *dev,
+                                         struct device_node *np,
+                                         const char *con_id)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
 static inline void phy_put(struct phy *phy)
 {
 }
@@ -259,6 +269,11 @@ static inline void devm_phy_put(struct device *dev, struct phy *phy)
 {
 }
 
+static inline struct phy *of_phy_get(struct device_node *np, const char *con_id)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
 static inline struct phy *of_phy_simple_xlate(struct device *dev,
        struct of_phandle_args *args)
 {
index b55600a1edc3dd90317cc24d99b82b34cd6957ec..6b7ec376fb4d98baa5ec5a8fa33fd2d83b06343e 100644 (file)
@@ -57,6 +57,7 @@ struct ep_device;
  * @extra: descriptors following this endpoint in the configuration
  * @extralen: how many bytes of "extra" are valid
  * @enabled: URBs may be submitted to this endpoint
+ * @streams: number of USB-3 streams allocated on the endpoint
  *
  * USB requests are always queued to a given endpoint, identified by a
  * descriptor within an active interface in a given USB configuration.
@@ -71,6 +72,7 @@ struct usb_host_endpoint {
        unsigned char *extra;   /* Extra descriptors */
        int extralen;
        int enabled;
+       int streams;
 };
 
 /* host-side wrapper for one interface setting's parsed descriptors */
@@ -202,6 +204,8 @@ static inline void usb_set_intfdata(struct usb_interface *intf, void *data)
 struct usb_interface *usb_get_intf(struct usb_interface *intf);
 void usb_put_intf(struct usb_interface *intf);
 
+/* Hard limit */
+#define USB_MAXENDPOINTS       30
 /* this maximum is arbitrary */
 #define USB_MAXINTERFACES      32
 #define USB_MAXIADS            (USB_MAXINTERFACES/2)
index 708bd119627f30593b6df8443a3c15eeed74cd58..bbe779f640bed0c91869d527b67d9d797b8441b7 100644 (file)
@@ -25,6 +25,7 @@ struct ci_hdrc_platform_data {
         */
 #define CI_HDRC_DUAL_ROLE_NOT_OTG      BIT(4)
 #define CI_HDRC_IMX28_WRITE_FIX                BIT(5)
+#define CI_HDRC_FORCE_FULLSPEED                BIT(6)
        enum usb_dr_mode        dr_mode;
 #define CI_HDRC_CONTROLLER_RESET_EVENT         0
 #define CI_HDRC_CONTROLLER_STOPPED_EVENT       1
index efe8d8a7c7addebb55cc152afffa80c060d4725d..485cd5e2100c426ed29969c68bc50890c72e9aed 100644 (file)
@@ -143,6 +143,7 @@ struct usb_hcd {
        unsigned                authorized_default:1;
        unsigned                has_tt:1;       /* Integrated TT in root hub */
        unsigned                amd_resume_bug:1; /* AMD remote wakeup quirk */
+       unsigned                can_do_streams:1; /* HC supports streams */
 
        unsigned int            irq;            /* irq allocated */
        void __iomem            *regs;          /* device memory/io */
diff --git a/include/linux/usb/omap_control_usb.h b/include/linux/usb/omap_control_usb.h
deleted file mode 100644 (file)
index 69ae383..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-/*
- * omap_control_usb.h - Header file for the USB part of control module.
- *
- * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.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.
- *
- * Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __OMAP_CONTROL_USB_H__
-#define __OMAP_CONTROL_USB_H__
-
-enum omap_control_usb_type {
-       OMAP_CTRL_TYPE_OTGHS = 1,       /* Mailbox OTGHS_CONTROL */
-       OMAP_CTRL_TYPE_USB2,    /* USB2_PHY, power down in CONTROL_DEV_CONF */
-       OMAP_CTRL_TYPE_PIPE3,   /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
-       OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
-       OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
-};
-
-struct omap_control_usb {
-       struct device *dev;
-
-       u32 __iomem *otghs_control;
-       u32 __iomem *power;
-       u32 __iomem *power_aux;
-
-       struct clk *sys_clk;
-
-       enum omap_control_usb_type type;
-};
-
-enum omap_control_usb_mode {
-       USB_MODE_UNDEFINED = 0,
-       USB_MODE_HOST,
-       USB_MODE_DEVICE,
-       USB_MODE_DISCONNECT,
-};
-
-#define        OMAP_CTRL_DEV_PHY_PD            BIT(0)
-
-#define        OMAP_CTRL_DEV_AVALID            BIT(0)
-#define        OMAP_CTRL_DEV_BVALID            BIT(1)
-#define        OMAP_CTRL_DEV_VBUSVALID         BIT(2)
-#define        OMAP_CTRL_DEV_SESSEND           BIT(3)
-#define        OMAP_CTRL_DEV_IDDIG             BIT(4)
-
-#define        OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK       0x003FC000
-#define        OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT      0xE
-
-#define        OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK      0xFFC00000
-#define        OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT     0x16
-
-#define        OMAP_CTRL_USB3_PHY_TX_RX_POWERON        0x3
-#define        OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF       0x0
-
-#define OMAP_CTRL_USB2_PHY_PD          BIT(28)
-
-#define AM437X_CTRL_USB2_PHY_PD                BIT(0)
-#define AM437X_CTRL_USB2_OTG_PD                BIT(1)
-#define AM437X_CTRL_USB2_OTGVDET_EN    BIT(19)
-#define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20)
-
-#if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
-extern void omap_control_usb_phy_power(struct device *dev, int on);
-extern void omap_control_usb_set_mode(struct device *dev,
-       enum omap_control_usb_mode mode);
-#else
-
-static inline void omap_control_usb_phy_power(struct device *dev, int on)
-{
-}
-
-static inline void omap_control_usb_set_mode(struct device *dev,
-       enum omap_control_usb_mode mode)
-{
-}
-#endif
-
-#endif /* __OMAP_CONTROL_USB_H__ */
diff --git a/include/linux/usb/omap_usb.h b/include/linux/usb/omap_usb.h
deleted file mode 100644 (file)
index 6ae2936..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * omap_usb.h -- omap usb2 phy header file
- *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.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.
- *
- * Author: Kishon Vijay Abraham I <kishon@ti.com>
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- */
-
-#ifndef __DRIVERS_OMAP_USB2_H
-#define __DRIVERS_OMAP_USB2_H
-
-#include <linux/io.h>
-#include <linux/usb/otg.h>
-
-struct usb_dpll_params {
-       u16     m;
-       u8      n;
-       u8      freq:3;
-       u8      sd;
-       u32     mf;
-};
-
-struct omap_usb {
-       struct usb_phy          phy;
-       struct phy_companion    *comparator;
-       void __iomem            *pll_ctrl_base;
-       struct device           *dev;
-       struct device           *control_dev;
-       struct clk              *wkupclk;
-       struct clk              *sys_clk;
-       struct clk              *optclk;
-       u8                      is_suspended:1;
-};
-
-#define        phy_to_omapusb(x)       container_of((x), struct omap_usb, phy)
-
-#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
-extern int omap_usb2_set_comparator(struct phy_companion *comparator);
-#else
-static inline int omap_usb2_set_comparator(struct phy_companion *comparator)
-{
-       return -ENODEV;
-}
-#endif
-
-static inline u32 omap_usb_readl(void __iomem *addr, unsigned offset)
-{
-       return __raw_readl(addr + offset);
-}
-
-static inline void omap_usb_writel(void __iomem *addr, unsigned offset,
-       u32 data)
-{
-       __raw_writel(data, addr + offset);
-}
-
-#endif /* __DRIVERS_OMAP_USB_H */
index 6c0b1c513db72ce9632396223ed99aca20ff3efb..353053a33f21e11af2e11a52fdebf535cfaeaab4 100644 (file)
@@ -111,6 +111,13 @@ struct usb_phy {
        int     (*set_suspend)(struct usb_phy *x,
                                int suspend);
 
+       /*
+        * Set wakeup enable for PHY, in that case, the PHY can be
+        * woken up from suspend status due to external events,
+        * like vbus change, dp/dm change and id.
+        */
+       int     (*set_wakeup)(struct usb_phy *x, bool enabled);
+
        /* notify phy connect status change */
        int     (*notify_connect)(struct usb_phy *x,
                        enum usb_device_speed speed);
@@ -264,6 +271,15 @@ usb_phy_set_suspend(struct usb_phy *x, int suspend)
                return 0;
 }
 
+static inline int
+usb_phy_set_wakeup(struct usb_phy *x, bool enabled)
+{
+       if (x && x->set_wakeup)
+               return x->set_wakeup(x, enabled);
+       else
+               return 0;
+}
+
 static inline int
 usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed)
 {
index 704a1ab8240ca124f29c5ce361c871090d28ea5b..9bb547c7bce7c7ce0942fdb4e313188d4878b371 100644 (file)
@@ -190,7 +190,8 @@ static inline void usb_set_serial_data(struct usb_serial *serial, void *data)
  * @num_ports: the number of different ports this device will have.
  * @bulk_in_size: minimum number of bytes to allocate for bulk-in buffer
  *     (0 = end-point size)
- * @bulk_out_size: bytes to allocate for bulk-out buffer (0 = end-point size)
+ * @bulk_out_size: minimum number of bytes to allocate for bulk-out buffer
+ *     (0 = end-point size)
  * @calc_num_ports: pointer to a function to determine how many ports this
  *     device has dynamically.  It will be called after the probe()
  *     callback is called, but before attach()
index 5499ab5c94bd486cf96d198ac310d29d2ab7e393..3fc8e8b9f04318528b4513ee927e8db4f72ab8b7 100644 (file)
@@ -9,7 +9,7 @@ struct iu {
        __u8 iu_id;
        __u8 rsvd1;
        __be16 tag;
-};
+} __attribute__((__packed__));
 
 enum {
        IU_ID_COMMAND           = 0x01,
@@ -52,7 +52,7 @@ struct command_iu {
        __u8 rsvd7;
        struct scsi_lun lun;
        __u8 cdb[16];   /* XXX: Overflow-checking tools may misunderstand */
-};
+} __attribute__((__packed__));
 
 struct task_mgmt_iu {
        __u8 iu_id;
@@ -62,7 +62,7 @@ struct task_mgmt_iu {
        __u8 rsvd2;
        __be16 task_tag;
        struct scsi_lun lun;
-};
+} __attribute__((__packed__));
 
 /*
  * Also used for the Read Ready and Write Ready IUs since they have the
@@ -77,15 +77,15 @@ struct sense_iu {
        __u8 rsvd7[7];
        __be16 len;
        __u8 sense[SCSI_SENSE_BUFFERSIZE];
-};
+} __attribute__((__packed__));
 
-struct response_ui {
+struct response_iu {
        __u8 iu_id;
        __u8 rsvd1;
        __be16 tag;
-       __be16 add_response_info;
+       __u8 add_response_info[3];
        __u8 response_code;
-};
+} __attribute__((__packed__));
 
 struct usb_pipe_usage_descriptor {
        __u8  bLength;
index 630356866030d88a355390a932105ff9c86b98bb..1a64b26046ed61e854d3c6c2dcafa256aca50c69 100644 (file)
                /* Initial READ(10) (and others) must be retried */     \
        US_FLAG(WRITE_CACHE,    0x00200000)                     \
                /* Write Cache status is not available */       \
-       US_FLAG(NEEDS_CAP16,    0x00400000)
-               /* cannot handle READ_CAPACITY_10 */
+       US_FLAG(NEEDS_CAP16,    0x00400000)                     \
+               /* cannot handle READ_CAPACITY_10 */            \
+       US_FLAG(IGNORE_UAS,     0x00800000)                     \
+               /* Device advertises UAS but it is broken */
 
 #define US_FLAG(name, value)   US_FL_##name = value ,
 enum { US_DO_ALL_FLAGS };
index d6b01283f85c4761bbdc2b241625952c07680143..2a4b4a72a4f915ee0c0e16db893713bc622359db 100644 (file)
 
 enum {
        FUNCTIONFS_DESCRIPTORS_MAGIC = 1,
-       FUNCTIONFS_STRINGS_MAGIC     = 2
+       FUNCTIONFS_STRINGS_MAGIC = 2,
+       FUNCTIONFS_DESCRIPTORS_MAGIC_V2 = 3,
 };
 
+enum functionfs_flags {
+       FUNCTIONFS_HAS_FS_DESC = 1,
+       FUNCTIONFS_HAS_HS_DESC = 2,
+       FUNCTIONFS_HAS_SS_DESC = 4,
+};
 
 #ifndef __KERNEL__
 
@@ -28,30 +34,40 @@ struct usb_endpoint_descriptor_no_audio {
 } __attribute__((packed));
 
 
-/*
- * All numbers must be in little endian order.
- */
-
-struct usb_functionfs_descs_head {
-       __le32 magic;
-       __le32 length;
-       __le32 fs_count;
-       __le32 hs_count;
-} __attribute__((packed));
-
 /*
  * Descriptors format:
  *
  * | off | name      | type         | description                          |
  * |-----+-----------+--------------+--------------------------------------|
- * |   0 | magic     | LE32         | FUNCTIONFS_{FS,HS}_DESCRIPTORS_MAGIC |
+ * |   0 | magic     | LE32         | FUNCTIONFS_DESCRIPTORS_MAGIC_V2      |
+ * |   4 | length    | LE32         | length of the whole data chunk       |
+ * |   8 | flags     | LE32         | combination of functionfs_flags      |
+ * |     | fs_count  | LE32         | number of full-speed descriptors     |
+ * |     | hs_count  | LE32         | number of high-speed descriptors     |
+ * |     | ss_count  | LE32         | number of super-speed descriptors    |
+ * |     | fs_descrs | Descriptor[] | list of full-speed descriptors       |
+ * |     | hs_descrs | Descriptor[] | list of high-speed descriptors       |
+ * |     | ss_descrs | Descriptor[] | list of super-speed descriptors      |
+ *
+ * Depending on which flags are set, various fields may be missing in the
+ * structure.  Any flags that are not recognised cause the whole block to be
+ * rejected with -ENOSYS.
+ *
+ * Legacy descriptors format:
+ *
+ * | off | name      | type         | description                          |
+ * |-----+-----------+--------------+--------------------------------------|
+ * |   0 | magic     | LE32         | FUNCTIONFS_DESCRIPTORS_MAGIC         |
  * |   4 | length    | LE32         | length of the whole data chunk       |
  * |   8 | fs_count  | LE32         | number of full-speed descriptors     |
  * |  12 | hs_count  | LE32         | number of high-speed descriptors     |
  * |  16 | fs_descrs | Descriptor[] | list of full-speed descriptors       |
  * |     | hs_descrs | Descriptor[] | list of high-speed descriptors       |
  *
- * descs are just valid USB descriptors and have the following format:
+ * All numbers must be in little endian order.
+ *
+ * Descriptor[] is an array of valid USB descriptors which have the following
+ * format:
  *
  * | off | name            | type | description              |
  * |-----+-----------------+------+--------------------------|
index 0c65e4b1261717f71b7bdeef94da72b608eec392..abe5f4bd4d82e0df75233b473316465999684cce 100644 (file)
@@ -102,7 +102,10 @@ struct usbdevfs_urb {
        int buffer_length;
        int actual_length;
        int start_frame;
-       int number_of_packets;
+       union {
+               int number_of_packets;  /* Only used for isoc urbs */
+               unsigned int stream_id; /* Only used with bulk streams */
+       };
        int error_count;
        unsigned int signr;     /* signal to be sent on completion,
                                  or 0 if none should be sent. */
@@ -144,6 +147,11 @@ struct usbdevfs_disconnect_claim {
        char driver[USBDEVFS_MAXDRIVERNAME + 1];
 };
 
+struct usbdevfs_streams {
+       unsigned int num_streams; /* Not used by USBDEVFS_FREE_STREAMS */
+       unsigned int num_eps;
+       unsigned char eps[0];
+};
 
 #define USBDEVFS_CONTROL           _IOWR('U', 0, struct usbdevfs_ctrltransfer)
 #define USBDEVFS_CONTROL32           _IOWR('U', 0, struct usbdevfs_ctrltransfer32)
@@ -176,5 +184,7 @@ struct usbdevfs_disconnect_claim {
 #define USBDEVFS_RELEASE_PORT      _IOR('U', 25, unsigned int)
 #define USBDEVFS_GET_CAPABILITIES  _IOR('U', 26, __u32)
 #define USBDEVFS_DISCONNECT_CLAIM  _IOR('U', 27, struct usbdevfs_disconnect_claim)
+#define USBDEVFS_ALLOC_STREAMS     _IOR('U', 28, struct usbdevfs_streams)
+#define USBDEVFS_FREE_STREAMS      _IOR('U', 29, struct usbdevfs_streams)
 
 #endif /* _UAPI_LINUX_USBDEVICE_FS_H */