]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'usb/usb-next'
authorThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:59:20 +0000 (14:59 +0200)
committerThierry Reding <treding@nvidia.com>
Thu, 24 Oct 2013 12:59:20 +0000 (14:59 +0200)
Conflicts:
drivers/Kconfig

163 files changed:
Documentation/devicetree/bindings/phy/phy-bindings.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/samsung-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/msm-hsusb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/usb/omap-usb.txt
Documentation/devicetree/bindings/usb/usb-phy.txt
Documentation/devicetree/bindings/video/exynos_dp.txt
Documentation/phy.txt [new file with mode: 0644]
Documentation/pps/pps.txt
MAINTAINERS
arch/arm/boot/dts/omap3-beagle-xm.dts
arch/arm/boot/dts/omap3-evm.dts
arch/arm/boot/dts/omap3-overo.dtsi
arch/arm/boot/dts/omap4.dtsi
arch/arm/boot/dts/omap5.dtsi
arch/arm/boot/dts/twl4030.dtsi
arch/arm/configs/ep93xx_defconfig
arch/arm/mach-exynos/include/mach/regs-pmu.h
arch/arm/mach-omap2/twl-common.c
arch/arm/mach-s5pv210/include/mach/regs-clock.h
arch/arm/plat-samsung/Kconfig
arch/arm/plat-samsung/Makefile
arch/arm/plat-samsung/devs.c
arch/arm/plat-samsung/include/plat/devs.h
arch/arm/plat-samsung/setup-mipiphy.c [deleted file]
drivers/Kconfig
drivers/Makefile
drivers/media/platform/exynos4-is/Kconfig
drivers/media/platform/exynos4-is/mipi-csis.c
drivers/phy/Kconfig [new file with mode: 0644]
drivers/phy/Makefile [new file with mode: 0644]
drivers/phy/phy-core.c [new file with mode: 0644]
drivers/phy/phy-exynos-dp-video.c [new file with mode: 0644]
drivers/phy/phy-exynos-mipi-video.c [new file with mode: 0644]
drivers/phy/phy-omap-usb2.c [moved from drivers/usb/phy/phy-omap-usb2.c with 81% similarity]
drivers/phy/phy-twl4030-usb.c [moved from drivers/usb/phy/phy-twl4030-usb.c with 94% similarity]
drivers/usb/chipidea/bits.h
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/udc.c
drivers/usb/core/devio.c
drivers/usb/core/driver.c
drivers/usb/core/file.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/quirks.c
drivers/usb/core/sysfs.c
drivers/usb/core/urb.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/ep0.c
drivers/usb/early/ehci-dbgp.c
drivers/usb/gadget/amd5536udc.c
drivers/usb/gadget/composite.c
drivers/usb/gadget/dummy_hcd.c
drivers/usb/gadget/g_ffs.c
drivers/usb/gadget/goku_udc.c
drivers/usb/gadget/mv_u3d_core.c
drivers/usb/gadget/net2280.c
drivers/usb/gadget/pch_udc.c
drivers/usb/gadget/rndis.c
drivers/usb/gadget/tcm_usb_gadget.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-dbg.c
drivers/usb/host/ehci-exynos.c [moved from drivers/usb/host/ehci-s5p.c with 55% similarity]
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-grlib.c
drivers/usb/host/ehci-hcd.c
drivers/usb/host/ehci-mem.c
drivers/usb/host/ehci-msm.c
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-octeon.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-pmcmsp.c
drivers/usb/host/ehci-ppc-of.c
drivers/usb/host/ehci-ps3.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-sched.c
drivers/usb/host/ehci-sead3.c
drivers/usb/host/ehci-sh.c
drivers/usb/host/ehci-sysfs.c
drivers/usb/host/ehci-tegra.c
drivers/usb/host/ehci-tilegx.c
drivers/usb/host/ehci-w90x900.c
drivers/usb/host/ehci-xilinx-of.c
drivers/usb/host/ehci.h
drivers/usb/host/fotg210-hcd.c
drivers/usb/host/fusbh200-hcd.c
drivers/usb/host/hwa-hc.c
drivers/usb/host/isp1362-hcd.c
drivers/usb/host/ohci-at91.c
drivers/usb/host/ohci-dbg.c
drivers/usb/host/ohci-ep93xx.c
drivers/usb/host/ohci-exynos.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-hub.c
drivers/usb/host/ohci-nxp.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-omap3.c
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-platform.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-s3c2410.c
drivers/usb/host/ohci-sm501.c
drivers/usb/host/ohci-spear.c
drivers/usb/host/pci-quirks.c
drivers/usb/host/pci-quirks.h
drivers/usb/host/sl811-hcd.c
drivers/usb/host/uhci-debug.c
drivers/usb/host/uhci-hub.c
drivers/usb/host/uhci-pci.c
drivers/usb/host/uhci-platform.c
drivers/usb/host/whci/hcd.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/usbtest.c
drivers/usb/musb/Kconfig
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/omap2430.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
drivers/usb/phy/phy-omap-control.c
drivers/usb/phy/phy-omap-usb3.c
drivers/usb/phy/phy-ulpi-viewport.c
drivers/usb/serial/cyberjack.c
drivers/usb/serial/ftdi_sio.c
drivers/usb/serial/generic.c
drivers/usb/serial/mos7840.c
drivers/usb/wusbcore/cbaf.c
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
drivers/video/exynos/Kconfig
drivers/video/exynos/exynos_dp_core.c
drivers/video/exynos/exynos_dp_core.h
drivers/video/exynos/exynos_dp_reg.c
drivers/video/exynos/exynos_mipi_dsi.c
include/linux/i2c/twl.h
include/linux/phy/phy.h [new file with mode: 0644]
include/linux/platform_data/mipi-csis.h
include/linux/platform_data/usb-ehci-s5p.h [deleted file]
include/linux/platform_data/usb-ohci-exynos.h [deleted file]
include/linux/usb.h
include/linux/usb/hcd.h
include/linux/usb/intel_mid_otg.h [deleted file]
include/linux/usb/musb.h
include/linux/usb/omap_control_usb.h
include/linux/usb/serial.h
include/linux/usb/wusb-wa.h
include/video/exynos_dp.h [deleted file]
include/video/exynos_mipi_dsim.h

diff --git a/Documentation/devicetree/bindings/phy/phy-bindings.txt b/Documentation/devicetree/bindings/phy/phy-bindings.txt
new file mode 100644 (file)
index 0000000..8ae844f
--- /dev/null
@@ -0,0 +1,66 @@
+This document explains only the device tree data binding. For general
+information about PHY subsystem refer to Documentation/phy.txt
+
+PHY device node
+===============
+
+Required Properties:
+#phy-cells:    Number of cells in a PHY specifier;  The meaning of all those
+               cells is defined by the binding for the phy node. The PHY
+               provider can use the values in cells to find the appropriate
+               PHY.
+
+For example:
+
+phys: phy {
+    compatible = "xxx";
+    reg = <...>;
+    .
+    .
+    #phy-cells = <1>;
+    .
+    .
+};
+
+That node describes an IP block (PHY provider) that implements 2 different PHYs.
+In order to differentiate between these 2 PHYs, an additonal specifier should be
+given while trying to get a reference to it.
+
+PHY user node
+=============
+
+Required Properties:
+phys : the phandle for the PHY device (used by the PHY subsystem)
+phy-names : the names of the PHY corresponding to the PHYs present in the
+           *phys* phandle
+
+Example 1:
+usb1: usb_otg_ss@xxx {
+    compatible = "xxx";
+    reg = <xxx>;
+    .
+    .
+    phys = <&usb2_phy>, <&usb3_phy>;
+    phy-names = "usb2phy", "usb3phy";
+    .
+    .
+};
+
+This node represents a controller that uses two PHYs, one for usb2 and one for
+usb3.
+
+Example 2:
+usb2: usb_otg_ss@xxx {
+    compatible = "xxx";
+    reg = <xxx>;
+    .
+    .
+    phys = <&phys 1>;
+    phy-names = "usbphy";
+    .
+    .
+};
+
+This node represents a controller that uses one of the PHYs of the PHY provider
+device defined previously. Note that the phy handle has an additional specifier
+"1" to differentiate between the two PHYs.
diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
new file mode 100644 (file)
index 0000000..c0fccaa
--- /dev/null
@@ -0,0 +1,22 @@
+Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be "samsung,s5pv210-mipi-video-phy";
+- reg : offset and length of the MIPI DPHY register set;
+- #phy-cells : from the generic phy bindings, must be 1;
+
+For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in
+the PHY specifier identifies the PHY and its meaning is as follows:
+  0 - MIPI CSIS 0,
+  1 - MIPI DSIM 0,
+  2 - MIPI CSIS 1,
+  3 - MIPI DSIM 1.
+
+Samsung EXYNOS SoC series Display Port PHY
+-------------------------------------------------
+
+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;
diff --git a/Documentation/devicetree/bindings/usb/msm-hsusb.txt b/Documentation/devicetree/bindings/usb/msm-hsusb.txt
new file mode 100644 (file)
index 0000000..5ea26c6
--- /dev/null
@@ -0,0 +1,17 @@
+MSM SoC HSUSB controllers
+
+EHCI
+
+Required properties:
+- compatible:  Should contain "qcom,ehci-host"
+- regs:                        offset and length of the register set in the memory map
+- usb-phy:             phandle for the PHY device
+
+Example EHCI controller device node:
+
+       ehci: ehci@f9a55000 {
+               compatible = "qcom,ehci-host";
+               reg = <0xf9a55000 0x400>;
+               usb-phy = <&usb_otg>;
+       };
+
index 9088ab09e20028c99257244975600fa24f44c403..090e5e22bd2b831a1b05885d5d01db228aec9bef 100644 (file)
@@ -3,9 +3,6 @@ OMAP GLUE AND OTHER OMAP SPECIFIC COMPONENTS
 OMAP MUSB GLUE
  - compatible : Should be "ti,omap4-musb" or "ti,omap3-musb"
  - ti,hwmods : must be "usb_otg_hs"
- - ti,has-mailbox : to specify that omap uses an external mailbox
-   (in control module) to communicate with the musb core during device connect
-   and disconnect.
  - multipoint : Should be "1" indicating the musb controller supports
    multipoint. This is a MUSB configuration-specific setting.
  - num-eps : Specifies the number of endpoints. This is also a
@@ -19,6 +16,9 @@ OMAP MUSB GLUE
  - power : Should be "50". This signifies the controller can supply up to
    100mA when operating in host mode.
  - usb-phy : the phandle for the PHY device
+ - phys : the phandle for the PHY device (used by generic PHY framework)
+ - phy-names : the names of the PHY corresponding to the PHYs present in the
+   *phy* phandle.
 
 Optional properties:
  - ctrl-module : phandle of the control module this glue uses to write to
@@ -28,11 +28,12 @@ SOC specific device node entry
 usb_otg_hs: usb_otg_hs@4a0ab000 {
        compatible = "ti,omap4-musb";
        ti,hwmods = "usb_otg_hs";
-       ti,has-mailbox;
        multipoint = <1>;
        num-eps = <16>;
        ram-bits = <12>;
        ctrl-module = <&omap_control_usb>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
 };
 
 Board specific device node entry
@@ -78,22 +79,22 @@ omap_dwc3 {
 OMAP CONTROL USB
 
 Required properties:
- - compatible: Should be "ti,omap-control-usb"
+ - 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.
  - reg : Address and length of the register set for the device. It contains
-   the address of "control_dev_conf" and "otghs_control" or "phy_power_usb"
-   depending upon omap4 or omap5.
- - reg-names: The names of the register addresses corresponding to the registers
-   filled in "reg".
- - ti,type: This is used to differentiate whether the control module has
-   usb mailbox or usb3 phy power. omap4 has usb mailbox in control module to
-   notify events to the musb core and omap5 has usb3 phy power register to
-   power on usb3 phy. Should be "1" if it has mailbox and "2" if it has usb3
-   phy power.
+   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,omap-control-usb";
-       reg = <0x4a002300 0x4>,
-             <0x4a00233c 0x4>;
-       reg-names = "control_dev_conf", "otghs_control";
-       ti,type = <1>;
+       compatible = "ti,control-phy-otghs";
+       reg = <0x4a00233c 0x4>;
+       reg-names = "otghs_control";
 };
index 61496f5cb095b39c149705e7f80c3fba379c434d..c0245c888982b9be44bad31e76664880e2449e2c 100644 (file)
@@ -5,6 +5,8 @@ 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
@@ -16,6 +18,7 @@ usb2phy@4a0ad080 {
        compatible = "ti,omap-usb2";
        reg = <0x4a0ad080 0x58>;
        ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
 };
 
 OMAP USB3 PHY
@@ -25,6 +28,8 @@ Required properties:
  - 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
@@ -39,4 +44,5 @@ usb3phy@4a084400 {
              <0x4a084c00 0x40>;
        reg-names = "phy_rx", "phy_tx", "pll_ctrl";
        ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
 };
index 84f10c16cb383497b0fc5a736d4ce03a59668c4c..3289d76a21d0b2d01b81620e0c19fb10af29cf6b 100644 (file)
@@ -6,10 +6,10 @@ We use two nodes:
        -dptx-phy node(defined inside dp-controller node)
 
 For the DP-PHY initialization, we use the dptx-phy node.
-Required properties for dptx-phy:
-       -reg:
+Required properties for dptx-phy: deprecated, use phys and phy-names
+       -reg: deprecated
                Base address of DP PHY register.
-       -samsung,enable-mask:
+       -samsung,enable-mask: deprecated
                The bit-mask used to enable/disable DP PHY.
 
 For the Panel initialization, we read data from dp-controller node.
@@ -27,6 +27,10 @@ Required properties for dp-controller:
                from common clock binding: Shall be "dp".
        -interrupt-parent:
                phandle to Interrupt combiner node.
+       -phys:
+               from general PHY binding: the phandle for the PHY device.
+       -phy-names:
+               from general PHY binding: Should be "dp".
        -samsung,color-space:
                input video data format.
                        COLOR_RGB = 0, COLOR_YCBCR422 = 1, COLOR_YCBCR444 = 2
@@ -68,11 +72,8 @@ SOC specific portion:
                clocks = <&clock 342>;
                clock-names = "dp";
 
-               dptx-phy {
-                       reg = <0x10040720>;
-                       samsung,enable-mask = <1>;
-               };
-
+               phys = <&dp_phy>;
+               phy-names = "dp";
        };
 
 Board Specific portion:
diff --git a/Documentation/phy.txt b/Documentation/phy.txt
new file mode 100644 (file)
index 0000000..0103e4b
--- /dev/null
@@ -0,0 +1,166 @@
+                           PHY SUBSYSTEM
+                 Kishon Vijay Abraham I <kishon@ti.com>
+
+This document explains the Generic PHY Framework along with the APIs provided,
+and how-to-use.
+
+1. Introduction
+
+*PHY* is the abbreviation for physical layer. It is used to connect a device
+to the physical medium e.g., the USB controller has a PHY to provide functions
+such as serialization, de-serialization, encoding, decoding and is responsible
+for obtaining the required data transmission rate. Note that some USB
+controllers have PHY functionality embedded into it and others use an external
+PHY. Other peripherals that use PHY include Wireless LAN, Ethernet,
+SATA etc.
+
+The intention of creating this framework is to bring the PHY drivers spread
+all over the Linux kernel to drivers/phy to increase code re-use and for
+better code maintainability.
+
+This framework will be of use only to devices that use external PHY (PHY
+functionality is not embedded within the controller).
+
+2. Registering/Unregistering the PHY provider
+
+PHY provider refers to an entity that implements one or more PHY instances.
+For the simple case where the PHY provider implements only a single instance of
+the PHY, the framework provides its own implementation of of_xlate in
+of_phy_simple_xlate. If the PHY provider implements multiple instances, it
+should provide its own implementation of of_xlate. of_xlate is used only for
+dt boot case.
+
+#define of_phy_provider_register(dev, xlate)    \
+        __of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+#define devm_of_phy_provider_register(dev, xlate)       \
+        __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+of_phy_provider_register and devm_of_phy_provider_register macros can be used to
+register the phy_provider and it takes device and of_xlate as
+arguments. For the dt boot case, all PHY providers should use one of the above
+2 macros to register the PHY provider.
+
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider);
+void of_phy_provider_unregister(struct phy_provider *phy_provider);
+
+devm_of_phy_provider_unregister and of_phy_provider_unregister can be used to
+unregister the PHY.
+
+3. Creating the PHY
+
+The PHY driver should create the PHY in order for other peripheral controllers
+to make use of it. The PHY framework provides 2 APIs to create the PHY.
+
+struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
+        struct phy_init_data *init_data);
+struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
+       struct phy_init_data *init_data);
+
+The PHY drivers can use one of the above 2 APIs to create the PHY by passing
+the device pointer, phy ops and init_data.
+phy_ops is a set of function pointers for performing PHY operations such as
+init, exit, power_on and power_off. *init_data* is mandatory to get a reference
+to the PHY in the case of non-dt boot. See section *Board File Initialization*
+on how init_data should be used.
+
+Inorder to dereference the private data (in phy_ops), the phy provider driver
+can use phy_set_drvdata() after creating the PHY and use phy_get_drvdata() in
+phy_ops to get back the private data.
+
+4. Getting a reference to the PHY
+
+Before the controller can make use of the PHY, it has to get a reference to
+it. This framework provides the following APIs to get a reference to the PHY.
+
+struct phy *phy_get(struct device *dev, const char *string);
+struct phy *devm_phy_get(struct device *dev, const char *string);
+
+phy_get and devm_phy_get can be used to get the PHY. In the case of dt boot,
+the string arguments should contain the phy name as given in the dt data and
+in the case of non-dt boot, it should contain the label of the PHY.
+The only difference between the two APIs is that devm_phy_get associates the
+device with the PHY using devres on successful PHY get. On driver detach,
+release function is invoked on the the devres data and devres data is freed.
+
+5. Releasing a reference to the PHY
+
+When the controller no longer needs the PHY, it has to release the reference
+to the PHY it has obtained using the APIs mentioned in the above section. The
+PHY framework provides 2 APIs to release a reference to the PHY.
+
+void phy_put(struct phy *phy);
+void devm_phy_put(struct device *dev, struct phy *phy);
+
+Both these APIs are used to release a reference to the PHY and devm_phy_put
+destroys the devres associated with this PHY.
+
+6. Destroying the PHY
+
+When the driver that created the PHY is unloaded, it should destroy the PHY it
+created using one of the following 2 APIs.
+
+void phy_destroy(struct phy *phy);
+void devm_phy_destroy(struct device *dev, struct phy *phy);
+
+Both these APIs destroy the PHY and devm_phy_destroy destroys the devres
+associated with this PHY.
+
+7. PM Runtime
+
+This subsystem is pm runtime enabled. So while creating the PHY,
+pm_runtime_enable of the phy device created by this subsystem is called and
+while destroying the PHY, pm_runtime_disable is called. Note that the phy
+device created by this subsystem will be a child of the device that calls
+phy_create (PHY provider device).
+
+So pm_runtime_get_sync of the phy_device created by this subsystem will invoke
+pm_runtime_get_sync of PHY provider device because of parent-child relationship.
+It should also be noted that phy_power_on and phy_power_off performs
+phy_pm_runtime_get_sync and phy_pm_runtime_put respectively.
+There are exported APIs like phy_pm_runtime_get, phy_pm_runtime_get_sync,
+phy_pm_runtime_put, phy_pm_runtime_put_sync, phy_pm_runtime_allow and
+phy_pm_runtime_forbid for performing PM operations.
+
+8. Board File Initialization
+
+Certain board file initialization is necessary in order to get a reference
+to the PHY in the case of non-dt boot.
+Say we have a single device that implements 3 PHYs that of USB, SATA and PCIe,
+then in the board file the following initialization should be done.
+
+struct phy_consumer consumers[] = {
+       PHY_CONSUMER("dwc3.0", "usb"),
+       PHY_CONSUMER("pcie.0", "pcie"),
+       PHY_CONSUMER("sata.0", "sata"),
+};
+PHY_CONSUMER takes 2 parameters, first is the device name of the controller
+(PHY consumer) and second is the port name.
+
+struct phy_init_data init_data = {
+       .consumers = consumers,
+       .num_consumers = ARRAY_SIZE(consumers),
+};
+
+static const struct platform_device pipe3_phy_dev = {
+       .name = "pipe3-phy",
+       .id = -1,
+       .dev = {
+               .platform_data = {
+                       .init_data = &init_data,
+               },
+       },
+};
+
+then, while doing phy_create, the PHY driver should pass this init_data
+       phy_create(dev, ops, pdata->init_data);
+
+and the controller driver (phy consumer) should pass the port name along with
+the device to get a reference to the PHY
+       phy_get(dev, "pcie");
+
+9. DeviceTree Binding
+
+The documentation for PHY dt binding can be found @
+Documentation/devicetree/bindings/phy/phy-bindings.txt
index d35dcdd82ff6a6c93b2dae00be717080b9b21cb1..c03b1be5eb1517b7b16cbddd6d2999101020ca53 100644 (file)
@@ -66,6 +66,21 @@ In LinuxPPS the PPS sources are simply char devices usually mapped
 into files /dev/pps0, /dev/pps1, etc..
 
 
+PPS with USB to serial devices
+------------------------------
+
+It is possible to grab the PPS from an USB to serial device. However,
+you should take into account the latencies and jitter introduced by
+the USB stack. Users has reported clock instability around +-1ms when
+synchronized with PPS through USB. This isn't suited for time server
+synchronization.
+
+If your device doesn't report PPS, you can check that the feature is
+supported by its driver. Most of the time, you only need to add a call
+to usb_serial_handle_dcd_change after checking the DCD status (see
+ch341 and pl2303 examples).
+
+
 Coding example
 --------------
 
index 316572ad49f8a8dd02ab5b2c601aaf5938542f11..0ffe927dbbd906f52be196a9c36a35d6c286889c 100644 (file)
@@ -3698,6 +3698,14 @@ S:       Maintained
 F:     include/asm-generic/
 F:     include/uapi/asm-generic/
 
+GENERIC PHY FRAMEWORK
+M:     Kishon Vijay Abraham I <kishon@ti.com>
+L:     linux-kernel@vger.kernel.org
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy.git
+S:     Supported
+F:     drivers/phy/
+F:     include/linux/phy/
+
 GENERIC UIO DRIVER FOR PCI DEVICES
 M:     "Michael S. Tsirkin" <mst@redhat.com>
 L:     kvm@vger.kernel.org
index 2816bf61267231fd7ec2dd11f87c6da9ed4b3311..ba4dcfc6b7214d5e3d63488edc2e8157f130ac19 100644 (file)
 &usb_otg_hs {
        interface-type = <0>;
        usb-phy = <&usb2_phy>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
        mode = <3>;
        power = <50>;
 };
index 7d4329d179c43e24cae9bfaddb4c5b1fbc626535..4134dd05c3a43835e56963aebb25a18347701588 100644 (file)
@@ -70,6 +70,8 @@
 &usb_otg_hs {
        interface-type = <0>;
        usb-phy = <&usb2_phy>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
        mode = <3>;
        power = <50>;
 };
index 8f1abec78275635535f9faf4cdd03c60d2daf909..a461d2fd1fb0e81862805ccec939e1c1b3a41244 100644 (file)
@@ -76,6 +76,8 @@
 &usb_otg_hs {
        interface-type = <0>;
        usb-phy = <&usb2_phy>;
+       phys = <&usb2_phy>;
+       phy-names = "usb2-phy";
        mode = <3>;
        power = <50>;
 };
index 22d9f2b593d461eb5bde36a35f24e2eaea40e298..ea4054bfdfd4964c57573813de9306f5f538c034 100644 (file)
                        usb2_phy: usb2phy@4a0ad080 {
                                compatible = "ti,omap-usb2";
                                reg = <0x4a0ad080 0x58>;
-                               ctrl-module = <&omap_control_usb>;
+                               ctrl-module = <&omap_control_usb2phy>;
+                               #phy-cells = <0>;
                        };
                };
 
                        };
                };
 
-               omap_control_usb: omap-control-usb@4a002300 {
-                       compatible = "ti,omap-control-usb";
-                       reg = <0x4a002300 0x4>,
-                             <0x4a00233c 0x4>;
-                       reg-names = "control_dev_conf", "otghs_control";
-                       ti,type = <1>;
+               omap_control_usb2phy: control-phy@4a002300 {
+                       compatible = "ti,control-phy-usb2";
+                       reg = <0x4a002300 0x4>;
+                       reg-names = "power";
+               };
+
+               omap_control_usbotg: control-phy@4a00233c {
+                       compatible = "ti,control-phy-otghs";
+                       reg = <0x4a00233c 0x4>;
+                       reg-names = "otghs_control";
                };
 
                usb_otg_hs: usb_otg_hs@4a0ab000 {
                        interrupt-names = "mc", "dma";
                        ti,hwmods = "usb_otg_hs";
                        usb-phy = <&usb2_phy>;
+                       phys = <&usb2_phy>;
+                       phy-names = "usb2-phy";
                        multipoint = <1>;
                        num-eps = <16>;
                        ram-bits = <12>;
-                       ti,has-mailbox;
+                       ctrl-module = <&omap_control_usbotg>;
                };
        };
 };
index 7cdea1bfea091917455e46cc18af7d6d80a2c0f0..c0ec6dce30fe2dd3e18e799fb1ce0f82f8a17c81 100644 (file)
                        hw-caps-temp-alert;
                };
 
-               omap_control_usb: omap-control-usb@4a002300 {
-                       compatible = "ti,omap-control-usb";
-                       reg = <0x4a002300 0x4>,
-                             <0x4a002370 0x4>;
-                       reg-names = "control_dev_conf", "phy_power_usb";
-                       ti,type = <2>;
+               omap_control_usb2phy: control-phy@4a002300 {
+                       compatible = "ti,control-phy-usb2";
+                       reg = <0x4a002300 0x4>;
+                       reg-names = "power";
+               };
+
+               omap_control_usb3phy: control-phy@4a002370 {
+                       compatible = "ti,control-phy-pipe3";
+                       reg = <0x4a002370 0x4>;
+                       reg-names = "power";
                };
 
                omap_dwc3@4a020000 {
                        usb2_phy: usb2phy@4a084000 {
                                compatible = "ti,omap-usb2";
                                reg = <0x4a084000 0x7c>;
-                               ctrl-module = <&omap_control_usb>;
+                               ctrl-module = <&omap_control_usb2phy>;
                        };
 
                        usb3_phy: usb3phy@4a084400 {
                                      <0x4a084800 0x64>,
                                      <0x4a084c00 0x40>;
                                reg-names = "phy_rx", "phy_tx", "pll_ctrl";
-                               ctrl-module = <&omap_control_usb>;
+                               ctrl-module = <&omap_control_usb3phy>;
                        };
                };
 
index ae6a17aed9ee224c85498b2bc6b1e0fa43e75e56..5aba238d1f1ec5322eb817e0eb606dffdd763b65 100644 (file)
@@ -86,6 +86,7 @@
                usb1v8-supply = <&vusb1v8>;
                usb3v1-supply = <&vusb3v1>;
                usb_mode = <1>;
+               #phy-cells = <0>;
        };
 
        twl_pwm: pwm {
index 806005a4c4c15cdeac19cbc6802494df0ff4e982..8eccbcbd5217f816d7f8e7e301884b50cd53dac5 100644 (file)
@@ -1,15 +1,14 @@
-CONFIG_EXPERIMENTAL=y
 CONFIG_SYSVIPC=y
 CONFIG_IKCONFIG=y
 CONFIG_IKCONFIG_PROC=y
 CONFIG_LOG_BUF_SHIFT=14
-CONFIG_SYSFS_DEPRECATED_V2=y
 CONFIG_EXPERT=y
 CONFIG_SLAB=y
 CONFIG_MODULES=y
 CONFIG_MODULE_UNLOAD=y
 CONFIG_MODULE_FORCE_UNLOAD=y
 # CONFIG_BLK_DEV_BSG is not set
+CONFIG_PARTITION_ADVANCED=y
 # CONFIG_IOSCHED_CFQ is not set
 CONFIG_ARCH_EP93XX=y
 CONFIG_CRUNCH=y
@@ -47,11 +46,8 @@ CONFIG_IPV6=y
 CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug"
 # CONFIG_FW_LOADER is not set
 CONFIG_MTD=y
-CONFIG_MTD_CONCAT=y
-CONFIG_MTD_PARTITIONS=y
 CONFIG_MTD_REDBOOT_PARTS=y
 CONFIG_MTD_CMDLINE_PARTS=y
-CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 CONFIG_MTD_CFI=y
 CONFIG_MTD_CFI_ADV_OPTIONS=y
@@ -67,15 +63,14 @@ CONFIG_SCSI=y
 # CONFIG_SCSI_PROC_FS is not set
 CONFIG_BLK_DEV_SD=y
 CONFIG_NETDEVICES=y
-CONFIG_NET_ETHERNET=y
 CONFIG_EP93XX_ETH=y
 CONFIG_USB_RTL8150=y
 # CONFIG_INPUT is not set
 # CONFIG_SERIO is not set
 # CONFIG_VT is not set
+# CONFIG_LEGACY_PTYS is not set
 CONFIG_SERIAL_AMBA_PL010=y
 CONFIG_SERIAL_AMBA_PL010_CONSOLE=y
-# CONFIG_LEGACY_PTYS is not set
 # CONFIG_HW_RANDOM is not set
 CONFIG_I2C=y
 CONFIG_I2C_CHARDEV=y
@@ -86,7 +81,6 @@ CONFIG_WATCHDOG=y
 CONFIG_EP93XX_WATCHDOG=y
 CONFIG_USB=y
 CONFIG_USB_DEBUG=y
-CONFIG_USB_DEVICEFS=y
 CONFIG_USB_DYNAMIC_MINORS=y
 CONFIG_USB_OHCI_HCD=y
 CONFIG_USB_STORAGE=y
@@ -100,24 +94,18 @@ CONFIG_RTC_DRV_EP93XX=y
 CONFIG_EXT2_FS=y
 CONFIG_EXT3_FS=y
 # CONFIG_EXT3_FS_XATTR is not set
-CONFIG_INOTIFY=y
 CONFIG_VFAT_FS=y
 CONFIG_TMPFS=y
 CONFIG_JFFS2_FS=y
 CONFIG_NFS_FS=y
-CONFIG_NFS_V3=y
 CONFIG_ROOT_NFS=y
-CONFIG_PARTITION_ADVANCED=y
 CONFIG_NLS_CODEPAGE_437=y
 CONFIG_NLS_ISO8859_1=y
 CONFIG_MAGIC_SYSRQ=y
-CONFIG_DEBUG_KERNEL=y
 CONFIG_DEBUG_SLAB=y
 CONFIG_DEBUG_SPINLOCK=y
 CONFIG_DEBUG_MUTEXES=y
-# CONFIG_RCU_CPU_STALL_DETECTOR is not set
 CONFIG_DEBUG_USER=y
-CONFIG_DEBUG_ERRORS=y
 CONFIG_DEBUG_LL=y
 # CONFIG_CRYPTO_ANSI_CPRNG is not set
 CONFIG_LIBCRC32C=y
index 57344b7e98ce03b8a46dc1a3698db60ef83b6dd9..2cdb63e8ce5c0eb352064f67c7c4d8dae8b43541 100644 (file)
 #define S5P_DAC_PHY_CONTROL                    S5P_PMUREG(0x070C)
 #define S5P_DAC_PHY_ENABLE                     (1 << 0)
 
-#define S5P_MIPI_DPHY_CONTROL(n)               S5P_PMUREG(0x0710 + (n) * 4)
-#define S5P_MIPI_DPHY_ENABLE                   (1 << 0)
-#define S5P_MIPI_DPHY_SRESETN                  (1 << 1)
-#define S5P_MIPI_DPHY_MRESETN                  (1 << 2)
-
 #define S5P_INFORM0                            S5P_PMUREG(0x0800)
 #define S5P_INFORM1                            S5P_PMUREG(0x0804)
 #define S5P_INFORM2                            S5P_PMUREG(0x0808)
index c05898fbd634a813542ab0c0b6e8b86586a6a0ee..b0d54dae1bcb536d818df84ad7642ba123a65ef5 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/i2c/twl.h>
 #include <linux/gpio.h>
 #include <linux/string.h>
+#include <linux/phy/phy.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/fixed.h>
 
@@ -90,8 +91,18 @@ void __init omap_pmic_late_init(void)
 }
 
 #if defined(CONFIG_ARCH_OMAP3)
+struct phy_consumer consumers[] = {
+       PHY_CONSUMER("musb-hdrc.0", "usb"),
+};
+
+struct phy_init_data init_data = {
+       .consumers = consumers,
+       .num_consumers = ARRAY_SIZE(consumers),
+};
+
 static struct twl4030_usb_data omap3_usb_pdata = {
        .usb_mode       = T2_USB_MODE_ULPI,
+       .init_data      = &init_data,
 };
 
 static int omap3_batt_table[] = {
index 032de66fb8be2817496da9c431a2f5fef69d66f9..e345584d4c34ccbd8363680038ad1511adf04b2a 100644 (file)
 #define S5P_HDMI_PHY_CONTROL   S5P_CLKREG(0xE804)
 #define S5P_USB_PHY_CONTROL    S5P_CLKREG(0xE80C)
 #define S5P_DAC_PHY_CONTROL    S5P_CLKREG(0xE810)
-#define S5P_MIPI_DPHY_CONTROL(x) S5P_CLKREG(0xE814)
-#define S5P_MIPI_DPHY_ENABLE   (1 << 0)
-#define S5P_MIPI_DPHY_SRESETN  (1 << 1)
-#define S5P_MIPI_DPHY_MRESETN  (1 << 2)
 
 #define S5P_INFORM0            S5P_CLKREG(0xF000)
 #define S5P_INFORM1            S5P_CLKREG(0xF004)
index 7dfba937d8fc34b017bf953b6761abd7f4f5f45c..6d95d60276d623bee59f28391d499f0e213730d5 100644 (file)
@@ -382,11 +382,6 @@ config S5P_DEV_TV
        help
          Compile in platform device definition for TV interface
 
-config S5P_DEV_USB_EHCI
-       bool
-       help
-         Compile in platform device definition for USB EHCI
-
 config S3C24XX_PWM
        bool "PWM device support"
        select PWM
@@ -395,11 +390,6 @@ config S3C24XX_PWM
          Support for exporting the PWM timer blocks via the pwm device
          system
 
-config S5P_SETUP_MIPIPHY
-       bool
-       help
-         Compile in common setup code for MIPI-CSIS and MIPI-DSIM devices
-
 config S3C_SETUP_CAMIF
        bool
        help
index 498c7c23e9f4cd8088eddc1ae937bfcae733d71e..9267d29549b47bd29c2ed6b76f98bd0c8180ce51 100644 (file)
@@ -38,7 +38,6 @@ obj-$(CONFIG_S5P_DEV_UART)    += s5p-dev-uart.o
 obj-$(CONFIG_SAMSUNG_DEV_BACKLIGHT)    += dev-backlight.o
 
 obj-$(CONFIG_S3C_SETUP_CAMIF)  += setup-camif.o
-obj-$(CONFIG_S5P_SETUP_MIPIPHY)        += setup-mipiphy.o
 
 # DMA support
 
index 8ce0ac007eb96397d0c16a67fe9014d12f7d6901..25f40c9b7f629aeffa31472ef6f31187aa2162ef 100644 (file)
@@ -49,7 +49,6 @@
 #include <plat/devs.h>
 #include <plat/adc.h>
 #include <linux/platform_data/ata-samsung_cf.h>
-#include <linux/platform_data/usb-ehci-s5p.h>
 #include <plat/fb.h>
 #include <plat/fb-s3c2410.h>
 #include <plat/hdmi.h>
@@ -1359,39 +1358,6 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 }
 #endif /* CONFIG_PLAT_S3C24XX */
 
-/* USB EHCI Host Controller */
-
-#ifdef CONFIG_S5P_DEV_USB_EHCI
-static struct resource s5p_ehci_resource[] = {
-       [0] = DEFINE_RES_MEM(S5P_PA_EHCI, SZ_256),
-       [1] = DEFINE_RES_IRQ(IRQ_USB_HOST),
-};
-
-struct platform_device s5p_device_ehci = {
-       .name           = "s5p-ehci",
-       .id             = -1,
-       .num_resources  = ARRAY_SIZE(s5p_ehci_resource),
-       .resource       = s5p_ehci_resource,
-       .dev            = {
-               .dma_mask               = &samsung_device_dma_mask,
-               .coherent_dma_mask      = DMA_BIT_MASK(32),
-       }
-};
-
-void __init s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd)
-{
-       struct s5p_ehci_platdata *npd;
-
-       npd = s3c_set_platdata(pd, sizeof(struct s5p_ehci_platdata),
-                       &s5p_device_ehci);
-
-       if (!npd->phy_init)
-               npd->phy_init = s5p_usb_phy_init;
-       if (!npd->phy_exit)
-               npd->phy_exit = s5p_usb_phy_exit;
-}
-#endif /* CONFIG_S5P_DEV_USB_EHCI */
-
 /* USB HSOTG */
 
 #ifdef CONFIG_S3C_DEV_USB_HSOTG
index 0dc4ac4909b09fc9a111ff3547b9be41dd33e3cc..eece188ed18826db7a6079f4ce18041752a98918 100644 (file)
@@ -75,7 +75,6 @@ extern struct platform_device s3c_device_usb_hsotg;
 extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_wdt;
 
-extern struct platform_device s5p_device_ehci;
 extern struct platform_device s5p_device_fimc0;
 extern struct platform_device s5p_device_fimc1;
 extern struct platform_device s5p_device_fimc2;
diff --git a/arch/arm/plat-samsung/setup-mipiphy.c b/arch/arm/plat-samsung/setup-mipiphy.c
deleted file mode 100644 (file)
index 66df315..0000000
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co., Ltd.
- *
- * S5P - Helper functions for MIPI-CSIS and MIPI-DSIM D-PHY control
- *
- * 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/export.h>
-#include <linux/kernel.h>
-#include <linux/platform_device.h>
-#include <linux/io.h>
-#include <linux/spinlock.h>
-#include <mach/regs-clock.h>
-
-static int __s5p_mipi_phy_control(int id, bool on, u32 reset)
-{
-       static DEFINE_SPINLOCK(lock);
-       void __iomem *addr;
-       unsigned long flags;
-       u32 cfg;
-
-       id = max(0, id);
-       if (id > 1)
-               return -EINVAL;
-
-       addr = S5P_MIPI_DPHY_CONTROL(id);
-
-       spin_lock_irqsave(&lock, flags);
-
-       cfg = __raw_readl(addr);
-       cfg = on ? (cfg | reset) : (cfg & ~reset);
-       __raw_writel(cfg, addr);
-
-       if (on) {
-               cfg |= S5P_MIPI_DPHY_ENABLE;
-       } else if (!(cfg & (S5P_MIPI_DPHY_SRESETN |
-                           S5P_MIPI_DPHY_MRESETN) & ~reset)) {
-               cfg &= ~S5P_MIPI_DPHY_ENABLE;
-       }
-
-       __raw_writel(cfg, addr);
-       spin_unlock_irqrestore(&lock, flags);
-
-       return 0;
-}
-
-int s5p_csis_phy_enable(int id, bool on)
-{
-       return __s5p_mipi_phy_control(id, on, S5P_MIPI_DPHY_SRESETN);
-}
-EXPORT_SYMBOL(s5p_csis_phy_enable);
-
-int s5p_dsim_phy_enable(struct platform_device *pdev, bool on)
-{
-       return __s5p_mipi_phy_control(pdev->id, on, S5P_MIPI_DPHY_MRESETN);
-}
-EXPORT_SYMBOL(s5p_dsim_phy_enable);
index 969e9871785ca5ae47e0153ef96c15c460daca18..97536a2c3ba2290ebeadc05515ae7971d30321b6 100644 (file)
@@ -168,4 +168,6 @@ source "drivers/fmc/Kconfig"
 
 source "drivers/powercap/Kconfig"
 
+source "drivers/phy/Kconfig"
+
 endmenu
index 34c1d554f69b489fc523a811e9f1fc76b5a5cd4e..3cc8214f9b26630e86eddcb37ac35f7a5f57e4a1 100644 (file)
@@ -8,6 +8,8 @@
 obj-y                          += irqchip/
 obj-y                          += bus/
 
+obj-$(CONFIG_GENERIC_PHY)      += phy/
+
 # GPIO must come after pinctrl as gpios may need to mux pins etc
 obj-y                          += pinctrl/
 obj-y                          += gpio/
index 53ad0f080179721dd97c333a051ce034d948e857..d2d3b4b614359e239a540198451fd55b81339353 100644 (file)
@@ -29,7 +29,7 @@ config VIDEO_S5P_FIMC
 config VIDEO_S5P_MIPI_CSIS
        tristate "S5P/EXYNOS MIPI-CSI2 receiver (MIPI-CSIS) driver"
        depends on REGULATOR
-       select S5P_SETUP_MIPIPHY
+       select GENERIC_PHY
        help
          This is a V4L2 driver for Samsung S5P and EXYNOS4 SoC MIPI-CSI2
          receiver (MIPI-CSIS) devices.
index 0914230b42de55ea531d63cc10bce844d1282ed3..9fc2af6a04466b402c6f931097e28dbdec2ad29c 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/memory.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/phy/phy.h>
 #include <linux/platform_data/mipi-csis.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -180,6 +181,7 @@ struct csis_drvdata {
  * @sd: v4l2_subdev associated with CSIS device instance
  * @index: the hardware instance index
  * @pdev: CSIS platform device
+ * @phy: pointer to the CSIS generic PHY
  * @regs: mmaped I/O registers memory
  * @supplies: CSIS regulator supplies
  * @clock: CSIS clocks
@@ -203,6 +205,7 @@ struct csis_state {
        struct v4l2_subdev sd;
        u8 index;
        struct platform_device *pdev;
+       struct phy *phy;
        void __iomem *regs;
        struct regulator_bulk_data supplies[CSIS_NUM_SUPPLIES];
        struct clk *clock[NUM_CSIS_CLOCKS];
@@ -779,8 +782,8 @@ static int s5pcsis_parse_dt(struct platform_device *pdev,
                                        "samsung,csis-wclk");
 
        state->num_lanes = endpoint.bus.mipi_csi2.num_data_lanes;
-
        of_node_put(node);
+
        return 0;
 }
 #else
@@ -829,6 +832,10 @@ static int s5pcsis_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
+       state->phy = devm_phy_get(dev, "csis");
+       if (IS_ERR(state->phy))
+               return PTR_ERR(state->phy);
+
        mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        state->regs = devm_ioremap_resource(dev, mem_res);
        if (IS_ERR(state->regs))
@@ -922,7 +929,7 @@ static int s5pcsis_pm_suspend(struct device *dev, bool runtime)
        mutex_lock(&state->lock);
        if (state->flags & ST_POWERED) {
                s5pcsis_stop_stream(state);
-               ret = s5p_csis_phy_enable(state->index, false);
+               ret = phy_power_off(state->phy);
                if (ret)
                        goto unlock;
                ret = regulator_bulk_disable(CSIS_NUM_SUPPLIES,
@@ -958,7 +965,7 @@ static int s5pcsis_pm_resume(struct device *dev, bool runtime)
                                            state->supplies);
                if (ret)
                        goto unlock;
-               ret = s5p_csis_phy_enable(state->index, true);
+               ret = phy_power_on(state->phy);
                if (!ret) {
                        state->flags |= ST_POWERED;
                } else {
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
new file mode 100644 (file)
index 0000000..a344f3d
--- /dev/null
@@ -0,0 +1,54 @@
+#
+# PHY
+#
+
+menu "PHY Subsystem"
+
+config GENERIC_PHY
+       tristate "PHY Core"
+       help
+         Generic PHY support.
+
+         This framework is designed to provide a generic interface for PHY
+         devices present in the kernel. This layer will have the generic
+         API by which phy drivers can create PHY using the phy framework and
+         phy users can obtain reference to the PHY. All the users of this
+         framework should select this config.
+
+config PHY_EXYNOS_MIPI_VIDEO
+       tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
+       help
+         Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
+         and EXYNOS SoCs.
+
+config OMAP_USB2
+       tristate "OMAP USB2 PHY Driver"
+       depends on ARCH_OMAP2PLUS
+       select GENERIC_PHY
+       select USB_PHY
+       select OMAP_CONTROL_USB
+       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 TWL4030_USB
+       tristate "TWL4030 USB Transceiver Driver"
+       depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
+       select GENERIC_PHY
+       select USB_PHY
+       help
+         Enable this to support the USB OTG transceiver on TWL4030
+         family chips (including the TWL5030 and TPS659x0 devices).
+         This transceiver supports high and full speed devices plus,
+         in host mode, low speed.
+
+config PHY_EXYNOS_DP_VIDEO
+       tristate "EXYNOS SoC series Display Port PHY driver"
+       depends on OF
+       select GENERIC_PHY
+       help
+         Support for Display Port PHY found on Samsung EXYNOS SoCs.
+
+endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
new file mode 100644 (file)
index 0000000..d0caae9
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Makefile for the phy drivers.
+#
+
+obj-$(CONFIG_GENERIC_PHY)              += phy-core.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_OMAP_USB2)                        += phy-omap-usb2.o
+obj-$(CONFIG_TWL4030_USB)              += phy-twl4030-usb.o
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
new file mode 100644 (file)
index 0000000..03cf8fb
--- /dev/null
@@ -0,0 +1,698 @@
+/*
+ * phy-core.c  --  Generic Phy framework.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+
+static struct class *phy_class;
+static DEFINE_MUTEX(phy_provider_mutex);
+static LIST_HEAD(phy_provider_list);
+static DEFINE_IDA(phy_ida);
+
+static void devm_phy_release(struct device *dev, void *res)
+{
+       struct phy *phy = *(struct phy **)res;
+
+       phy_put(phy);
+}
+
+static void devm_phy_provider_release(struct device *dev, void *res)
+{
+       struct phy_provider *phy_provider = *(struct phy_provider **)res;
+
+       of_phy_provider_unregister(phy_provider);
+}
+
+static void devm_phy_consume(struct device *dev, void *res)
+{
+       struct phy *phy = *(struct phy **)res;
+
+       phy_destroy(phy);
+}
+
+static int devm_phy_match(struct device *dev, void *res, void *match_data)
+{
+       return res == match_data;
+}
+
+static struct phy *phy_lookup(struct device *device, const char *port)
+{
+       unsigned int count;
+       struct phy *phy;
+       struct device *dev;
+       struct phy_consumer *consumers;
+       struct class_dev_iter iter;
+
+       class_dev_iter_init(&iter, phy_class, NULL, NULL);
+       while ((dev = class_dev_iter_next(&iter))) {
+               phy = to_phy(dev);
+               count = phy->init_data->num_consumers;
+               consumers = phy->init_data->consumers;
+               while (count--) {
+                       if (!strcmp(consumers->dev_name, dev_name(device)) &&
+                                       !strcmp(consumers->port, port)) {
+                               class_dev_iter_exit(&iter);
+                               return phy;
+                       }
+                       consumers++;
+               }
+       }
+
+       class_dev_iter_exit(&iter);
+       return ERR_PTR(-ENODEV);
+}
+
+static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
+{
+       struct phy_provider *phy_provider;
+
+       list_for_each_entry(phy_provider, &phy_provider_list, list) {
+               if (phy_provider->dev->of_node == node)
+                       return phy_provider;
+       }
+
+       return ERR_PTR(-EPROBE_DEFER);
+}
+
+int phy_pm_runtime_get(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_get(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
+
+int phy_pm_runtime_get_sync(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_get_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
+
+int phy_pm_runtime_put(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_put(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put);
+
+int phy_pm_runtime_put_sync(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_put_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
+
+void phy_pm_runtime_allow(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return;
+
+       pm_runtime_allow(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
+
+void phy_pm_runtime_forbid(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return;
+
+       pm_runtime_forbid(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
+
+int phy_init(struct phy *phy)
+{
+       int ret;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (phy->init_count++ == 0 && phy->ops->init) {
+               ret = phy->ops->init(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy init failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_init);
+
+int phy_exit(struct phy *phy)
+{
+       int ret;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (--phy->init_count == 0 && phy->ops->exit) {
+               ret = phy->ops->exit(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_exit);
+
+int phy_power_on(struct phy *phy)
+{
+       int ret = -ENOTSUPP;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (phy->power_count++ == 0 && phy->ops->power_on) {
+               ret = phy->ops->power_on(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_on);
+
+int phy_power_off(struct phy *phy)
+{
+       int ret = -ENOTSUPP;
+
+       mutex_lock(&phy->mutex);
+       if (--phy->power_count == 0 && phy->ops->power_off) {
+               ret =  phy->ops->power_off(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+
+       return ret;
+}
+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
+ * @index: the index of the phy
+ *
+ * Returns the phy associated with the given phandle value,
+ * after getting a refcount to it or -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * 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)
+{
+       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",
+               index, &args);
+       if (ret) {
+               dev_dbg(dev, "failed to get phy in %s node\n",
+                       dev->of_node->full_name);
+               return ERR_PTR(-ENODEV);
+       }
+
+       mutex_lock(&phy_provider_mutex);
+       phy_provider = of_phy_provider_lookup(args.np);
+       if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) {
+               phy = ERR_PTR(-EPROBE_DEFER);
+               goto err0;
+       }
+
+       phy = phy_provider->of_xlate(phy_provider->dev, &args);
+       module_put(phy_provider->owner);
+
+err0:
+       mutex_unlock(&phy_provider_mutex);
+       of_node_put(args.np);
+
+       return phy;
+}
+
+/**
+ * phy_put() - release the PHY
+ * @phy: the phy returned by phy_get()
+ *
+ * Releases a refcount the caller received from phy_get().
+ */
+void phy_put(struct phy *phy)
+{
+       if (IS_ERR(phy))
+               return;
+
+       module_put(phy->ops->owner);
+       put_device(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_put);
+
+/**
+ * devm_phy_put() - release the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_put
+ * to release the phy.
+ */
+void devm_phy_put(struct device *dev, struct phy *phy)
+{
+       int r;
+
+       r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_put);
+
+/**
+ * of_phy_simple_xlate() - returns the phy instance from phy provider
+ * @dev: the PHY provider device
+ * @args: of_phandle_args (not used here)
+ *
+ * Intended to be used by phy provider for the common case where #phy-cells is
+ * 0. For other cases where #phy-cells is greater than '0', the phy provider
+ * should provide a custom of_xlate function that reads the *args* and returns
+ * the appropriate phy.
+ */
+struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
+       *args)
+{
+       struct phy *phy;
+       struct class_dev_iter iter;
+       struct device_node *node = dev->of_node;
+
+       class_dev_iter_init(&iter, phy_class, NULL, NULL);
+       while ((dev = class_dev_iter_next(&iter))) {
+               phy = to_phy(dev);
+               if (node != phy->dev.of_node)
+                       continue;
+
+               class_dev_iter_exit(&iter);
+               return phy;
+       }
+
+       class_dev_iter_exit(&iter);
+       return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(of_phy_simple_xlate);
+
+/**
+ * phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * 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 *phy_get(struct device *dev, const char *string)
+{
+       int index = 0;
+       struct phy *phy = NULL;
+
+       if (string == NULL) {
+               dev_WARN(dev, "missing string\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (dev->of_node) {
+               index = of_property_match_string(dev->of_node, "phy-names",
+                       string);
+               phy = of_phy_get(dev, index);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "unable to find phy\n");
+                       return phy;
+               }
+       } else {
+               phy = phy_lookup(dev, string);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "unable to find phy\n");
+                       return phy;
+               }
+       }
+
+       if (!try_module_get(phy->ops->owner))
+               return ERR_PTR(-EPROBE_DEFER);
+
+       get_device(&phy->dev);
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(phy_get);
+
+/**
+ * devm_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or phy device name
+ * for non-dt case
+ *
+ * Gets the phy using 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_phy_get(struct device *dev, const char *string)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = phy_get(dev, string);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_get);
+
+/**
+ * phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Called to create a phy using phy framework.
+ */
+struct phy *phy_create(struct device *dev, const struct phy_ops *ops,
+       struct phy_init_data *init_data)
+{
+       int ret;
+       int id;
+       struct phy *phy;
+
+       if (!dev) {
+               dev_WARN(dev, "no device provided for PHY\n");
+               ret = -EINVAL;
+               goto err0;
+       }
+
+       phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               ret = -ENOMEM;
+               goto err0;
+       }
+
+       id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
+       if (id < 0) {
+               dev_err(dev, "unable to get id\n");
+               ret = id;
+               goto err0;
+       }
+
+       device_initialize(&phy->dev);
+       mutex_init(&phy->mutex);
+
+       phy->dev.class = phy_class;
+       phy->dev.parent = dev;
+       phy->dev.of_node = dev->of_node;
+       phy->id = id;
+       phy->ops = ops;
+       phy->init_data = init_data;
+
+       ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
+       if (ret)
+               goto err1;
+
+       ret = device_add(&phy->dev);
+       if (ret)
+               goto err1;
+
+       if (pm_runtime_enabled(dev)) {
+               pm_runtime_enable(&phy->dev);
+               pm_runtime_no_callbacks(&phy->dev);
+       }
+
+       return phy;
+
+err1:
+       ida_remove(&phy_ida, phy->id);
+       put_device(&phy->dev);
+       kfree(phy);
+
+err0:
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(phy_create);
+
+/**
+ * devm_phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Creates a new PHY device adding it to the PHY class.
+ * While at that, it also associates the device with the phy using devres.
+ * On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_create(struct device *dev, const struct phy_ops *ops,
+       struct phy_init_data *init_data)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = phy_create(dev, ops, init_data);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_create);
+
+/**
+ * phy_destroy() - destroy the phy
+ * @phy: the phy to be destroyed
+ *
+ * Called to destroy the phy.
+ */
+void phy_destroy(struct phy *phy)
+{
+       pm_runtime_disable(&phy->dev);
+       device_unregister(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_destroy);
+
+/**
+ * devm_phy_destroy() - destroy the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_destroy
+ * to destroy the phy.
+ */
+void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+       int r;
+
+       r = devres_destroy(dev, devm_phy_consume, devm_phy_match, phy);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_destroy);
+
+/**
+ * __of_phy_provider_register() - create/register phy provider with the framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider.
+ */
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       struct phy_provider *phy_provider;
+
+       phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
+       if (!phy_provider)
+               return ERR_PTR(-ENOMEM);
+
+       phy_provider->dev = dev;
+       phy_provider->owner = owner;
+       phy_provider->of_xlate = of_xlate;
+
+       mutex_lock(&phy_provider_mutex);
+       list_add_tail(&phy_provider->list, &phy_provider_list);
+       mutex_unlock(&phy_provider_mutex);
+
+       return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__of_phy_provider_register);
+
+/**
+ * __devm_of_phy_provider_register() - create/register phy provider with the
+ * framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider. While at that, it also associates the device with the
+ * phy provider using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ */
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       struct phy_provider **ptr, *phy_provider;
+
+       ptr = devres_alloc(devm_phy_provider_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
+       if (!IS_ERR(phy_provider)) {
+               *ptr = phy_provider;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__devm_of_phy_provider_register);
+
+/**
+ * of_phy_provider_unregister() - unregister phy provider from the framework
+ * @phy_provider: phy provider returned by of_phy_provider_register()
+ *
+ * Removes the phy_provider created using of_phy_provider_register().
+ */
+void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+       if (IS_ERR(phy_provider))
+               return;
+
+       mutex_lock(&phy_provider_mutex);
+       list_del(&phy_provider->list);
+       kfree(phy_provider);
+       mutex_unlock(&phy_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(of_phy_provider_unregister);
+
+/**
+ * devm_of_phy_provider_unregister() - remove phy provider from the framework
+ * @dev: struct device of the phy provider
+ *
+ * destroys the devres associated with this phy provider and invokes
+ * of_phy_provider_unregister to unregister the phy provider.
+ */
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider) {
+       int r;
+
+       r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match,
+               phy_provider);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY provider device resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_provider_unregister);
+
+/**
+ * phy_release() - release the phy
+ * @dev: the dev member within phy
+ *
+ * When the last reference to the device is removed, it is called
+ * from the embedded kobject as release method.
+ */
+static void phy_release(struct device *dev)
+{
+       struct phy *phy;
+
+       phy = to_phy(dev);
+       dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+       ida_remove(&phy_ida, phy->id);
+       kfree(phy);
+}
+
+static int __init phy_core_init(void)
+{
+       phy_class = class_create(THIS_MODULE, "phy");
+       if (IS_ERR(phy_class)) {
+               pr_err("failed to create phy class --> %ld\n",
+                       PTR_ERR(phy_class));
+               return PTR_ERR(phy_class);
+       }
+
+       phy_class->dev_release = phy_release;
+
+       return 0;
+}
+module_init(phy_core_init);
+
+static void __exit phy_core_exit(void)
+{
+       class_destroy(phy_class);
+}
+module_exit(phy_core_exit);
+
+MODULE_DESCRIPTION("Generic PHY Framework");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c
new file mode 100644 (file)
index 0000000..1dbe6ce
--- /dev/null
@@ -0,0 +1,111 @@
+/*
+ * Samsung EXYNOS SoC series Display Port PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@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/io.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>
+
+/* DPTX_PHY_CONTROL register */
+#define EXYNOS_DPTX_PHY_ENABLE         (1 << 0)
+
+struct exynos_dp_video_phy {
+       void __iomem *regs;
+};
+
+static int __set_phy_state(struct exynos_dp_video_phy *state, unsigned int on)
+{
+       u32 reg;
+
+       reg = readl(state->regs);
+       if (on)
+               reg |= EXYNOS_DPTX_PHY_ENABLE;
+       else
+               reg &= ~EXYNOS_DPTX_PHY_ENABLE;
+       writel(reg, state->regs);
+
+       return 0;
+}
+
+static int exynos_dp_video_phy_power_on(struct phy *phy)
+{
+       struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+       return __set_phy_state(state, 1);
+}
+
+static int exynos_dp_video_phy_power_off(struct phy *phy)
+{
+       struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+       return __set_phy_state(state, 0);
+}
+
+static struct phy_ops exynos_dp_video_phy_ops = {
+       .power_on       = exynos_dp_video_phy_power_on,
+       .power_off      = exynos_dp_video_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int exynos_dp_video_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_dp_video_phy *state;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct phy *phy;
+
+       state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       state->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(state->regs))
+               return PTR_ERR(state->regs);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       phy = devm_phy_create(dev, &exynos_dp_video_phy_ops, NULL);
+       if (IS_ERR(phy)) {
+               dev_err(dev, "failed to create Display Port PHY\n");
+               return PTR_ERR(phy);
+       }
+       phy_set_drvdata(phy, state);
+
+       return 0;
+}
+
+static const struct of_device_id exynos_dp_video_phy_of_match[] = {
+       { .compatible = "samsung,exynos5250-dp-video-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match);
+
+static struct platform_driver exynos_dp_video_phy_driver = {
+       .probe  = exynos_dp_video_phy_probe,
+       .driver = {
+               .name   = "exynos-dp-video-phy",
+               .owner  = THIS_MODULE,
+               .of_match_table = exynos_dp_video_phy_of_match,
+       }
+};
+module_platform_driver(exynos_dp_video_phy_driver);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c
new file mode 100644 (file)
index 0000000..0c5efab
--- /dev/null
@@ -0,0 +1,176 @@
+/*
+ * 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.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/spinlock.h>
+
+/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
+#define EXYNOS_MIPI_PHY_CONTROL(n)     ((n) * 4)
+#define EXYNOS_MIPI_PHY_ENABLE         (1 << 0)
+#define EXYNOS_MIPI_PHY_SRESETN                (1 << 1)
+#define EXYNOS_MIPI_PHY_MRESETN                (1 << 2)
+#define EXYNOS_MIPI_PHY_RESET_MASK     (3 << 1)
+
+enum exynos_mipi_phy_id {
+       EXYNOS_MIPI_PHY_ID_CSIS0,
+       EXYNOS_MIPI_PHY_ID_DSIM0,
+       EXYNOS_MIPI_PHY_ID_CSIS1,
+       EXYNOS_MIPI_PHY_ID_DSIM1,
+       EXYNOS_MIPI_PHYS_NUM
+};
+
+#define is_mipi_dsim_phy_id(id) \
+       ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
+
+struct exynos_mipi_video_phy {
+       spinlock_t slock;
+       struct video_phy_desc {
+               struct phy *phy;
+               unsigned int index;
+       } phys[EXYNOS_MIPI_PHYS_NUM];
+       void __iomem *regs;
+};
+
+static int __set_phy_state(struct exynos_mipi_video_phy *state,
+                       enum exynos_mipi_phy_id id, unsigned int on)
+{
+       void __iomem *addr;
+       u32 reg, reset;
+
+       addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+
+       if (is_mipi_dsim_phy_id(id))
+               reset = EXYNOS_MIPI_PHY_MRESETN;
+       else
+               reset = EXYNOS_MIPI_PHY_SRESETN;
+
+       spin_lock(&state->slock);
+       reg = readl(addr);
+       if (on)
+               reg |= reset;
+       else
+               reg &= ~reset;
+       writel(reg, addr);
+
+       /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
+       if (on)
+               reg |= EXYNOS_MIPI_PHY_ENABLE;
+       else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
+               reg &= ~EXYNOS_MIPI_PHY_ENABLE;
+
+       writel(reg, addr);
+       spin_unlock(&state->slock);
+       return 0;
+}
+
+#define to_mipi_video_phy(desc) \
+       container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
+
+static int exynos_mipi_video_phy_power_on(struct phy *phy)
+{
+       struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+       struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+       return __set_phy_state(state, phy_desc->index, 1);
+}
+
+static int exynos_mipi_video_phy_power_off(struct phy *phy)
+{
+       struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+       struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+       return __set_phy_state(state, phy_desc->index, 0);
+}
+
+static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
+
+       if (WARN_ON(args->args[0] > EXYNOS_MIPI_PHYS_NUM))
+               return ERR_PTR(-ENODEV);
+
+       return state->phys[args->args[0]].phy;
+}
+
+static struct phy_ops exynos_mipi_video_phy_ops = {
+       .power_on       = exynos_mipi_video_phy_power_on,
+       .power_off      = exynos_mipi_video_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_mipi_video_phy *state;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       unsigned int i;
+
+       state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       state->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(state->regs))
+               return PTR_ERR(state->regs);
+
+       dev_set_drvdata(dev, state);
+       spin_lock_init(&state->slock);
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                       exynos_mipi_video_phy_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
+               struct phy *phy = devm_phy_create(dev,
+                                       &exynos_mipi_video_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", i);
+                       return PTR_ERR(phy);
+               }
+
+               state->phys[i].phy = phy;
+               state->phys[i].index = i;
+               phy_set_drvdata(phy, &state->phys[i]);
+       }
+
+       return 0;
+}
+
+static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
+       { .compatible = "samsung,s5pv210-mipi-video-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
+
+static struct platform_driver exynos_mipi_video_phy_driver = {
+       .probe  = exynos_mipi_video_phy_probe,
+       .driver = {
+               .of_match_table = exynos_mipi_video_phy_of_match,
+               .name  = "exynos-mipi-video-phy",
+               .owner = THIS_MODULE,
+       }
+};
+module_platform_driver(exynos_mipi_video_phy_driver);
+
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
similarity index 81%
rename from drivers/usb/phy/phy-omap-usb2.c
rename to drivers/phy/phy-omap-usb2.c
index d266861d24f73978c682fed2a1e59518106f3758..bfc5c337f99a8178278d9aa8f0b9b7be74f41889 100644 (file)
@@ -28,6 +28,8 @@
 #include <linux/pm_runtime.h>
 #include <linux/delay.h>
 #include <linux/usb/omap_control_usb.h>
+#include <linux/phy/phy.h>
+#include <linux/of_platform.h>
 
 /**
  * omap_usb2_set_comparator - links the comparator present in the sytem with
@@ -118,10 +120,42 @@ static int omap_usb2_suspend(struct usb_phy *x, int suspend)
        return 0;
 }
 
+static int omap_usb_power_off(struct phy *x)
+{
+       struct omap_usb *phy = phy_get_drvdata(x);
+
+       omap_control_usb_phy_power(phy->control_dev, 0);
+
+       return 0;
+}
+
+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, 1);
+
+       return 0;
+}
+
+static struct phy_ops ops = {
+       .power_on       = omap_usb_power_on,
+       .power_off      = omap_usb_power_off,
+       .owner          = THIS_MODULE,
+};
+
 static int omap_usb2_probe(struct platform_device *pdev)
 {
-       struct omap_usb                 *phy;
-       struct usb_otg                  *otg;
+       struct omap_usb *phy;
+       struct phy *generic_phy;
+       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;
+
+       if (!node)
+               return -EINVAL;
 
        phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
        if (!phy) {
@@ -143,12 +177,25 @@ static int omap_usb2_probe(struct platform_device *pdev)
        phy->phy.otg            = otg;
        phy->phy.type           = USB_PHY_TYPE_USB2;
 
-       phy->control_dev = omap_get_control_dev();
-       if (IS_ERR(phy->control_dev)) {
-               dev_dbg(&pdev->dev, "Failed to get control device\n");
-               return -ENODEV;
+       phy_provider = devm_of_phy_provider_register(phy->dev,
+                       of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       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;
+
        phy->is_suspended       = 1;
        omap_control_usb_phy_power(phy->control_dev, 0);
 
@@ -158,6 +205,15 @@ static int omap_usb2_probe(struct platform_device *pdev)
        otg->start_srp          = omap_usb_start_srp;
        otg->phy                = &phy->phy;
 
+       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->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");
@@ -173,10 +229,6 @@ static int omap_usb2_probe(struct platform_device *pdev)
 
        usb_add_phy_dev(&phy->phy);
 
-       platform_set_drvdata(pdev, phy);
-
-       pm_runtime_enable(phy->dev);
-
        return 0;
 }
 
similarity index 94%
rename from drivers/usb/phy/phy-twl4030-usb.c
rename to drivers/phy/phy-twl4030-usb.c
index 90730c8762b8f8ee850a8a153ad757a4b5b3337e..e0212d80c75ca5450002f0d91fcd5365103bc723 100644 (file)
@@ -33,6 +33,7 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 #include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
 #include <linux/usb/musb-omap.h>
 #include <linux/usb/ulpi.h>
 #include <linux/i2c/twl.h>
@@ -421,17 +422,20 @@ static void twl4030_phy_power(struct twl4030_usb *twl, int on)
        }
 }
 
-static void twl4030_phy_suspend(struct twl4030_usb *twl, int controller_off)
+static int twl4030_phy_power_off(struct phy *phy)
 {
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
+
        if (twl->asleep)
-               return;
+               return 0;
 
        twl4030_phy_power(twl, 0);
        twl->asleep = 1;
        dev_dbg(twl->dev, "%s\n", __func__);
+       return 0;
 }
 
-static void __twl4030_phy_resume(struct twl4030_usb *twl)
+static void __twl4030_phy_power_on(struct twl4030_usb *twl)
 {
        twl4030_phy_power(twl, 1);
        twl4030_i2c_access(twl, 1);
@@ -440,11 +444,13 @@ static void __twl4030_phy_resume(struct twl4030_usb *twl)
                twl4030_i2c_access(twl, 0);
 }
 
-static void twl4030_phy_resume(struct twl4030_usb *twl)
+static int twl4030_phy_power_on(struct phy *phy)
 {
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
+
        if (!twl->asleep)
-               return;
-       __twl4030_phy_resume(twl);
+               return 0;
+       __twl4030_phy_power_on(twl);
        twl->asleep = 0;
        dev_dbg(twl->dev, "%s\n", __func__);
 
@@ -457,6 +463,7 @@ static void twl4030_phy_resume(struct twl4030_usb *twl)
                cancel_delayed_work(&twl->id_workaround_work);
                schedule_delayed_work(&twl->id_workaround_work, HZ);
        }
+       return 0;
 }
 
 static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
@@ -587,9 +594,9 @@ static void twl4030_id_workaround_work(struct work_struct *work)
        }
 }
 
-static int twl4030_usb_phy_init(struct usb_phy *phy)
+static int twl4030_phy_init(struct phy *phy)
 {
-       struct twl4030_usb *twl = phy_to_twl(phy);
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
        enum omap_musb_vbus_id_status status;
 
        /*
@@ -602,25 +609,15 @@ static int twl4030_usb_phy_init(struct usb_phy *phy)
        status = twl4030_usb_linkstat(twl);
        twl->linkstat = status;
 
-       if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID)
+       if (status == OMAP_MUSB_ID_GROUND || status == OMAP_MUSB_VBUS_VALID) {
                omap_musb_mailbox(twl->linkstat);
+               twl4030_phy_power_on(phy);
+       }
 
        sysfs_notify(&twl->dev->kobj, NULL, "vbus");
        return 0;
 }
 
-static int twl4030_set_suspend(struct usb_phy *x, int suspend)
-{
-       struct twl4030_usb *twl = phy_to_twl(x);
-
-       if (suspend)
-               twl4030_phy_suspend(twl, 1);
-       else
-               twl4030_phy_resume(twl);
-
-       return 0;
-}
-
 static int twl4030_set_peripheral(struct usb_otg *otg,
                                        struct usb_gadget *gadget)
 {
@@ -646,13 +643,23 @@ static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
        return 0;
 }
 
+static const struct phy_ops ops = {
+       .init           = twl4030_phy_init,
+       .power_on       = twl4030_phy_power_on,
+       .power_off      = twl4030_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
 static int twl4030_usb_probe(struct platform_device *pdev)
 {
        struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev);
        struct twl4030_usb      *twl;
+       struct phy              *phy;
        int                     status, err;
        struct usb_otg          *otg;
        struct device_node      *np = pdev->dev.of_node;
+       struct phy_provider     *phy_provider;
+       struct phy_init_data    *init_data = NULL;
 
        twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
        if (!twl)
@@ -661,9 +668,10 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        if (np)
                of_property_read_u32(np, "usb_mode",
                                (enum twl4030_usb_mode *)&twl->usb_mode);
-       else if (pdata)
+       else if (pdata) {
                twl->usb_mode = pdata->usb_mode;
-       else {
+               init_data = pdata->init_data;
+       } else {
                dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
                return -EINVAL;
        }
@@ -682,13 +690,24 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        twl->phy.label          = "twl4030";
        twl->phy.otg            = otg;
        twl->phy.type           = USB_PHY_TYPE_USB2;
-       twl->phy.set_suspend    = twl4030_set_suspend;
-       twl->phy.init           = twl4030_usb_phy_init;
 
        otg->phy                = &twl->phy;
        otg->set_host           = twl4030_set_host;
        otg->set_peripheral     = twl4030_set_peripheral;
 
+       phy_provider = devm_of_phy_provider_register(twl->dev,
+               of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       phy = devm_phy_create(twl->dev, &ops, init_data);
+       if (IS_ERR(phy)) {
+               dev_dbg(&pdev->dev, "Failed to create PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       phy_set_drvdata(phy, twl);
+
        /* init spinlock for workqueue */
        spin_lock_init(&twl->lock);
 
index 464584c6ccaeaf84d87a888d2e4b8c1382f97e61..a857131656889fa2a2634dc33ed398a5143c373a 100644 (file)
@@ -48,6 +48,7 @@
 #define PORTSC_SUSP           BIT(7)
 #define PORTSC_HSP            BIT(9)
 #define PORTSC_PTC            (0x0FUL << 16)
+#define PORTSC_PHCD(d)       ((d) ? BIT(22) : BIT(23))
 /* PTS and PTW for non lpm version only */
 #define PORTSC_PTS(d)                                          \
        (u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
index 7ad541591c81c0db06b6dc6b4b3d7e6d49137103..bb5d976e5b818e14cc4ff2a3506d3a82e182adf0 100644 (file)
@@ -108,14 +108,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
        }
 
        data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
-       if (!IS_ERR(data->phy)) {
-               ret = usb_phy_init(data->phy);
-               if (ret) {
-                       dev_err(&pdev->dev, "unable to init phy: %d\n", ret);
-                       goto err_clk;
-               }
-       } else if (PTR_ERR(data->phy) == -EPROBE_DEFER) {
-               ret = -EPROBE_DEFER;
+       if (IS_ERR(data->phy)) {
+               ret = PTR_ERR(data->phy);
                goto err_clk;
        }
 
@@ -130,7 +124,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
                if (ret) {
                        dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n",
                                        ret);
-                       goto err_phy;
+                       goto err_clk;
                }
        }
 
@@ -142,7 +136,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
                dev_err(&pdev->dev,
                        "Can't register ci_hdrc platform device, err=%d\n",
                        ret);
-               goto err_phy;
+               goto err_clk;
        }
 
        if (data->usbmisc_data) {
@@ -163,9 +157,6 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
 
 disable_device:
        ci_hdrc_remove_device(data->ci_pdev);
-err_phy:
-       if (data->phy)
-               usb_phy_shutdown(data->phy);
 err_clk:
        clk_disable_unprepare(data->clk);
        return ret;
@@ -177,10 +168,6 @@ static int ci_hdrc_imx_remove(struct platform_device *pdev)
 
        pm_runtime_disable(&pdev->dev);
        ci_hdrc_remove_device(data->ci_pdev);
-
-       if (data->phy)
-               usb_phy_shutdown(data->phy);
-
        clk_disable_unprepare(data->clk);
 
        return 0;
index 23763dcec069b2e8d1ba6dd140fc9cf644d2ddec..06204b77fc4caa29dcd041c65e11fd73dd9ec7e7 100644 (file)
@@ -172,6 +172,27 @@ u8 hw_port_test_get(struct ci_hdrc *ci)
        return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
 }
 
+/* The PHY enters/leaves low power mode */
+static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
+{
+       enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
+       bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
+
+       if (enable && !lpm) {
+               hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+                               PORTSC_PHCD(ci->hw_bank.lpm));
+       } else  if (!enable && lpm) {
+               hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+                               0);
+               /* 
+                * The controller needs at least 1ms to reflect
+                * PHY's status, the PHY also needs some time (less
+                * than 1ms) to leave low power mode.
+                */
+               usleep_range(1500, 2000);
+       }
+}
+
 static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
 {
        u32 reg;
@@ -199,6 +220,8 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
        if (ci->hw_ep_max > ENDPT_MAX)
                return -ENODEV;
 
+       ci_hdrc_enter_lpm(ci, false);
+
        /* Disable all interrupts bits */
        hw_write(ci, OP_USBINTR, 0xffffffff, 0);
 
@@ -381,6 +404,15 @@ static int ci_get_platdata(struct device *dev,
                return PTR_ERR(platdata->reg_vbus);
        }
 
+       if (!platdata->phy_mode)
+               platdata->phy_mode = of_usb_get_phy_mode(dev->of_node);
+
+       if (!platdata->dr_mode)
+               platdata->dr_mode = of_usb_get_dr_mode(dev->of_node);
+
+       if (platdata->dr_mode == USB_DR_MODE_UNKNOWN)
+               platdata->dr_mode = USB_DR_MODE_OTG;
+
        return 0;
 }
 
@@ -465,6 +497,33 @@ 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;
@@ -473,7 +532,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        void __iomem    *base;
        int             ret;
        enum usb_dr_mode dr_mode;
-       struct device_node *of_node = dev->of_node ?: dev->parent->of_node;
 
        if (!dev->platform_data) {
                dev_err(dev, "platform data missing\n");
@@ -493,10 +551,6 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
        ci->dev = dev;
        ci->platdata = dev->platform_data;
-       if (ci->platdata->phy)
-               ci->transceiver = ci->platdata->phy;
-       else
-               ci->global_phy = true;
 
        ret = hw_device_init(ci, base);
        if (ret < 0) {
@@ -504,27 +558,25 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
+       ret = ci_usb_phy_init(ci);
+       if (ret) {
+               dev_err(dev, "unable to init phy: %d\n", ret);
+               return ret;
+       }
+
        ci->hw_bank.phys = res->start;
 
        ci->irq = platform_get_irq(pdev, 0);
        if (ci->irq < 0) {
                dev_err(dev, "missing IRQ\n");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto destroy_phy;
        }
 
        ci_get_otg_capable(ci);
 
-       if (!ci->platdata->phy_mode)
-               ci->platdata->phy_mode = of_usb_get_phy_mode(of_node);
-
        hw_phymode_configure(ci);
 
-       if (!ci->platdata->dr_mode)
-               ci->platdata->dr_mode = of_usb_get_dr_mode(of_node);
-
-       if (ci->platdata->dr_mode == USB_DR_MODE_UNKNOWN)
-               ci->platdata->dr_mode = USB_DR_MODE_OTG;
-
        dr_mode = ci->platdata->dr_mode;
        /* initialize role(s) before the interrupt is requested */
        if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
@@ -537,11 +589,23 @@ 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");
-               return -ENODEV;
+               ret = -ENODEV;
+               goto destroy_phy;
        }
 
        if (ci->is_otg) {
@@ -594,6 +658,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);
 
        return ret;
 }
@@ -605,6 +671,8 @@ static int ci_hdrc_remove(struct platform_device *pdev)
        dbg_remove_files(ci);
        free_irq(ci->irq, ci);
        ci_role_destroy(ci);
+       ci_hdrc_enter_lpm(ci, true);
+       ci_usb_phy_destroy(ci);
        kfree(ci->hw_bank.regmap);
 
        return 0;
index 9333083dd1111c047c7016a44402242e2aa87f7d..b34c81969cba672a7e880f405f2445adf36b7e6f 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/pm_runtime.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
-#include <linux/usb/otg.h>
 #include <linux/usb/chipidea.h>
 
 #include "ci.h"
@@ -686,9 +685,6 @@ static int _gadget_stop_activity(struct usb_gadget *gadget)
        usb_ep_fifo_flush(&ci->ep0out->ep);
        usb_ep_fifo_flush(&ci->ep0in->ep);
 
-       if (ci->driver)
-               ci->driver->disconnect(gadget);
-
        /* make sure to disable all endpoints */
        gadget_for_each_ep(ep, gadget) {
                usb_ep_disable(ep);
@@ -718,6 +714,11 @@ __acquires(ci->lock)
        int retval;
 
        spin_unlock(&ci->lock);
+       if (ci->gadget.speed != USB_SPEED_UNKNOWN) {
+               if (ci->driver)
+                       ci->driver->disconnect(&ci->gadget);
+       }
+
        retval = _gadget_stop_activity(&ci->gadget);
        if (retval)
                goto done;
@@ -1461,6 +1462,8 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
                        hw_device_state(ci, ci->ep0out->qh.dma);
                        dev_dbg(ci->dev, "Connected to host\n");
                } else {
+                       if (ci->driver)
+                               ci->driver->disconnect(&ci->gadget);
                        hw_device_state(ci, 0);
                        if (ci->platdata->notify_event)
                                ci->platdata->notify_event(ci,
@@ -1633,23 +1636,22 @@ static int ci_udc_start(struct usb_gadget *gadget,
        retval = usb_ep_enable(&ci->ep0in->ep);
        if (retval)
                return retval;
-       spin_lock_irqsave(&ci->lock, flags);
 
        ci->driver = driver;
        pm_runtime_get_sync(&ci->gadget.dev);
        if (ci->vbus_active) {
+               spin_lock_irqsave(&ci->lock, flags);
                hw_device_reset(ci, USBMODE_CM_DC);
        } else {
                pm_runtime_put_sync(&ci->gadget.dev);
-               goto done;
+               return retval;
        }
 
        retval = hw_device_state(ci, ci->ep0out->qh.dma);
+       spin_unlock_irqrestore(&ci->lock, flags);
        if (retval)
                pm_runtime_put_sync(&ci->gadget.dev);
 
- done:
-       spin_unlock_irqrestore(&ci->lock, flags);
        return retval;
 }
 
@@ -1786,34 +1788,9 @@ static int udc_start(struct ci_hdrc *ci)
 
        ci->gadget.ep0 = &ci->ep0in->ep;
 
-       if (ci->global_phy) {
-               ci->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
-               if (IS_ERR(ci->transceiver))
-                       ci->transceiver = NULL;
-       }
-
-       if (ci->platdata->flags & CI_HDRC_REQUIRE_TRANSCEIVER) {
-               if (ci->transceiver == NULL) {
-                       retval = -ENODEV;
-                       goto destroy_eps;
-               }
-       }
-
-       if (ci->transceiver) {
-               retval = 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 (retval && retval != -ENOTSUPP)
-                       goto put_transceiver;
-       }
-
        retval = usb_add_gadget_udc(dev, &ci->gadget);
        if (retval)
-               goto remove_trans;
+               goto destroy_eps;
 
        pm_runtime_no_callbacks(&ci->gadget.dev);
        pm_runtime_enable(&ci->gadget.dev);
@@ -1823,17 +1800,6 @@ static int udc_start(struct ci_hdrc *ci)
 
        return retval;
 
-remove_trans:
-       if (ci->transceiver) {
-               otg_set_peripheral(ci->transceiver->otg, NULL);
-               if (ci->global_phy)
-                       usb_put_phy(ci->transceiver);
-       }
-
-       dev_err(dev, "error = %i\n", retval);
-put_transceiver:
-       if (ci->transceiver && ci->global_phy)
-               usb_put_phy(ci->transceiver);
 destroy_eps:
        destroy_eps(ci);
 free_pools:
index 71dc5d768fa5cef3edc5ac27c84c032d2a0a0dcf..967152a63bd3c92f86f937a5b2f3c7b1d0805cc2 100644 (file)
@@ -914,10 +914,8 @@ static int proc_control(struct dev_state *ps, void __user *arg)
        snoop(&dev->dev, "control urb: bRequestType=%02x "
                "bRequest=%02x wValue=%04x "
                "wIndex=%04x wLength=%04x\n",
-               ctrl.bRequestType, ctrl.bRequest,
-               __le16_to_cpup(&ctrl.wValue),
-               __le16_to_cpup(&ctrl.wIndex),
-               __le16_to_cpup(&ctrl.wLength));
+               ctrl.bRequestType, ctrl.bRequest, ctrl.wValue,
+               ctrl.wIndex, ctrl.wLength);
        if (ctrl.bRequestType & 0x80) {
                if (ctrl.wLength && !access_ok(VERIFY_WRITE, ctrl.data,
                                               ctrl.wLength)) {
@@ -1636,32 +1634,32 @@ static int proc_reapurbnonblock(struct dev_state *ps, void __user *arg)
 static int proc_control_compat(struct dev_state *ps,
                                struct usbdevfs_ctrltransfer32 __user *p32)
 {
-        struct usbdevfs_ctrltransfer __user *p;
-        __u32 udata;
-        p = compat_alloc_user_space(sizeof(*p));
-        if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
-            get_user(udata, &p32->data) ||
+       struct usbdevfs_ctrltransfer __user *p;
+       __u32 udata;
+       p = compat_alloc_user_space(sizeof(*p));
+       if (copy_in_user(p, p32, (sizeof(*p32) - sizeof(compat_caddr_t))) ||
+           get_user(udata, &p32->data) ||
            put_user(compat_ptr(udata), &p->data))
                return -EFAULT;
-        return proc_control(ps, p);
+       return proc_control(ps, p);
 }
 
 static int proc_bulk_compat(struct dev_state *ps,
                        struct usbdevfs_bulktransfer32 __user *p32)
 {
-        struct usbdevfs_bulktransfer __user *p;
-        compat_uint_t n;
-        compat_caddr_t addr;
+       struct usbdevfs_bulktransfer __user *p;
+       compat_uint_t n;
+       compat_caddr_t addr;
 
-        p = compat_alloc_user_space(sizeof(*p));
+       p = compat_alloc_user_space(sizeof(*p));
 
-        if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
-            get_user(n, &p32->len) || put_user(n, &p->len) ||
-            get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
-            get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
-                return -EFAULT;
+       if (get_user(n, &p32->ep) || put_user(n, &p->ep) ||
+           get_user(n, &p32->len) || put_user(n, &p->len) ||
+           get_user(n, &p32->timeout) || put_user(n, &p->timeout) ||
+           get_user(addr, &p32->data) || put_user(compat_ptr(addr), &p->data))
+               return -EFAULT;
 
-        return proc_bulk(ps, p);
+       return proc_bulk(ps, p);
 }
 static int proc_disconnectsignal_compat(struct dev_state *ps, void __user *arg)
 {
index f7841d44feda967f0a78b21bf4e8229556ccd7c5..47aade2a5e741ade190022342590c363316514d7 100644 (file)
@@ -1179,8 +1179,8 @@ static int usb_resume_interface(struct usb_device *udev,
                                                "reset_resume", status);
                } else {
                        intf->needs_binding = 1;
-                       dev_warn(&intf->dev, "no %s for driver %s?\n",
-                                       "reset_resume", driver->name);
+                       dev_dbg(&intf->dev, "no reset_resume for driver %s?\n",
+                                       driver->name);
                }
        } else {
                status = driver->resume(intf);
@@ -1790,6 +1790,9 @@ int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
        struct usb_hcd *hcd = bus_to_hcd(udev->bus);
        int ret = -EPERM;
 
+       if (enable && !udev->usb2_hw_lpm_allowed)
+               return 0;
+
        if (hcd->driver->set_usb2_hw_lpm) {
                ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
                if (!ret)
index 7421888087a3b752a3915c0dc70bfcda640d9122..3bdfbf88a0aee73e36989c8d7adfbe3190b2e8ce 100644 (file)
@@ -8,7 +8,7 @@
  * (C) Copyright Deti Fliegl 1999 (new USB architecture)
  * (C) Copyright Randy Dunlap 2000
  * (C) Copyright David Brownell 2000-2001 (kernel hotplug, usb_device_id,
      more docs, etc)
*     more docs, etc)
  * (C) Copyright Yggdrasil Computing, Inc. 2000
  *     (usb_device_id matching changes by Adam J. Richter)
  * (C) Copyright Greg Kroah-Hartman 2002-2003
@@ -27,7 +27,7 @@
 static const struct file_operations *usb_minors[MAX_USB_MINORS];
 static DECLARE_RWSEM(minor_rwsem);
 
-static int usb_open(struct inode * inode, struct file * file)
+static int usb_open(struct inode *inode, struct file *file)
 {
        int minor = iminor(inode);
        const struct file_operations *c;
@@ -44,7 +44,7 @@ static int usb_open(struct inode * inode, struct file * file)
        file->f_op = new_fops;
        /* Curiouser and curiouser... NULL ->open() as "no device" ? */
        if (file->f_op->open)
-               err = file->f_op->open(inode,file);
+               err = file->f_op->open(inode, file);
        if (err) {
                fops_put(file->f_op);
                file->f_op = fops_get(old_fops);
@@ -166,7 +166,7 @@ int usb_register_dev(struct usb_interface *intf,
        char *temp;
 
 #ifdef CONFIG_USB_DYNAMIC_MINORS
-       /* 
+       /*
         * We don't care what the device tries to start at, we want to start
         * at zero to pack the devices into the smallest available space with
         * no holes in the minor range.
index b9d3c43e38599e67c4cc8c409c686bb4f45fcf8a..dfe9d0f229780653c2bcb505e73ee00f4b372197 100644 (file)
@@ -215,6 +215,9 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
                goto disable_pci;
        }
 
+       hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) &&
+                       driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0;
+
        if (driver->flags & HCD_MEMORY) {
                /* EHCI, OHCI */
                hcd->rsrc_start = pci_resource_start(dev, 0);
index d6a8d23f047ba69c33d836adb8adc9e0741a85c5..6bffb8c87bc9aaee98c2342b0f7a5a3c05c870b0 100644 (file)
@@ -6,7 +6,7 @@
  * (C) Copyright Deti Fliegl 1999
  * (C) Copyright Randy Dunlap 2000
  * (C) Copyright David Brownell 2000-2002
- * 
+ *
  * 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
@@ -40,6 +40,7 @@
 #include <linux/platform_device.h>
 #include <linux/workqueue.h>
 #include <linux/pm_runtime.h>
+#include <linux/types.h>
 
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
@@ -92,10 +93,7 @@ EXPORT_SYMBOL_GPL (usb_bus_list);
 
 /* used when allocating bus numbers */
 #define USB_MAXBUS             64
-struct usb_busmap {
-       unsigned long busmap [USB_MAXBUS / (8*sizeof (unsigned long))];
-};
-static struct usb_busmap busmap;
+static DECLARE_BITMAP(busmap, USB_MAXBUS);
 
 /* used when updating list of hcds */
 DEFINE_MUTEX(usb_bus_list_lock);       /* exported only for usbfs */
@@ -171,7 +169,7 @@ static const u8 usb25_rh_dev_descriptor[18] = {
 };
 
 /* usb 2.0 root hub device descriptor */
-static const u8 usb2_rh_dev_descriptor [18] = {
+static const u8 usb2_rh_dev_descriptor[18] = {
        0x12,       /*  __u8  bLength; */
        0x01,       /*  __u8  bDescriptorType; Device */
        0x00, 0x02, /*  __le16 bcdUSB; v2.0 */
@@ -194,7 +192,7 @@ static const u8 usb2_rh_dev_descriptor [18] = {
 /* no usb 2.0 root hub "device qualifier" descriptor: one speed only */
 
 /* usb 1.1 root hub device descriptor */
-static const u8 usb11_rh_dev_descriptor [18] = {
+static const u8 usb11_rh_dev_descriptor[18] = {
        0x12,       /*  __u8  bLength; */
        0x01,       /*  __u8  bDescriptorType; Device */
        0x10, 0x01, /*  __le16 bcdUSB; v1.1 */
@@ -219,7 +217,7 @@ static const u8 usb11_rh_dev_descriptor [18] = {
 
 /* Configuration descriptors for our root hubs */
 
-static const u8 fs_rh_config_descriptor [] = {
+static const u8 fs_rh_config_descriptor[] = {
 
        /* one configuration */
        0x09,       /*  __u8  bLength; */
@@ -228,13 +226,13 @@ static const u8 fs_rh_config_descriptor [] = {
        0x01,       /*  __u8  bNumInterfaces; (1) */
        0x01,       /*  __u8  bConfigurationValue; */
        0x00,       /*  __u8  iConfiguration; */
-       0xc0,       /*  __u8  bmAttributes; 
+       0xc0,       /*  __u8  bmAttributes;
                                 Bit 7: must be set,
                                     6: Self-powered,
                                     5: Remote wakeup,
                                     4..0: resvd */
        0x00,       /*  __u8  MaxPower; */
-      
+
        /* USB 1.1:
         * USB 2.0, single TT organization (mandatory):
         *      one interface, protocol 0
@@ -256,17 +254,17 @@ static const u8 fs_rh_config_descriptor [] = {
        0x00,       /*  __u8  if_bInterfaceSubClass; */
        0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
        0x00,       /*  __u8  if_iInterface; */
-     
+
        /* one endpoint (status change endpoint) */
        0x07,       /*  __u8  ep_bLength; */
        0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
        0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
-       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
-       0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
+       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+       0x02, 0x00, /*  __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
        0xff        /*  __u8  ep_bInterval; (255ms -- usb 2.0 spec) */
 };
 
-static const u8 hs_rh_config_descriptor [] = {
+static const u8 hs_rh_config_descriptor[] = {
 
        /* one configuration */
        0x09,       /*  __u8  bLength; */
@@ -275,13 +273,13 @@ static const u8 hs_rh_config_descriptor [] = {
        0x01,       /*  __u8  bNumInterfaces; (1) */
        0x01,       /*  __u8  bConfigurationValue; */
        0x00,       /*  __u8  iConfiguration; */
-       0xc0,       /*  __u8  bmAttributes; 
+       0xc0,       /*  __u8  bmAttributes;
                                 Bit 7: must be set,
                                     6: Self-powered,
                                     5: Remote wakeup,
                                     4..0: resvd */
        0x00,       /*  __u8  MaxPower; */
-      
+
        /* USB 1.1:
         * USB 2.0, single TT organization (mandatory):
         *      one interface, protocol 0
@@ -303,12 +301,12 @@ static const u8 hs_rh_config_descriptor [] = {
        0x00,       /*  __u8  if_bInterfaceSubClass; */
        0x00,       /*  __u8  if_bInterfaceProtocol; [usb1.1 or single tt] */
        0x00,       /*  __u8  if_iInterface; */
-     
+
        /* one endpoint (status change endpoint) */
        0x07,       /*  __u8  ep_bLength; */
        0x05,       /*  __u8  ep_bDescriptorType; Endpoint */
        0x81,       /*  __u8  ep_bEndpointAddress; IN Endpoint 1 */
-       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
+       0x03,       /*  __u8  ep_bmAttributes; Interrupt */
                    /* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
                     * see hub.c:hub_configure() for details. */
        (USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
@@ -428,7 +426,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
        char const *s;
        static char const langids[4] = {4, USB_DT_STRING, 0x09, 0x04};
 
-       // language ids
+       /* language ids */
        switch (id) {
        case 0:
                /* Array of LANGID codes (0x0409 is MSFT-speak for "en-us") */
@@ -464,7 +462,7 @@ rh_string(int id, struct usb_hcd const *hcd, u8 *data, unsigned len)
 static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
 {
        struct usb_ctrlrequest *cmd;
-       u16             typeReq, wValue, wIndex, wLength;
+       u16             typeReq, wValue, wIndex, wLength;
        u8              *ubuf = urb->transfer_buffer;
        unsigned        len = 0;
        int             status;
@@ -526,10 +524,10 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
         */
 
        case DeviceRequest | USB_REQ_GET_STATUS:
-               tbuf [0] = (device_may_wakeup(&hcd->self.root_hub->dev)
+               tbuf[0] = (device_may_wakeup(&hcd->self.root_hub->dev)
                                        << USB_DEVICE_REMOTE_WAKEUP)
                                | (1 << USB_DEVICE_SELF_POWERED);
-               tbuf [1] = 0;
+               tbuf[1] = 0;
                len = 2;
                break;
        case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -546,7 +544,7 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
                        goto error;
                break;
        case DeviceRequest | USB_REQ_GET_CONFIGURATION:
-               tbuf [0] = 1;
+               tbuf[0] = 1;
                len = 1;
                        /* FALLTHROUGH */
        case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
@@ -609,13 +607,13 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
                }
                break;
        case DeviceRequest | USB_REQ_GET_INTERFACE:
-               tbuf [0] = 0;
+               tbuf[0] = 0;
                len = 1;
                        /* FALLTHROUGH */
        case DeviceOutRequest | USB_REQ_SET_INTERFACE:
                break;
        case DeviceOutRequest | USB_REQ_SET_ADDRESS:
-               // wValue == urb->dev->devaddr
+               /* wValue == urb->dev->devaddr */
                dev_dbg (hcd->self.controller, "root hub device address %d\n",
                        wValue);
                break;
@@ -625,9 +623,9 @@ static int rh_call_control (struct usb_hcd *hcd, struct urb *urb)
        /* ENDPOINT REQUESTS */
 
        case EndpointRequest | USB_REQ_GET_STATUS:
-               // ENDPOINT_HALT flag
-               tbuf [0] = 0;
-               tbuf [1] = 0;
+               /* ENDPOINT_HALT flag */
+               tbuf[0] = 0;
+               tbuf[1] = 0;
                len = 2;
                        /* FALLTHROUGH */
        case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
@@ -683,7 +681,7 @@ error:
                if (urb->transfer_buffer_length < len)
                        len = urb->transfer_buffer_length;
                urb->actual_length = len;
-               // always USB_DIR_IN, toward host
+               /* always USB_DIR_IN, toward host */
                memcpy (ubuf, bufp, len);
 
                /* report whether RH hardware supports remote wakeup */
@@ -877,11 +875,11 @@ static ssize_t authorized_default_store(struct device *dev,
        usb_hcd = bus_to_hcd(usb_bus);
        result = sscanf(buf, "%u\n", &val);
        if (result == 1) {
-               usb_hcd->authorized_default = val? 1 : 0;
+               usb_hcd->authorized_default = val ? 1 : 0;
                result = size;
-       }
-       else
+       } else {
                result = -EINVAL;
+       }
        return result;
 }
 static DEVICE_ATTR_RW(authorized_default);
@@ -941,12 +939,12 @@ static int usb_register_bus(struct usb_bus *bus)
        int busnum;
 
        mutex_lock(&usb_bus_list_lock);
-       busnum = find_next_zero_bit (busmap.busmap, USB_MAXBUS, 1);
+       busnum = find_next_zero_bit(busmap, USB_MAXBUS, 1);
        if (busnum >= USB_MAXBUS) {
                printk (KERN_ERR "%s: too many buses\n", usbcore_name);
                goto error_find_busnum;
        }
-       set_bit (busnum, busmap.busmap);
+       set_bit(busnum, busmap);
        bus->busnum = busnum;
 
        /* Add it to the local list of buses */
@@ -987,7 +985,7 @@ static void usb_deregister_bus (struct usb_bus *bus)
 
        usb_notify_remove_bus(bus);
 
-       clear_bit (bus->busnum, busmap.busmap);
+       clear_bit(bus->busnum, busmap);
 }
 
 /**
@@ -1033,6 +1031,7 @@ static int register_root_hub(struct usb_hcd *hcd)
                                        dev_name(&usb_dev->dev), retval);
                        return retval;
                }
+               usb_dev->lpm_capable = usb_device_supports_lpm(usb_dev);
        }
 
        retval = usb_new_device (usb_dev);
@@ -1120,21 +1119,21 @@ long usb_calc_bus_time (int speed, int is_input, int isoc, int bytecount)
        case USB_SPEED_LOW:     /* INTR only */
                if (is_input) {
                        tmp = (67667L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+                       return 64060L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
                } else {
                        tmp = (66700L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp);
+                       return 64107L + (2 * BW_HUB_LS_SETUP) + BW_HOST_DELAY + tmp;
                }
        case USB_SPEED_FULL:    /* ISOC or INTR */
                if (isoc) {
                        tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp);
+                       return ((is_input) ? 7268L : 6265L) + BW_HOST_DELAY + tmp;
                } else {
                        tmp = (8354L * (31L + 10L * BitTime (bytecount))) / 1000L;
-                       return (9107L + BW_HOST_DELAY + tmp);
+                       return 9107L + BW_HOST_DELAY + tmp;
                }
        case USB_SPEED_HIGH:    /* ISOC or INTR */
-               // FIXME adjust for input vs output
+               /* FIXME adjust for input vs output */
                if (isoc)
                        tmp = HS_NSECS_ISO (bytecount);
                else
@@ -1651,6 +1650,7 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
 static void __usb_hcd_giveback_urb(struct urb *urb)
 {
        struct usb_hcd *hcd = bus_to_hcd(urb->dev->bus);
+       struct usb_anchor *anchor = urb->anchor;
        int status = urb->unlinked;
        unsigned long flags;
 
@@ -1662,6 +1662,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
 
        unmap_urb_for_dma(hcd, urb);
        usbmon_urb_complete(&hcd->self, urb, status);
+       usb_anchor_suspend_wakeups(anchor);
        usb_unanchor_urb(urb);
 
        /* pass ownership to the completion handler */
@@ -1681,6 +1682,7 @@ static void __usb_hcd_giveback_urb(struct urb *urb)
        urb->complete(urb);
        local_irq_restore(flags);
 
+       usb_anchor_resume_wakeups(anchor);
        atomic_dec(&urb->use_count);
        if (unlikely(atomic_read(&urb->reject)))
                wake_up(&usb_kill_urb_queue);
@@ -1703,7 +1705,9 @@ static void usb_giveback_urb_bh(unsigned long param)
 
                urb = list_entry(local_list.next, struct urb, urb_list);
                list_del_init(&urb->urb_list);
+               bh->completing_ep = urb->ep;
                __usb_hcd_giveback_urb(urb);
+               bh->completing_ep = NULL;
        }
 
        /* check if there are new URBs to giveback */
@@ -1812,7 +1816,7 @@ rescan:
                                 case USB_ENDPOINT_XFER_INT:
                                        s = "-intr"; break;
                                 default:
-                                       s = "-iso"; break;
+                                       s = "-iso"; break;
                                };
                                s;
                        }));
@@ -2073,8 +2077,11 @@ 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.
  */
-void usb_free_streams(struct usb_interface *interface,
+int usb_free_streams(struct usb_interface *interface,
                struct usb_host_endpoint **eps, unsigned int num_eps,
                gfp_t mem_flags)
 {
@@ -2085,14 +2092,14 @@ void usb_free_streams(struct usb_interface *interface,
        dev = interface_to_usbdev(interface);
        hcd = bus_to_hcd(dev->bus);
        if (dev->speed != USB_SPEED_SUPER)
-               return;
+               return -EINVAL;
 
        /* Streams only apply to bulk endpoints. */
        for (i = 0; i < num_eps; i++)
                if (!eps[i] || !usb_endpoint_xfer_bulk(&eps[i]->desc))
-                       return;
+                       return -EINVAL;
 
-       hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
+       return hcd->driver->free_streams(hcd, dev, eps, num_eps, mem_flags);
 }
 EXPORT_SYMBOL_GPL(usb_free_streams);
 
@@ -2245,7 +2252,7 @@ static void hcd_resume_work(struct work_struct *work)
 }
 
 /**
- * usb_hcd_resume_root_hub - called by HCD to resume its root hub 
+ * usb_hcd_resume_root_hub - called by HCD to resume its root hub
  * @hcd: host controller for this root hub
  *
  * The USB host controller calls this function when its root hub is
@@ -2324,15 +2331,8 @@ EXPORT_SYMBOL_GPL(usb_bus_start_enum);
 irqreturn_t usb_hcd_irq (int irq, void *__hcd)
 {
        struct usb_hcd          *hcd = __hcd;
-       unsigned long           flags;
        irqreturn_t             rc;
 
-       /* IRQF_DISABLED doesn't work correctly with shared IRQs
-        * when the first handler doesn't use it.  So let's just
-        * assume it's never used.
-        */
-       local_irq_save(flags);
-
        if (unlikely(HCD_DEAD(hcd) || !HCD_HW_ACCESSIBLE(hcd)))
                rc = IRQ_NONE;
        else if (hcd->driver->irq(hcd) == IRQ_NONE)
@@ -2340,7 +2340,6 @@ irqreturn_t usb_hcd_irq (int irq, void *__hcd)
        else
                rc = IRQ_HANDLED;
 
-       local_irq_restore(flags);
        return rc;
 }
 EXPORT_SYMBOL_GPL(usb_hcd_irq);
@@ -2547,13 +2546,6 @@ static int usb_hcd_request_irqs(struct usb_hcd *hcd,
 
        if (hcd->driver->irq) {
 
-               /* IRQF_DISABLED doesn't work as advertised when used together
-                * with IRQF_SHARED. As usb_hcd_irq() will always disable
-                * interrupts we can remove it here.
-                */
-               if (irqflags & IRQF_SHARED)
-                       irqflags &= ~IRQF_DISABLED;
-
                snprintf(hcd->irq_descr, sizeof(hcd->irq_descr), "%s:usb%d",
                                hcd->driver->description, hcd->self.busnum);
                retval = request_irq(irqnum, &usb_hcd_irq, irqflags,
@@ -2600,7 +2592,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
 
        /* Keep old behaviour if authorized_default is not in [0, 1]. */
        if (authorized_default < 0 || authorized_default > 1)
-               hcd->authorized_default = hcd->wireless? 0 : 1;
+               hcd->authorized_default = hcd->wireless ? 0 : 1;
        else
                hcd->authorized_default = authorized_default;
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
@@ -2743,7 +2735,7 @@ err_allocate_root_hub:
 err_register_bus:
        hcd_buffer_destroy(hcd);
        return retval;
-} 
+}
 EXPORT_SYMBOL_GPL(usb_add_hcd);
 
 /**
@@ -2818,7 +2810,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 EXPORT_SYMBOL_GPL(usb_remove_hcd);
 
 void
-usb_hcd_platform_shutdown(struct platform_devicedev)
+usb_hcd_platform_shutdown(struct platform_device *dev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
 
@@ -2840,7 +2832,7 @@ struct usb_mon_operations *mon_ops;
  * Notice that the code is minimally error-proof. Because usbmon needs
  * symbols from usbcore, usbcore gets referenced and cannot be unloaded first.
  */
+
 int usb_mon_register (struct usb_mon_operations *ops)
 {
 
index e6b682c6c236b8152561496b2e00bba22e75e25c..06cec635e703adc3f9859ae8ca53dbb124820cd4 100644 (file)
@@ -120,7 +120,7 @@ static inline char *portspeed(struct usb_hub *hub, int portstatus)
        if (hub_is_superspeed(hub->hdev))
                return "5.0 Gb/s";
        if (portstatus & USB_PORT_STAT_HIGH_SPEED)
-               return "480 Mb/s";
+               return "480 Mb/s";
        else if (portstatus & USB_PORT_STAT_LOW_SPEED)
                return "1.5 Mb/s";
        else
@@ -135,7 +135,7 @@ struct usb_hub *usb_hub_to_struct_hub(struct usb_device *hdev)
        return usb_get_intfdata(hdev->actconfig->interface[0]);
 }
 
-static int usb_device_supports_lpm(struct usb_device *udev)
+int usb_device_supports_lpm(struct usb_device *udev)
 {
        /* USB 2.1 (and greater) devices indicate LPM support through
         * their USB 2.0 Extended Capabilities BOS descriptor.
@@ -156,6 +156,11 @@ static int usb_device_supports_lpm(struct usb_device *udev)
                                "Power management will be impacted.\n");
                return 0;
        }
+
+       /* udev is root hub */
+       if (!udev->parent)
+               return 1;
+
        if (udev->parent->lpm_capable)
                return 1;
 
@@ -310,9 +315,9 @@ static void usb_set_lpm_parameters(struct usb_device *udev)
                return;
 
        udev_u1_del = udev->bos->ss_cap->bU1devExitLat;
-       udev_u2_del = udev->bos->ss_cap->bU2DevExitLat;
+       udev_u2_del = le16_to_cpu(udev->bos->ss_cap->bU2DevExitLat);
        hub_u1_del = udev->parent->bos->ss_cap->bU1devExitLat;
-       hub_u2_del = udev->parent->bos->ss_cap->bU2DevExitLat;
+       hub_u2_del = le16_to_cpu(udev->parent->bos->ss_cap->bU2DevExitLat);
 
        usb_set_lpm_mel(udev, &udev->u1_params, udev_u1_del,
                        hub, &udev->parent->u1_params, hub_u1_del);
@@ -433,7 +438,7 @@ static void set_port_led(
                        case HUB_LED_OFF: s = "off"; break;
                        case HUB_LED_AUTO: s = "auto"; break;
                        default: s = "??"; break;
-                       }; s; }),
+                       } s; }),
                        status);
 }
 
@@ -857,7 +862,7 @@ static int hub_hub_status(struct usb_hub *hub,
                                "%s failed (err = %d)\n", __func__, ret);
        } else {
                *status = le16_to_cpu(hub->status->hub.wHubStatus);
-               *change = le16_to_cpu(hub->status->hub.wHubChange); 
+               *change = le16_to_cpu(hub->status->hub.wHubChange);
                ret = 0;
        }
        mutex_unlock(&hub->status_mutex);
@@ -956,7 +961,7 @@ static void hub_port_logical_disconnect(struct usb_hub *hub, int port1)
         */
 
        set_bit(port1, hub->change_bits);
-       kick_khubd(hub);
+       kick_khubd(hub);
 }
 
 /**
@@ -1107,16 +1112,13 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        /*
                         * USB3 protocol ports will automatically transition
                         * to Enabled state when detect an USB3.0 device attach.
-                        * Do not disable USB3 protocol ports.
+                        * Do not disable USB3 protocol ports, just pretend
+                        * power was lost
                         */
-                       if (!hub_is_superspeed(hdev)) {
+                       portstatus &= ~USB_PORT_STAT_ENABLE;
+                       if (!hub_is_superspeed(hdev))
                                usb_clear_port_feature(hdev, port1,
                                                   USB_PORT_FEAT_ENABLE);
-                               portstatus &= ~USB_PORT_STAT_ENABLE;
-                       } else {
-                               /* Pretend that power was lost for USB3 devs */
-                               portstatus &= ~USB_PORT_STAT_ENABLE;
-                       }
                }
 
                /* Clear status-change flags; we'll debounce later */
@@ -1130,6 +1132,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
                        usb_clear_port_feature(hub->hdev, port1,
                                        USB_PORT_FEAT_C_ENABLE);
                }
+               if (portchange & USB_PORT_STAT_C_RESET) {
+                       need_debounce_delay = true;
+                       usb_clear_port_feature(hub->hdev, port1,
+                                       USB_PORT_FEAT_C_RESET);
+               }
                if ((portchange & USB_PORT_STAT_C_BH_RESET) &&
                                hub_is_superspeed(hub->hdev)) {
                        need_debounce_delay = true;
@@ -1361,7 +1368,7 @@ static int hub_configure(struct usb_hub *hub,
        if ((wHubCharacteristics & HUB_CHAR_COMPOUND) &&
                        !(hub_is_superspeed(hdev))) {
                int     i;
-               char    portstr [USB_MAXCHILDREN + 1];
+               char    portstr[USB_MAXCHILDREN + 1];
 
                for (i = 0; i < hdev->maxchild; i++)
                        portstr[i] = hub->descriptor->u.hs.DeviceRemovable
@@ -1429,32 +1436,32 @@ static int hub_configure(struct usb_hub *hub,
 
        /* Note 8 FS bit times == (8 bits / 12000000 bps) ~= 666ns */
        switch (wHubCharacteristics & HUB_CHAR_TTTT) {
-               case HUB_TTTT_8_BITS:
-                       if (hdev->descriptor.bDeviceProtocol != 0) {
-                               hub->tt.think_time = 666;
-                               dev_dbg(hub_dev, "TT requires at most %d "
-                                               "FS bit times (%d ns)\n",
-                                       8, hub->tt.think_time);
-                       }
-                       break;
-               case HUB_TTTT_16_BITS:
-                       hub->tt.think_time = 666 * 2;
-                       dev_dbg(hub_dev, "TT requires at most %d "
-                                       "FS bit times (%d ns)\n",
-                               16, hub->tt.think_time);
-                       break;
-               case HUB_TTTT_24_BITS:
-                       hub->tt.think_time = 666 * 3;
+       case HUB_TTTT_8_BITS:
+               if (hdev->descriptor.bDeviceProtocol != 0) {
+                       hub->tt.think_time = 666;
                        dev_dbg(hub_dev, "TT requires at most %d "
                                        "FS bit times (%d ns)\n",
-                               24, hub->tt.think_time);
-                       break;
-               case HUB_TTTT_32_BITS:
-                       hub->tt.think_time = 666 * 4;
-                       dev_dbg(hub_dev, "TT requires at most %d "
-                                       "FS bit times (%d ns)\n",
-                               32, hub->tt.think_time);
-                       break;
+                               8, hub->tt.think_time);
+               }
+               break;
+       case HUB_TTTT_16_BITS:
+               hub->tt.think_time = 666 * 2;
+               dev_dbg(hub_dev, "TT requires at most %d "
+                               "FS bit times (%d ns)\n",
+                       16, hub->tt.think_time);
+               break;
+       case HUB_TTTT_24_BITS:
+               hub->tt.think_time = 666 * 3;
+               dev_dbg(hub_dev, "TT requires at most %d "
+                               "FS bit times (%d ns)\n",
+                       24, hub->tt.think_time);
+               break;
+       case HUB_TTTT_32_BITS:
+               hub->tt.think_time = 666 * 4;
+               dev_dbg(hub_dev, "TT requires at most %d "
+                               "FS bit times (%d ns)\n",
+                       32, hub->tt.think_time);
+               break;
        }
 
        /* probe() zeroes hub->indicator[] */
@@ -1560,7 +1567,7 @@ static int hub_configure(struct usb_hub *hub,
 
        /* maybe cycle the hub leds */
        if (hub->has_indicators && blinkenlights)
-               hub->indicator [0] = INDICATOR_CYCLE;
+               hub->indicator[0] = INDICATOR_CYCLE;
 
        for (i = 0; i < hdev->maxchild; i++) {
                ret = usb_hub_create_port_device(hub, i + 1);
@@ -1978,7 +1985,7 @@ static void choose_devnum(struct usb_device *udev)
                if (devnum >= 128)
                        devnum = find_next_zero_bit(bus->devmap.devicemap,
                                                    128, 1);
-               bus->devnum_next = ( devnum >= 127 ? 1 : devnum + 1);
+               bus->devnum_next = (devnum >= 127 ? 1 : devnum + 1);
        }
        if (devnum < 128) {
                set_bit(devnum, bus->devmap.devicemap);
@@ -2018,8 +2025,8 @@ static void hub_free_dev(struct usb_device *udev)
  * Something got disconnected. Get rid of it and all of its children.
  *
  * If *pdev is a normal device then the parent hub must already be locked.
- * If *pdev is a root hub then this routine will acquire the
- * usb_bus_list_lock on behalf of the caller.
+ * If *pdev is a root hub then the caller must hold the usb_bus_list_lock,
+ * which protects the set of root hubs as well as the list of buses.
  *
  * Only hub drivers (including virtual root hub drivers for host
  * controllers) should ever call this.
@@ -2232,8 +2239,7 @@ static int usb_enumerate_device(struct usb_device *udev)
                udev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
                udev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
                udev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-       }
-       else {
+       } else {
                /* read the standard strings and cache them if present */
                udev->product = usb_cache_string(udev, udev->descriptor.iProduct);
                udev->manufacturer = usb_cache_string(udev,
@@ -2489,7 +2495,7 @@ error_device_descriptor:
        usb_autosuspend_device(usb_dev);
 error_autoresume:
 out_authorized:
-       usb_unlock_device(usb_dev);     // complements locktree
+       usb_unlock_device(usb_dev);     /* complements locktree */
        return result;
 }
 
@@ -3108,8 +3114,8 @@ static int finish_port_resume(struct usb_device *udev)
  retry_reset_resume:
                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,
+       /* 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,
         * and device drivers will know about any resume quirks.
         */
        if (status == 0) {
@@ -3211,7 +3217,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
        if (status == 0 && !port_is_suspended(hub, portstatus))
                goto SuspendCleared;
 
-       // dev_dbg(hub->intfdev, "resume port %d\n", port1);
+       /* dev_dbg(hub->intfdev, "resume port %d\n", port1); */
 
        set_bit(port1, hub->busy_bits);
 
@@ -3855,7 +3861,7 @@ EXPORT_SYMBOL_GPL(usb_enable_ltm);
  * Between connect detection and reset signaling there must be a delay
  * of 100ms at least for debounce and power-settling.  The corresponding
  * timer shall restart whenever the downstream port detects a disconnect.
- * 
+ *
  * Apparently there are some bluetooth and irda-dongles and a number of
  * low-speed devices for which this debounce period may last over a second.
  * Not covered by the spec - but easy to deal with.
@@ -3949,6 +3955,32 @@ static int hub_set_address(struct usb_device *udev, int devnum)
        return retval;
 }
 
+/*
+ * There are reports of USB 3.0 devices that say they support USB 2.0 Link PM
+ * when they're plugged into a USB 2.0 port, but they don't work when LPM is
+ * enabled.
+ *
+ * Only enable USB 2.0 Link PM if the port is internal (hardwired), or the
+ * device says it supports the new USB 2.0 Link PM errata by setting the BESL
+ * support bit in the BOS descriptor.
+ */
+static void hub_set_initial_usb2_lpm_policy(struct usb_device *udev)
+{
+       int connect_type;
+
+       if (!udev->usb2_hw_lpm_capable)
+               return;
+
+       connect_type = usb_get_hub_port_connect_type(udev->parent,
+                       udev->portnum);
+
+       if ((udev->bos->ext_cap->bmAttributes & USB_BESL_SUPPORT) ||
+                       connect_type == USB_PORT_CONNECT_TYPE_HARD_WIRED) {
+               udev->usb2_hw_lpm_allowed = 1;
+               usb_set_usb2_hardware_lpm(udev, 1);
+       }
+}
+
 /* Reset device, (re)assign address, get device descriptor.
  * Device connection must be stable, no more debouncing needed.
  * Returns device in USB_STATE_ADDRESS, except on error.
@@ -4055,7 +4087,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                udev->tt = &hub->tt;
                udev->ttport = port1;
        }
+
        /* Why interleave GET_DESCRIPTOR and SET_ADDRESS this way?
         * Because device hardware and firmware is sometimes buggy in
         * this area, and this is how Linux has done it for ages.
@@ -4130,11 +4162,11 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
 #undef GET_DESCRIPTOR_BUFSIZE
                }
 
-               /*
-                * If device is WUSB, we already assigned an
-                * unauthorized address in the Connect Ack sequence;
-                * authorization will assign the final address.
-                */
+               /*
+                * If device is WUSB, we already assigned an
+                * unauthorized address in the Connect Ack sequence;
+                * authorization will assign the final address.
+                */
                if (udev->wusb == 0) {
                        for (j = 0; j < SET_ADDRESS_TRIES; ++j) {
                                retval = hub_set_address(udev, devnum);
@@ -4163,7 +4195,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                        msleep(10);
                        if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
                                break;
-               }
+               }
 
                retval = usb_get_device_descriptor(udev, 8);
                if (retval < 8) {
@@ -4219,7 +4251,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
                udev->ep0.desc.wMaxPacketSize = cpu_to_le16(i);
                usb_ep0_reinit(udev);
        }
-  
+
        retval = usb_get_device_descriptor(udev, USB_DT_DEVICE_SIZE);
        if (retval < (signed)sizeof(udev->descriptor)) {
                if (retval != -ENODEV)
@@ -4242,6 +4274,7 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
        /* notify HCD that we have a device connected and addressed */
        if (hcd->driver->update_device)
                hcd->driver->update_device(hcd, udev);
+       hub_set_initial_usb2_lpm_policy(udev);
 fail:
        if (retval) {
                hub_port_disable(hub, port1, 0);
@@ -4316,7 +4349,7 @@ hub_power_remaining (struct usb_hub *hub)
        }
        if (remaining < 0) {
                dev_warn(hub->intfdev, "%dmA over power budget!\n",
-                       - remaining);
+                       -remaining);
                remaining = 0;
        }
        return remaining;
@@ -4427,7 +4460,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                        set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
                if (portstatus & USB_PORT_STAT_ENABLE)
-                       goto done;
+                       goto done;
                return;
        }
        if (hub_is_superspeed(hub->hdev))
@@ -4450,7 +4483,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                }
 
                usb_set_device_state(udev, USB_STATE_POWERED);
-               udev->bus_mA = hub->mA_per_port;
+               udev->bus_mA = hub->mA_per_port;
                udev->level = hdev->level + 1;
                udev->wusb = hub_is_wusb(hub);
 
@@ -4504,7 +4537,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
                                goto loop_disable;
                        }
                }
+
                /* check for devices running slower than they could */
                if (le16_to_cpu(udev->descriptor.bcdUSB) >= 0x0200
                                && udev->speed == USB_SPEED_FULL
@@ -4564,7 +4597,7 @@ loop:
                        dev_err(hub_dev, "unable to enumerate USB device on port %d\n",
                                        port1);
        }
+
 done:
        hub_port_disable(hub, port1, 1);
        if (hcd->driver->relinquish_port && !hub->hdev->parent)
@@ -4729,7 +4762,7 @@ static void hub_events(void)
                                 * EM interference sometimes causes badly
                                 * shielded USB devices to be shutdown by
                                 * the hub, this hack enables them again.
-                                * Works at least with mouse driver. 
+                                * Works at least with mouse driver.
                                 */
                                if (!(portstatus & USB_PORT_STAT_ENABLE)
                                    && !connect_change
@@ -4841,7 +4874,7 @@ static void hub_events(void)
                                dev_dbg(hub_dev, "over-current change\n");
                                clear_hub_feature(hdev, C_HUB_OVER_CURRENT);
                                msleep(500);    /* Cool down */
-                               hub_power_on(hub, true);
+                               hub_power_on(hub, true);
                                hub_hub_status(hub, &status, &unused);
                                if (status & HUB_STATUS_OVERCURRENT)
                                        dev_err(hub_dev, "over-current "
@@ -4861,7 +4894,7 @@ static void hub_events(void)
                usb_unlock_device(hdev);
                kref_put(&hub->kref, hub_release);
 
-        } /* end while (1) */
+       } /* end while (1) */
 }
 
 static int hub_thread(void *__unused)
@@ -4886,7 +4919,7 @@ static int hub_thread(void *__unused)
 
 static const struct usb_device_id hub_id_table[] = {
     { .match_flags = USB_DEVICE_ID_MATCH_VENDOR
-                  | USB_DEVICE_ID_MATCH_INT_CLASS,
+                       | USB_DEVICE_ID_MATCH_INT_CLASS,
       .idVendor = USB_VENDOR_GENESYS_LOGIC,
       .bInterfaceClass = USB_CLASS_HUB,
       .driver_info = HUB_QUIRK_CHECK_PORT_AUTOSUSPEND},
@@ -5086,6 +5119,12 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
        }
        parent_hub = usb_hub_to_struct_hub(parent_hdev);
 
+       /* Disable USB2 hardware LPM.
+        * It will be re-enabled by the enumeration process.
+        */
+       if (udev->usb2_hw_lpm_enabled == 1)
+               usb_set_usb2_hardware_lpm(udev, 0);
+
        bos = udev->bos;
        udev->bos = NULL;
 
@@ -5120,13 +5159,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 
        if (ret < 0)
                goto re_enumerate;
+
        /* Device might have changed firmware (DFU or similar) */
        if (descriptors_changed(udev, &descriptor, bos)) {
                dev_info(&udev->dev, "device firmware changed\n");
                udev->descriptor = descriptor;  /* for disconnect() calls */
                goto re_enumerate;
-       }
+       }
 
        /* Restore the device's previous configuration */
        if (!udev->actconfig)
@@ -5151,7 +5190,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
                        udev->actconfig->desc.bConfigurationValue, ret);
                mutex_unlock(hcd->bandwidth_mutex);
                goto re_enumerate;
-       }
+       }
        mutex_unlock(hcd->bandwidth_mutex);
        usb_set_device_state(udev, USB_STATE_CONFIGURED);
 
@@ -5193,12 +5232,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
 
 done:
        /* Now that the alt settings are re-installed, enable LTM and LPM. */
+       usb_set_usb2_hardware_lpm(udev, 1);
        usb_unlocked_enable_lpm(udev);
        usb_enable_ltm(udev);
        usb_release_bos_descriptor(udev);
        udev->bos = bos;
        return 0;
+
 re_enumerate:
        /* LPM state doesn't matter when we're about to destroy the device. */
        hub_port_logical_disconnect(parent_hub, port1);
index 82927e1ed27d078f513a69727ebf7e55ef885ad8..bb315970e475e3bd90b5985c45a0afc0daf61fd2 100644 (file)
@@ -1182,8 +1182,12 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0)
                        put_device(&dev->actconfig->interface[i]->dev);
                        dev->actconfig->interface[i] = NULL;
                }
+
+               if (dev->usb2_hw_lpm_enabled == 1)
+                       usb_set_usb2_hardware_lpm(dev, 0);
                usb_unlocked_disable_lpm(dev);
                usb_disable_ltm(dev);
+
                dev->actconfig = NULL;
                if (dev->state == USB_STATE_CONFIGURED)
                        usb_set_device_state(dev, USB_STATE_ADDRESS);
index 01fe36273f3b144cb1c1fe3306ebdf16459c605f..12924dbfdc2cb2c6b18319f4c4dec2679e3ae1bc 100644 (file)
@@ -13,6 +13,7 @@
 
 #include <linux/usb.h>
 #include <linux/usb/quirks.h>
+#include <linux/usb/hcd.h>
 #include "usb.h"
 
 /* Lists of quirky USB devices, split in device quirks and interface quirks.
@@ -161,6 +162,21 @@ static const struct usb_device_id usb_interface_quirk_list[] = {
        { }  /* terminating entry must be last */
 };
 
+static const struct usb_device_id usb_amd_resume_quirk_list[] = {
+       /* Lenovo Mouse with Pixart controller */
+       { USB_DEVICE(0x17ef, 0x602e), .driver_info = USB_QUIRK_RESET_RESUME },
+
+       /* Pixart Mouse */
+       { USB_DEVICE(0x093a, 0x2500), .driver_info = USB_QUIRK_RESET_RESUME },
+       { USB_DEVICE(0x093a, 0x2510), .driver_info = USB_QUIRK_RESET_RESUME },
+       { USB_DEVICE(0x093a, 0x2521), .driver_info = USB_QUIRK_RESET_RESUME },
+
+       /* Logitech Optical Mouse M90/M100 */
+       { USB_DEVICE(0x046d, 0xc05a), .driver_info = USB_QUIRK_RESET_RESUME },
+
+       { }  /* terminating entry must be last */
+};
+
 static bool usb_match_any_interface(struct usb_device *udev,
                                    const struct usb_device_id *id)
 {
@@ -187,6 +203,18 @@ static bool usb_match_any_interface(struct usb_device *udev,
        return false;
 }
 
+static int usb_amd_resume_quirk(struct usb_device *udev)
+{
+       struct usb_hcd *hcd;
+
+       hcd = bus_to_hcd(udev->bus);
+       /* The device should be attached directly to root hub */
+       if (udev->level == 1 && hcd->amd_resume_bug == 1)
+               return 1;
+
+       return 0;
+}
+
 static u32 __usb_detect_quirks(struct usb_device *udev,
                               const struct usb_device_id *id)
 {
@@ -212,6 +240,15 @@ static u32 __usb_detect_quirks(struct usb_device *udev,
 void usb_detect_quirks(struct usb_device *udev)
 {
        udev->quirks = __usb_detect_quirks(udev, usb_quirk_list);
+
+       /*
+        * Pixart-based mice would trigger remote wakeup issue on AMD
+        * Yangtze chipset, so set them as RESET_RESUME flag.
+        */
+       if (usb_amd_resume_quirk(udev))
+               udev->quirks |= __usb_detect_quirks(udev,
+                               usb_amd_resume_quirk_list);
+
        if (udev->quirks)
                dev_dbg(&udev->dev, "USB quirks for this device: %x\n",
                        udev->quirks);
index 6d2c8edb1ffe939e8c31b4a1eeb4585b27be619b..52a97adf02a04dcb39e866a131bfdfa5053efe51 100644 (file)
@@ -23,14 +23,16 @@ static ssize_t field##_show(struct device *dev,                             \
 {                                                                      \
        struct usb_device *udev;                                        \
        struct usb_host_config *actconfig;                              \
+       ssize_t rc = 0;                                                 \
                                                                        \
        udev = to_usb_device(dev);                                      \
+       usb_lock_device(udev);                                          \
        actconfig = udev->actconfig;                                    \
        if (actconfig)                                                  \
-               return sprintf(buf, format_string,                      \
+               rc = sprintf(buf, format_string,                        \
                                actconfig->desc.field);                 \
-       else                                                            \
-               return 0;                                               \
+       usb_unlock_device(udev);                                        \
+       return rc;                                                      \
 }                                                                      \
 
 #define usb_actconfig_attr(field, format_string)               \
@@ -45,12 +47,15 @@ static ssize_t bMaxPower_show(struct device *dev,
 {
        struct usb_device *udev;
        struct usb_host_config *actconfig;
+       ssize_t rc = 0;
 
        udev = to_usb_device(dev);
+       usb_lock_device(udev);
        actconfig = udev->actconfig;
-       if (!actconfig)
-               return 0;
-       return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+       if (actconfig)
+               rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+       usb_unlock_device(udev);
+       return rc;
 }
 static DEVICE_ATTR_RO(bMaxPower);
 
@@ -59,12 +64,15 @@ static ssize_t configuration_show(struct device *dev,
 {
        struct usb_device *udev;
        struct usb_host_config *actconfig;
+       ssize_t rc = 0;
 
        udev = to_usb_device(dev);
+       usb_lock_device(udev);
        actconfig = udev->actconfig;
-       if ((!actconfig) || (!actconfig->string))
-               return 0;
-       return sprintf(buf, "%s\n", actconfig->string);
+       if (actconfig && actconfig->string)
+               rc = sprintf(buf, "%s\n", actconfig->string);
+       usb_unlock_device(udev);
+       return rc;
 }
 static DEVICE_ATTR_RO(configuration);
 
@@ -390,7 +398,8 @@ static DEVICE_ATTR_RW(autosuspend);
 static const char on_string[] = "on";
 static const char auto_string[] = "auto";
 
-static void warn_level(void) {
+static void warn_level(void)
+{
        static int level_warned;
 
        if (!level_warned) {
@@ -449,7 +458,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev,
        struct usb_device *udev = to_usb_device(dev);
        const char *p;
 
-       if (udev->usb2_hw_lpm_enabled == 1)
+       if (udev->usb2_hw_lpm_allowed == 1)
                p = "enabled";
        else
                p = "disabled";
@@ -469,8 +478,10 @@ static ssize_t usb2_hardware_lpm_store(struct device *dev,
 
        ret = strtobool(buf, &value);
 
-       if (!ret)
+       if (!ret) {
+               udev->usb2_hw_lpm_allowed = value;
                ret = usb_set_usb2_hardware_lpm(udev, value);
+       }
 
        usb_unlock_device(udev);
 
@@ -644,7 +655,7 @@ static ssize_t authorized_store(struct device *dev,
                result = usb_deauthorize_device(usb_dev);
        else
                result = usb_authorize_device(usb_dev);
-       return result < 0? result : size;
+       return result < 0 ? result : size;
 }
 static DEVICE_ATTR_IGNORE_LOCKDEP(authorized, S_IRUGO | S_IWUSR,
                                  authorized_show, authorized_store);
@@ -764,6 +775,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
         * Following that are the raw descriptor entries for all the
         * configurations (config plus subsidiary descriptors).
         */
+       usb_lock_device(udev);
        for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
                        nleft > 0; ++cfgno) {
                if (cfgno < 0) {
@@ -784,6 +796,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
                        off -= srclen;
                }
        }
+       usb_unlock_device(udev);
        return count - nleft;
 }
 
@@ -870,9 +883,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr,
        char *string;
 
        intf = to_usb_interface(dev);
-       string = intf->cur_altsetting->string;
-       barrier();              /* The altsetting might change! */
-
+       string = ACCESS_ONCE(intf->cur_altsetting->string);
        if (!string)
                return 0;
        return sprintf(buf, "%s\n", string);
@@ -888,7 +899,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
 
        intf = to_usb_interface(dev);
        udev = interface_to_usbdev(intf);
-       alt = intf->cur_altsetting;
+       alt = ACCESS_ONCE(intf->cur_altsetting);
 
        return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
                        "ic%02Xisc%02Xip%02Xin%02X\n",
@@ -909,23 +920,14 @@ static ssize_t supports_autosuspend_show(struct device *dev,
                                         struct device_attribute *attr,
                                         char *buf)
 {
-       struct usb_interface *intf;
-       struct usb_device *udev;
-       int ret;
+       int s;
 
-       intf = to_usb_interface(dev);
-       udev = interface_to_usbdev(intf);
-
-       usb_lock_device(udev);
+       device_lock(dev);
        /* Devices will be autosuspended even when an interface isn't claimed */
-       if (!intf->dev.driver ||
-                       to_usb_driver(intf->dev.driver)->supports_autosuspend)
-               ret = sprintf(buf, "%u\n", 1);
-       else
-               ret = sprintf(buf, "%u\n", 0);
-       usb_unlock_device(udev);
+       s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
+       device_unlock(dev);
 
-       return ret;
+       return sprintf(buf, "%u\n", s);
 }
 static DEVICE_ATTR_RO(supports_autosuspend);
 
index c12bc790a6a702de065087b9d4c07563f3bb54b1..e62208356c8915d6fc565173f4561500cb5261a7 100644 (file)
@@ -138,13 +138,19 @@ void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
 }
 EXPORT_SYMBOL_GPL(usb_anchor_urb);
 
+static int usb_anchor_check_wakeup(struct usb_anchor *anchor)
+{
+       return atomic_read(&anchor->suspend_wakeups) == 0 &&
+               list_empty(&anchor->urb_list);
+}
+
 /* Callers must hold anchor->lock */
 static void __usb_unanchor_urb(struct urb *urb, struct usb_anchor *anchor)
 {
        urb->anchor = NULL;
        list_del(&urb->anchor_list);
        usb_put_urb(urb);
-       if (list_empty(&anchor->urb_list))
+       if (usb_anchor_check_wakeup(anchor))
                wake_up(&anchor->wait);
 }
 
@@ -845,6 +851,39 @@ void usb_unlink_anchored_urbs(struct usb_anchor *anchor)
 }
 EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 
+/**
+ * usb_anchor_suspend_wakeups
+ * @anchor: the anchor you want to suspend wakeups on
+ *
+ * Call this to stop the last urb being unanchored from waking up any
+ * usb_wait_anchor_empty_timeout waiters. This is used in the hcd urb give-
+ * back path to delay waking up until after the completion handler has run.
+ */
+void usb_anchor_suspend_wakeups(struct usb_anchor *anchor)
+{
+       if (anchor)
+               atomic_inc(&anchor->suspend_wakeups);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_suspend_wakeups);
+
+/**
+ * usb_anchor_resume_wakeups
+ * @anchor: the anchor you want to resume wakeups on
+ *
+ * Allow usb_wait_anchor_empty_timeout waiters to be woken up again, and
+ * wake up any current waiters if the anchor is empty.
+ */
+void usb_anchor_resume_wakeups(struct usb_anchor *anchor)
+{
+       if (!anchor)
+               return;
+
+       atomic_dec(&anchor->suspend_wakeups);
+       if (usb_anchor_check_wakeup(anchor))
+               wake_up(&anchor->wait);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_resume_wakeups);
+
 /**
  * usb_wait_anchor_empty_timeout - wait for an anchor to be unused
  * @anchor: the anchor you want to become unused
@@ -858,7 +897,8 @@ EXPORT_SYMBOL_GPL(usb_unlink_anchored_urbs);
 int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
                                  unsigned int timeout)
 {
-       return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
+       return wait_event_timeout(anchor->wait,
+                                 usb_anchor_check_wakeup(anchor),
                                  msecs_to_jiffies(timeout));
 }
 EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
index 0a6ee2e70b25afecaae6dfce65a03fe22e40ca04..4d1144990d4c848362c2f86cf8aa1039c3d70923 100644 (file)
@@ -497,7 +497,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
                dev->authorized = 1;
        else {
                dev->authorized = usb_hcd->authorized_default;
-               dev->wusb = usb_bus_is_wusb(bus)? 1 : 0;
+               dev->wusb = usb_bus_is_wusb(bus) ? 1 : 0;
        }
        return dev;
 }
index 823857767a16f3384730dcf1f90c42f8eedc3588..c49383669cd87ce4dda9b996fd9b3f1caf03bd2b 100644 (file)
@@ -35,6 +35,7 @@ extern int usb_get_device_descriptor(struct usb_device *dev,
                unsigned int size);
 extern int usb_get_bos_descriptor(struct usb_device *dev);
 extern void usb_release_bos_descriptor(struct usb_device *dev);
+extern int usb_device_supports_lpm(struct usb_device *udev);
 extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
index 2e252aae51ca0bcc5da41b3251c45cec91cced7d..31443aeedcdbf374a6d663e69087ba18f9c37612 100644 (file)
@@ -165,7 +165,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
        return 0;
 
 err3:
-       pci_set_drvdata(pci, NULL);
        platform_device_put(dwc3);
 err1:
        pci_disable_device(pci);
@@ -180,7 +179,6 @@ static void dwc3_pci_remove(struct pci_dev *pci)
        platform_device_unregister(glue->dwc3);
        platform_device_unregister(glue->usb2_phy);
        platform_device_unregister(glue->usb3_phy);
-       pci_set_drvdata(pci, NULL);
        pci_disable_device(pci);
 }
 
index 7fa93f4bc507de41782486301760506e18f44a03..95f7649c71a78745692a63bafd1527daacda52e3 100644 (file)
@@ -352,7 +352,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        response_pkt = (__le16 *) dwc->setup_buf;
        *response_pkt = cpu_to_le16(usb_status);
@@ -470,7 +470,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc,
 
        default:
                return -EINVAL;
-       };
+       }
 
        return 0;
 }
@@ -709,7 +709,7 @@ static int dwc3_ep0_std_request(struct dwc3 *dwc, struct usb_ctrlrequest *ctrl)
                dev_vdbg(dwc->dev, "Forwarding to gadget driver\n");
                ret = dwc3_ep0_delegate_req(dwc, ctrl);
                break;
-       };
+       }
 
        return ret;
 }
index 5e29ddeb4d33d0c65f29116dca2ddbe42ea5407f..8cfc3191be50aee8f9157ba95e84c49446d1ae55 100644 (file)
@@ -568,10 +568,6 @@ try_again:
                dbgp_printk("Could not find attached debug device\n");
                goto err;
        }
-       if (ret < 0) {
-               dbgp_printk("Attached device is not a debug device\n");
-               goto err;
-       }
        dbgp_endpoint_out = dbgp_desc.bDebugOutEndpoint;
        dbgp_endpoint_in = dbgp_desc.bDebugInEndpoint;
 
index a9a4346c83aae221776a297eff8330aab6bb733c..54a1e2954cea9d53384085e04b91d65418fb575e 100644 (file)
@@ -3078,8 +3078,6 @@ static void udc_pci_remove(struct pci_dev *pdev)
        if (dev->active)
                pci_disable_device(pdev);
 
-       pci_set_drvdata(pdev, NULL);
-
        udc_remove(dev);
 }
 
index d4f0f3305759876a2fc1e81cb96e0ade69918eca..3e7ae707f691c4b0cf4b701024d8df0af4a5bb69 100644 (file)
@@ -354,7 +354,7 @@ static u8 encode_bMaxPower(enum usb_device_speed speed,
                return DIV_ROUND_UP(val, 8);
        default:
                return DIV_ROUND_UP(val, 2);
-       };
+       }
 }
 
 static int config_buf(struct usb_configuration *config,
index b8a2376971a47aa357794117f74390aaee01a594..8f4dae3109235624e9e60c04a837aab7f6571e4a 100644 (file)
@@ -544,7 +544,7 @@ static int dummy_enable(struct usb_ep *_ep,
                 default:
                         val = "ctrl";
                         break;
-                }; val; }),
+                } val; }),
                max, ep->stream_en ? "enabled" : "disabled");
 
        /* at this point real hardware should be NAKing transfers
@@ -2271,7 +2271,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
                default:
                        s = "?";
                        break;
-                }; s; }),
+                } s; }),
                ep, ep ? (usb_pipein(urb->pipe) ? "in" : "out") : "",
                ({ char *s; \
                switch (usb_pipetype(urb->pipe)) { \
@@ -2287,7 +2287,7 @@ static inline ssize_t show_urb(char *buf, size_t size, struct urb *urb)
                default: \
                        s = "-iso"; \
                        break; \
-               }; s; }),
+               } s; }),
                urb->actual_length, urb->transfer_buffer_length);
 }
 
index 5327c82472eda8034a54ca664392baf5875b0a7a..2344efe4f4ce8611e6869605e89e1c18aef7000a 100644 (file)
@@ -76,7 +76,9 @@ struct gfs_ffs_obj {
 
 USB_GADGET_COMPOSITE_OPTIONS();
 
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
 USB_ETHERNET_MODULE_PARAMETERS();
+#endif
 
 static struct usb_device_descriptor gfs_dev_desc = {
        .bLength                = sizeof gfs_dev_desc,
index c64deb9e3d62368e073893448dfe9f8b7ec610cc..f82768015715c69ed998e2cb95cc0735dd6a035b 100644 (file)
@@ -1165,7 +1165,7 @@ static int udc_proc_read(struct seq_file *m, void *v)
                                s = "invalid"; break;
                        default:
                                s = "?"; break;
-                       }; s; }),
+                       } s; }),
                        (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
                        (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
                        (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
@@ -1701,7 +1701,6 @@ static void goku_remove(struct pci_dev *pdev)
        if (dev->enabled)
                pci_disable_device(pdev);
 
-       pci_set_drvdata(pdev, NULL);
        dev->regs = NULL;
 
        INFO(dev, "unbind\n");
index 561b30efb8ee84de6fc37c33005cdadc6fbcc5ff..5b06c989951cdac24da064551a789cb6579b1b64 100644 (file)
@@ -1936,7 +1936,7 @@ static int mv_u3d_probe(struct platform_device *dev)
        }
        u3d->irq = r->start;
        if (request_irq(u3d->irq, mv_u3d_irq,
-               IRQF_DISABLED | IRQF_SHARED, driver_name, u3d)) {
+               IRQF_SHARED, driver_name, u3d)) {
                u3d->irq = 0;
                dev_err(&dev->dev, "Request irq %d for u3d failed\n",
                        u3d->irq);
index 0781bff70015da3e259106a829d50ad1a507e346..fc852177c08719428d9e445546adb0f53f99a47b 100644 (file)
@@ -129,7 +129,7 @@ static char *type_string (u8 bmAttributes)
        case USB_ENDPOINT_XFER_BULK:    return "bulk";
        case USB_ENDPOINT_XFER_ISOC:    return "iso";
        case USB_ENDPOINT_XFER_INT:     return "intr";
-       };
+       }
        return "control";
 }
 #endif
@@ -1630,7 +1630,7 @@ static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
                                        val = "intr"; break;
                                 default:
                                        val = "iso"; break;
-                                }; val; }),
+                                } val; }),
                                usb_endpoint_maxp (d) & 0x1fff,
                                ep->dma ? "dma" : "pio", ep->fifo_size
                                );
@@ -2680,7 +2680,6 @@ static void net2280_remove (struct pci_dev *pdev)
        if (dev->enabled)
                pci_disable_device (pdev);
        device_remove_file (&pdev->dev, &dev_attr_registers);
-       pci_set_drvdata (pdev, NULL);
 
        INFO (dev, "unbind\n");
 }
index 24174e1d15642be63c652340c80e81dc61d1bb15..32d5e923750b017a479d394e51ba09c991c8ef4d 100644 (file)
@@ -3080,7 +3080,6 @@ static void pch_udc_remove(struct pci_dev *pdev)
        if (dev->active)
                pci_disable_device(pdev);
        kfree(dev);
-       pci_set_drvdata(pdev, NULL);
 }
 
 #ifdef CONFIG_PM
index 9575085ded8112e05eddda04a60d52231b1f0a02..a3ad732bc812254798a11f4664093008729ba567 100644 (file)
@@ -1068,7 +1068,7 @@ static int rndis_proc_show(struct seq_file *m, void *v)
                                s = "RNDIS_INITIALIZED"; break;
                         case RNDIS_DATA_INITIALIZED:
                                s = "RNDIS_DATA_INITIALIZED"; break;
-                       }; s; }),
+                       } s; }),
                         param->medium,
                         (param->media_state) ? 0 : param->speed*100,
                         (param->media_state) ? "disconnected" : "connected",
index 0ff33396eef3ac6e99a78af1db7874d82cff8480..eccea1df702df3afc45c692de31b23febfb52c37 100644 (file)
@@ -472,7 +472,7 @@ static int usbg_bot_setup(struct usb_function *f,
                bot_enqueue_cmd_cbw(fu);
                return 0;
                break;
-       };
+       }
        return -ENOTSUPP;
 }
 
@@ -617,7 +617,7 @@ static void uasp_status_data_cmpl(struct usb_ep *ep, struct usb_request *req)
 
        default:
                BUG();
-       };
+       }
        return;
 
 cleanup:
index b3f20d7f15dee9554ef9ef96acd735c3e3926064..80e72fba01f4814970d7ebe02fe19605606ca7f6 100644 (file)
@@ -54,7 +54,7 @@ config USB_EHCI_HCD
 
 config USB_EHCI_ROOT_HUB_TT
        bool "Root Hub Transaction Translators"
-       depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST
+       depends on USB_EHCI_HCD
        ---help---
          Some EHCI chips have vendor-specific extensions to integrate
          transaction translators, so that no OHCI or UHCI companion
@@ -66,7 +66,7 @@ config USB_EHCI_ROOT_HUB_TT
 
 config USB_EHCI_TT_NEWSCHED
        bool "Improved Transaction Translator scheduling"
-       depends on USB_EHCI_HCD || USB_CHIPIDEA_HOST
+       depends on USB_EHCI_HCD
        default y
        ---help---
          This changes the periodic scheduling code to fill more of the low
@@ -203,12 +203,11 @@ config USB_EHCI_SH
          Enables support for the on-chip EHCI controller on the SuperH.
          If you use the PCI EHCI controller, this option is not necessary.
 
-config USB_EHCI_S5P
+config USB_EHCI_EXYNOS
        tristate "EHCI support for Samsung S5P/EXYNOS SoC Series"
        depends on PLAT_S5P || ARCH_EXYNOS
        help
-       Enable support for the Samsung S5Pxxxx and Exynos3/4/5 SOC's
-       on-chip EHCI controller.
+       Enable support for the Samsung Exynos SOC's on-chip EHCI controller.
 
 config USB_EHCI_MV
        bool "EHCI support for Marvell PXA/MMP USB controller"
@@ -224,7 +223,7 @@ config USB_EHCI_MV
          on-chip EHCI USB controller" for those.
 
 config USB_W90X900_EHCI
-       bool "W90X900(W90P910) EHCI support"
+       tristate "W90X900(W90P910) EHCI support"
        depends on ARCH_W90X900
        ---help---
                Enables support for the W90X900 USB controller
@@ -367,14 +366,62 @@ config USB_OHCI_HCD
 if USB_OHCI_HCD
 
 config USB_OHCI_HCD_OMAP1
-       bool "OHCI support for OMAP1/2 chips"
+       tristate "OHCI support for OMAP1/2 chips"
        depends on ARCH_OMAP1
        default y
        ---help---
          Enables support for the OHCI controller on OMAP1/2 chips.
 
+config USB_OHCI_HCD_SPEAR
+        tristate "Support for ST SPEAr on-chip OHCI USB controller"
+        depends on USB_OHCI_HCD && PLAT_SPEAR
+        default y
+        ---help---
+          Enables support for the on-chip OHCI controller on
+          ST SPEAr chips.
+
+config USB_OHCI_HCD_S3C2410
+        tristate "OHCI support for Samsung S3C24xx/S3C64xx SoC series"
+        depends on USB_OHCI_HCD && (ARCH_S3C24XX || ARCH_S3C64XX)
+        default y
+        ---help---
+          Enables support for the on-chip OHCI controller on
+          S3C24xx/S3C64xx chips.
+
+config USB_OHCI_HCD_LPC32XX
+       tristate "Support for LPC on-chip OHCI USB controller"
+       depends on USB_OHCI_HCD && ARCH_LPC32XX
+       default y
+       ---help---
+          Enables support for the on-chip OHCI controller on
+          NXP chips.
+
+config USB_OHCI_HCD_EP93XX
+       tristate "Support for EP93XX on-chip OHCI USB controller"
+       depends on USB_OHCI_HCD && ARCH_EP93XX
+       default y
+       ---help---
+         Enables support for the on-chip OHCI controller on
+         EP93XX chips.
+
+config USB_OHCI_HCD_PXA27X
+       tristate "Support for PXA27X/PXA3XX on-chip OHCI USB controller"
+       depends on USB_OHCI_HCD && (PXA27x || PXA3xx)
+       default y
+       ---help---
+         Enables support for the on-chip OHCI controller on
+         PXA27x/PXA3xx chips.
+
+config USB_OHCI_HCD_AT91
+        tristate "Support for Atmel on-chip OHCI USB controller"
+        depends on USB_OHCI_HCD && ARCH_AT91
+        default y
+        ---help---
+          Enables support for the on-chip OHCI controller on
+          Atmel chips.
+
 config USB_OHCI_HCD_OMAP3
-       bool "OHCI support for OMAP3 and later chips"
+       tristate "OHCI support for OMAP3 and later chips"
        depends on (ARCH_OMAP3 || ARCH_OMAP4)
        default y
        ---help---
@@ -454,8 +501,8 @@ config USB_OHCI_SH
          If you use the PCI OHCI controller, this option is not necessary.
 
 config USB_OHCI_EXYNOS
-       boolean "OHCI support for Samsung EXYNOS SoC Series"
-       depends on ARCH_EXYNOS
+       tristate "OHCI support for Samsung S5P/EXYNOS SoC Series"
+       depends on PLAT_S5P || ARCH_EXYNOS
        help
         Enable support for the Samsung Exynos SOC's on-chip OHCI controller.
 
index 50b0041c09a95464ed9ff353b090818e39d0112c..9dc11c2ee45410ce7a3e4a2fd7af722bfb64e937 100644 (file)
@@ -34,10 +34,11 @@ obj-$(CONFIG_USB_EHCI_MXC)  += ehci-mxc.o
 obj-$(CONFIG_USB_EHCI_HCD_OMAP)        += ehci-omap.o
 obj-$(CONFIG_USB_EHCI_HCD_ORION)       += ehci-orion.o
 obj-$(CONFIG_USB_EHCI_HCD_SPEAR)       += ehci-spear.o
-obj-$(CONFIG_USB_EHCI_S5P)     += ehci-s5p.o
+obj-$(CONFIG_USB_EHCI_EXYNOS)  += ehci-exynos.o
 obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
 obj-$(CONFIG_USB_EHCI_MSM)     += ehci-msm.o
 obj-$(CONFIG_USB_EHCI_TEGRA)   += ehci-tegra.o
+obj-$(CONFIG_USB_W90X900_EHCI) += ehci-w90x900.o
 
 obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
 obj-$(CONFIG_USB_ISP116X_HCD)  += isp116x-hcd.o
@@ -46,6 +47,15 @@ obj-$(CONFIG_USB_ISP1362_HCD)        += isp1362-hcd.o
 obj-$(CONFIG_USB_OHCI_HCD)     += ohci-hcd.o
 obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o
 obj-$(CONFIG_USB_OHCI_HCD_PLATFORM)    += ohci-platform.o
+obj-$(CONFIG_USB_OHCI_EXYNOS)  += ohci-exynos.o
+obj-$(CONFIG_USB_OHCI_HCD_OMAP1)       += ohci-omap.o
+obj-$(CONFIG_USB_OHCI_HCD_OMAP3)       += ohci-omap3.o
+obj-$(CONFIG_USB_OHCI_HCD_SPEAR)       += ohci-spear.o
+obj-$(CONFIG_USB_OHCI_HCD_AT91)        += ohci-at91.o
+obj-$(CONFIG_USB_OHCI_HCD_S3C2410)     += ohci-s3c2410.o
+obj-$(CONFIG_USB_OHCI_HCD_LPC32XX)     += ohci-nxp.o
+obj-$(CONFIG_USB_OHCI_HCD_EP93XX)      += ohci-ep93xx.o
+obj-$(CONFIG_USB_OHCI_HCD_PXA27X)      += ohci-pxa27x.o
 
 obj-$(CONFIG_USB_UHCI_HCD)     += uhci-hcd.o
 obj-$(CONFIG_USB_FHCI_HCD)     += fhci.o
index aa5b603f39336f69ffc2dce8a787f64694fa7d23..4a9c2edbcb2bccf8cdf678aa04266fb5d78560d8 100644 (file)
@@ -334,6 +334,7 @@ static inline void remove_debug_files (struct ehci_hcd *bus) { }
 /* troubleshooting help: expose state in debugfs */
 
 static int debug_async_open(struct inode *, struct file *);
+static int debug_bandwidth_open(struct inode *, struct file *);
 static int debug_periodic_open(struct inode *, struct file *);
 static int debug_registers_open(struct inode *, struct file *);
 
@@ -347,6 +348,13 @@ static const struct file_operations debug_async_fops = {
        .release        = debug_close,
        .llseek         = default_llseek,
 };
+static const struct file_operations debug_bandwidth_fops = {
+       .owner          = THIS_MODULE,
+       .open           = debug_bandwidth_open,
+       .read           = debug_output,
+       .release        = debug_close,
+       .llseek         = default_llseek,
+};
 static const struct file_operations debug_periodic_fops = {
        .owner          = THIS_MODULE,
        .open           = debug_periodic_open,
@@ -379,7 +387,7 @@ struct debug_buffer {
                case QH_LOW_SPEED:  tmp = 'l'; break; \
                case QH_HIGH_SPEED: tmp = 'h'; break; \
                default: tmp = '?'; break; \
-               }; tmp; })
+               } tmp; })
 
 static inline char token_mark(struct ehci_hcd *ehci, __hc32 token)
 {
@@ -525,6 +533,89 @@ static ssize_t fill_async_buffer(struct debug_buffer *buf)
        return strlen(buf->output_buf);
 }
 
+static ssize_t fill_bandwidth_buffer(struct debug_buffer *buf)
+{
+       struct ehci_hcd         *ehci;
+       struct ehci_tt          *tt;
+       struct ehci_per_sched   *ps;
+       unsigned                temp, size;
+       char                    *next;
+       unsigned                i;
+       u8                      *bw;
+       u16                     *bf;
+       u8                      budget[EHCI_BANDWIDTH_SIZE];
+
+       ehci = hcd_to_ehci(bus_to_hcd(buf->bus));
+       next = buf->output_buf;
+       size = buf->alloc_size;
+
+       *next = 0;
+
+       spin_lock_irq(&ehci->lock);
+
+       /* Dump the HS bandwidth table */
+       temp = scnprintf(next, size,
+                       "HS bandwidth allocation (us per microframe)\n");
+       size -= temp;
+       next += temp;
+       for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
+               bw = &ehci->bandwidth[i];
+               temp = scnprintf(next, size,
+                               "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
+                               i, bw[0], bw[1], bw[2], bw[3],
+                                       bw[4], bw[5], bw[6], bw[7]);
+               size -= temp;
+               next += temp;
+       }
+
+       /* Dump all the FS/LS tables */
+       list_for_each_entry(tt, &ehci->tt_list, tt_list) {
+               temp = scnprintf(next, size,
+                               "\nTT %s port %d  FS/LS bandwidth allocation (us per frame)\n",
+                               dev_name(&tt->usb_tt->hub->dev),
+                               tt->tt_port + !!tt->usb_tt->multi);
+               size -= temp;
+               next += temp;
+
+               bf = tt->bandwidth;
+               temp = scnprintf(next, size,
+                               "  %5u%5u%5u%5u%5u%5u%5u%5u\n",
+                               bf[0], bf[1], bf[2], bf[3],
+                                       bf[4], bf[5], bf[6], bf[7]);
+               size -= temp;
+               next += temp;
+
+               temp = scnprintf(next, size,
+                               "FS/LS budget (us per microframe)\n");
+               size -= temp;
+               next += temp;
+               compute_tt_budget(budget, tt);
+               for (i = 0; i < EHCI_BANDWIDTH_SIZE; i += 8) {
+                       bw = &budget[i];
+                       temp = scnprintf(next, size,
+                                       "%2u: %4u%4u%4u%4u%4u%4u%4u%4u\n",
+                                       i, bw[0], bw[1], bw[2], bw[3],
+                                               bw[4], bw[5], bw[6], bw[7]);
+                       size -= temp;
+                       next += temp;
+               }
+               list_for_each_entry(ps, &tt->ps_list, ps_list) {
+                       temp = scnprintf(next, size,
+                                       "%s ep %02x:  %4u @ %2u.%u+%u mask %04x\n",
+                                       dev_name(&ps->udev->dev),
+                                       ps->ep->desc.bEndpointAddress,
+                                       ps->tt_usecs,
+                                       ps->bw_phase, ps->phase_uf,
+                                       ps->bw_period, ps->cs_mask);
+                       size -= temp;
+                       next += temp;
+               }
+       }
+       spin_unlock_irq(&ehci->lock);
+
+       return next - buf->output_buf;
+}
+
 #define DBG_SCHED_LIMIT 64
 static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
 {
@@ -571,7 +662,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
                        case Q_TYPE_QH:
                                hw = p.qh->hw;
                                temp = scnprintf (next, size, " qh%d-%04x/%p",
-                                               p.qh->period,
+                                               p.qh->ps.period,
                                                hc32_to_cpup(ehci,
                                                        &hw->hw_info2)
                                                        /* uframe masks */
@@ -618,7 +709,8 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
                                                speed_char (scratch),
                                                scratch & 0x007f,
                                                (scratch >> 8) & 0x000f, type,
-                                               p.qh->usecs, p.qh->c_usecs,
+                                               p.qh->ps.usecs,
+                                               p.qh->ps.c_usecs,
                                                temp,
                                                0x7ff & (scratch >> 16));
 
@@ -645,7 +737,7 @@ static ssize_t fill_periodic_buffer(struct debug_buffer *buf)
                        case Q_TYPE_SITD:
                                temp = scnprintf (next, size,
                                        " sitd%d-%04x/%p",
-                                       p.sitd->stream->interval,
+                                       p.sitd->stream->ps.period,
                                        hc32_to_cpup(ehci, &p.sitd->hw_uframe)
                                                & 0x0000ffff,
                                        p.sitd);
@@ -918,6 +1010,7 @@ static int debug_close(struct inode *inode, struct file *file)
 
        return 0;
 }
+
 static int debug_async_open(struct inode *inode, struct file *file)
 {
        file->private_data = alloc_buffer(inode->i_private, fill_async_buffer);
@@ -925,6 +1018,14 @@ static int debug_async_open(struct inode *inode, struct file *file)
        return file->private_data ? 0 : -ENOMEM;
 }
 
+static int debug_bandwidth_open(struct inode *inode, struct file *file)
+{
+       file->private_data = alloc_buffer(inode->i_private,
+                       fill_bandwidth_buffer);
+
+       return file->private_data ? 0 : -ENOMEM;
+}
+
 static int debug_periodic_open(struct inode *inode, struct file *file)
 {
        struct debug_buffer *buf;
@@ -957,6 +1058,10 @@ static inline void create_debug_files (struct ehci_hcd *ehci)
                                                &debug_async_fops))
                goto file_error;
 
+       if (!debugfs_create_file("bandwidth", S_IRUGO, ehci->debug_dir, bus,
+                                               &debug_bandwidth_fops))
+               goto file_error;
+
        if (!debugfs_create_file("periodic", S_IRUGO, ehci->debug_dir, bus,
                                                &debug_periodic_fops))
                goto file_error;
similarity index 55%
rename from drivers/usb/host/ehci-s5p.c
rename to drivers/usb/host/ehci-exynos.c
index d919ed47bd47fdc8f631b1804e74bd54e134cf17..e97c198e052fd88aa99a98679952769678728a75 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * SAMSUNG S5P USB HOST EHCI Controller
+ * SAMSUNG EXYNOS USB HOST EHCI Controller
  *
  * Copyright (C) 2011 Samsung Electronics Co.Ltd
  * Author: Jingoo Han <jg1.han@samsung.com>
@@ -20,7 +20,6 @@
 #include <linux/of.h>
 #include <linux/of_gpio.h>
 #include <linux/platform_device.h>
-#include <linux/platform_data/usb-ehci-s5p.h>
 #include <linux/usb/phy.h>
 #include <linux/usb/samsung_usb_phy.h>
 #include <linux/usb.h>
@@ -29,7 +28,7 @@
 
 #include "ehci.h"
 
-#define DRIVER_DESC "EHCI s5p driver"
+#define DRIVER_DESC "EHCI EXYNOS driver"
 
 #define EHCI_INSNREG00(base)                   (base + 0x90)
 #define EHCI_INSNREG00_ENA_INCR16              (0x1 << 25)
        (EHCI_INSNREG00_ENA_INCR16 | EHCI_INSNREG00_ENA_INCR8 | \
         EHCI_INSNREG00_ENA_INCR4 | EHCI_INSNREG00_ENA_INCRX_ALIGN)
 
-static const char hcd_name[] = "ehci-s5p";
-static struct hc_driver __read_mostly s5p_ehci_hc_driver;
+static const char hcd_name[] = "ehci-exynos";
+static struct hc_driver __read_mostly exynos_ehci_hc_driver;
 
-struct s5p_ehci_hcd {
+struct exynos_ehci_hcd {
        struct clk *clk;
        struct usb_phy *phy;
        struct usb_otg *otg;
-       struct s5p_ehci_platdata *pdata;
 };
 
-static struct s5p_ehci_platdata empty_platdata;
+#define to_exynos_ehci(hcd) (struct exynos_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
 
-#define to_s5p_ehci(hcd)      (struct s5p_ehci_hcd *)(hcd_to_ehci(hcd)->priv)
-
-static void s5p_setup_vbus_gpio(struct platform_device *pdev)
+static void exynos_setup_vbus_gpio(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        int err;
@@ -73,10 +69,9 @@ static void s5p_setup_vbus_gpio(struct platform_device *pdev)
                dev_err(dev, "can't request ehci vbus gpio %d", gpio);
 }
 
-static int s5p_ehci_probe(struct platform_device *pdev)
+static int exynos_ehci_probe(struct platform_device *pdev)
 {
-       struct s5p_ehci_platdata *pdata = dev_get_platdata(&pdev->dev);
-       struct s5p_ehci_hcd *s5p_ehci;
+       struct exynos_ehci_hcd *exynos_ehci;
        struct usb_hcd *hcd;
        struct ehci_hcd *ehci;
        struct resource *res;
@@ -93,48 +88,41 @@ static int s5p_ehci_probe(struct platform_device *pdev)
        if (err)
                return err;
 
-       s5p_setup_vbus_gpio(pdev);
+       exynos_setup_vbus_gpio(pdev);
 
-       hcd = usb_create_hcd(&s5p_ehci_hc_driver,
+       hcd = usb_create_hcd(&exynos_ehci_hc_driver,
                             &pdev->dev, dev_name(&pdev->dev));
        if (!hcd) {
                dev_err(&pdev->dev, "Unable to create HCD\n");
                return -ENOMEM;
        }
-       s5p_ehci = to_s5p_ehci(hcd);
+       exynos_ehci = to_exynos_ehci(hcd);
 
        if (of_device_is_compatible(pdev->dev.of_node,
-                                       "samsung,exynos5440-ehci")) {
-               s5p_ehci->pdata = &empty_platdata;
+                                       "samsung,exynos5440-ehci"))
                goto skip_phy;
-       }
 
        phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
        if (IS_ERR(phy)) {
-               /* Fallback to pdata */
-               if (!pdata) {
-                       usb_put_hcd(hcd);
-                       dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
-                       return -EPROBE_DEFER;
-               } else {
-                       s5p_ehci->pdata = pdata;
-               }
+               usb_put_hcd(hcd);
+               dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
+               return -EPROBE_DEFER;
        } else {
-               s5p_ehci->phy = phy;
-               s5p_ehci->otg = phy->otg;
+               exynos_ehci->phy = phy;
+               exynos_ehci->otg = phy->otg;
        }
 
 skip_phy:
 
-       s5p_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
+       exynos_ehci->clk = devm_clk_get(&pdev->dev, "usbhost");
 
-       if (IS_ERR(s5p_ehci->clk)) {
+       if (IS_ERR(exynos_ehci->clk)) {
                dev_err(&pdev->dev, "Failed to get usbhost clock\n");
-               err = PTR_ERR(s5p_ehci->clk);
+               err = PTR_ERR(exynos_ehci->clk);
                goto fail_clk;
        }
 
-       err = clk_prepare_enable(s5p_ehci->clk);
+       err = clk_prepare_enable(exynos_ehci->clk);
        if (err)
                goto fail_clk;
 
@@ -161,13 +149,11 @@ skip_phy:
                goto fail_io;
        }
 
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
 
-       if (s5p_ehci->phy)
-               usb_phy_init(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_init)
-               s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
+       if (exynos_ehci->phy)
+               usb_phy_init(exynos_ehci->phy);
 
        ehci = hcd_to_ehci(hcd);
        ehci->caps = hcd->regs;
@@ -186,33 +172,29 @@ skip_phy:
        return 0;
 
 fail_add_hcd:
-       if (s5p_ehci->phy)
-               usb_phy_shutdown(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_exit)
-               s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
+       if (exynos_ehci->phy)
+               usb_phy_shutdown(exynos_ehci->phy);
 fail_io:
-       clk_disable_unprepare(s5p_ehci->clk);
+       clk_disable_unprepare(exynos_ehci->clk);
 fail_clk:
        usb_put_hcd(hcd);
        return err;
 }
 
-static int s5p_ehci_remove(struct platform_device *pdev)
+static int exynos_ehci_remove(struct platform_device *pdev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
-       struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
+       struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
 
        usb_remove_hcd(hcd);
 
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
 
-       if (s5p_ehci->phy)
-               usb_phy_shutdown(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_exit)
-               s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
+       if (exynos_ehci->phy)
+               usb_phy_shutdown(exynos_ehci->phy);
 
-       clk_disable_unprepare(s5p_ehci->clk);
+       clk_disable_unprepare(exynos_ehci->clk);
 
        usb_put_hcd(hcd);
 
@@ -220,45 +202,39 @@ static int s5p_ehci_remove(struct platform_device *pdev)
 }
 
 #ifdef CONFIG_PM
-static int s5p_ehci_suspend(struct device *dev)
+static int exynos_ehci_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
-       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
 
        bool do_wakeup = device_may_wakeup(dev);
        int rc;
 
        rc = ehci_suspend(hcd, do_wakeup);
 
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
 
-       if (s5p_ehci->phy)
-               usb_phy_shutdown(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_exit)
-               s5p_ehci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
+       if (exynos_ehci->phy)
+               usb_phy_shutdown(exynos_ehci->phy);
 
-       clk_disable_unprepare(s5p_ehci->clk);
+       clk_disable_unprepare(exynos_ehci->clk);
 
        return rc;
 }
 
-static int s5p_ehci_resume(struct device *dev)
+static int exynos_ehci_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct  s5p_ehci_hcd *s5p_ehci = to_s5p_ehci(hcd);
-       struct platform_device *pdev = to_platform_device(dev);
+       struct exynos_ehci_hcd *exynos_ehci = to_exynos_ehci(hcd);
 
-       clk_prepare_enable(s5p_ehci->clk);
+       clk_prepare_enable(exynos_ehci->clk);
 
-       if (s5p_ehci->otg)
-               s5p_ehci->otg->set_host(s5p_ehci->otg, &hcd->self);
+       if (exynos_ehci->otg)
+               exynos_ehci->otg->set_host(exynos_ehci->otg, &hcd->self);
 
-       if (s5p_ehci->phy)
-               usb_phy_init(s5p_ehci->phy);
-       else if (s5p_ehci->pdata->phy_init)
-               s5p_ehci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
+       if (exynos_ehci->phy)
+               usb_phy_init(exynos_ehci->phy);
 
        /* DMA burst Enable */
        writel(EHCI_INSNREG00_ENABLE_DMA_BURST, EHCI_INSNREG00(hcd->regs));
@@ -267,13 +243,13 @@ static int s5p_ehci_resume(struct device *dev)
        return 0;
 }
 #else
-#define s5p_ehci_suspend       NULL
-#define s5p_ehci_resume                NULL
+#define exynos_ehci_suspend    NULL
+#define exynos_ehci_resume     NULL
 #endif
 
-static const struct dev_pm_ops s5p_ehci_pm_ops = {
-       .suspend        = s5p_ehci_suspend,
-       .resume         = s5p_ehci_resume,
+static const struct dev_pm_ops exynos_ehci_pm_ops = {
+       .suspend        = exynos_ehci_suspend,
+       .resume         = exynos_ehci_resume,
 };
 
 #ifdef CONFIG_OF
@@ -285,40 +261,40 @@ static const struct of_device_id exynos_ehci_match[] = {
 MODULE_DEVICE_TABLE(of, exynos_ehci_match);
 #endif
 
-static struct platform_driver s5p_ehci_driver = {
-       .probe          = s5p_ehci_probe,
-       .remove         = s5p_ehci_remove,
+static struct platform_driver exynos_ehci_driver = {
+       .probe          = exynos_ehci_probe,
+       .remove         = exynos_ehci_remove,
        .shutdown       = usb_hcd_platform_shutdown,
        .driver = {
-               .name   = "s5p-ehci",
+               .name   = "exynos-ehci",
                .owner  = THIS_MODULE,
-               .pm     = &s5p_ehci_pm_ops,
+               .pm     = &exynos_ehci_pm_ops,
                .of_match_table = of_match_ptr(exynos_ehci_match),
        }
 };
-static const struct ehci_driver_overrides s5p_overrides __initdata = {
-       .extra_priv_size = sizeof(struct s5p_ehci_hcd),
+static const struct ehci_driver_overrides exynos_overrides __initdata = {
+       .extra_priv_size = sizeof(struct exynos_ehci_hcd),
 };
 
-static int __init ehci_s5p_init(void)
+static int __init ehci_exynos_init(void)
 {
        if (usb_disabled())
                return -ENODEV;
 
        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
-       ehci_init_driver(&s5p_ehci_hc_driver, &s5p_overrides);
-       return platform_driver_register(&s5p_ehci_driver);
+       ehci_init_driver(&exynos_ehci_hc_driver, &exynos_overrides);
+       return platform_driver_register(&exynos_ehci_driver);
 }
-module_init(ehci_s5p_init);
+module_init(ehci_exynos_init);
 
-static void __exit ehci_s5p_cleanup(void)
+static void __exit ehci_exynos_cleanup(void)
 {
-       platform_driver_unregister(&s5p_ehci_driver);
+       platform_driver_unregister(&exynos_ehci_driver);
 }
-module_exit(ehci_s5p_cleanup);
+module_exit(ehci_exynos_cleanup);
 
 MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_ALIAS("platform:s5p-ehci");
+MODULE_ALIAS("platform:exynos-ehci");
 MODULE_AUTHOR("Jingoo Han");
 MODULE_AUTHOR("Joonyoung Shim");
 MODULE_LICENSE("GPL v2");
index f2407b2e8a996210aec7f3e7a442119fd6928924..a06d5012201fe6800c0de5c7363bf8bd1cecb58f 100644 (file)
@@ -57,7 +57,7 @@ static int usb_hcd_fsl_probe(const struct hc_driver *driver,
        pr_debug("initializing FSL-SOC USB Controller\n");
 
        /* Need platform data for setup */
-       pdata = (struct fsl_usb2_platform_data *)dev_get_platdata(&pdev->dev);
+       pdata = dev_get_platdata(&pdev->dev);
        if (!pdata) {
                dev_err(&pdev->dev,
                        "No platform data for %s.\n", dev_name(&pdev->dev));
@@ -664,7 +664,7 @@ static const struct hc_driver ehci_fsl_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_USB2 | HCD_MEMORY,
+       .flags = HCD_USB2 | HCD_MEMORY | HCD_BH,
 
        /*
         * basic lifecycle operations
index 83ab51af250f158e735373760f8c008ce6fe0ad8..b52a66ce92e8592b123239aa24b724dddfd085fa 100644 (file)
@@ -43,7 +43,7 @@ static const struct hc_driver ehci_grlib_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 86ab9fd9fe9e938fc29cadeac72a935e8c26b8ff..e8ba4c44223a5c360ec552cbbb4137ca2b4ca3b2 100644 (file)
@@ -110,6 +110,9 @@ MODULE_PARM_DESC (ignore_oc, "ignore bogus hardware overcurrent indications");
 #include "ehci.h"
 #include "pci-quirks.h"
 
+static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
+               struct ehci_tt *tt);
+
 /*
  * The MosChip MCS9990 controller updates its microframe counter
  * a little before the frame counter, and occasionally we will read
@@ -484,6 +487,7 @@ static int ehci_init(struct usb_hcd *hcd)
        INIT_LIST_HEAD(&ehci->intr_qh_list);
        INIT_LIST_HEAD(&ehci->cached_itd_list);
        INIT_LIST_HEAD(&ehci->cached_sitd_list);
+       INIT_LIST_HEAD(&ehci->tt_list);
 
        if (HCC_PGM_FRAMELISTLEN(hcc_params)) {
                /* periodic schedule size can be smaller than default */
@@ -956,6 +960,7 @@ rescan:
                        goto idle_timeout;
 
                /* BUG_ON(!list_empty(&stream->free_list)); */
+               reserve_release_iso_bandwidth(ehci, stream, -1);
                kfree(stream);
                goto done;
        }
@@ -982,6 +987,8 @@ idle_timeout:
                if (qh->clearing_tt)
                        goto idle_timeout;
                if (list_empty (&qh->qtd_list)) {
+                       if (qh->ps.bw_uperiod)
+                               reserve_release_intr_bandwidth(ehci, qh, -1);
                        qh_destroy(ehci, qh);
                        break;
                }
@@ -1022,7 +1029,6 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
         * the toggle bit in the QH.
         */
        if (qh) {
-               usb_settoggle(qh->dev, epnum, is_out, 0);
                if (!list_empty(&qh->qtd_list)) {
                        WARN_ONCE(1, "clear_halt for a busy endpoint\n");
                } else {
@@ -1030,6 +1036,7 @@ ehci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep)
                         * while the QH is active.  Unlink it now;
                         * re-linking will call qh_refresh().
                         */
+                       usb_settoggle(qh->ps.udev, epnum, is_out, 0);
                        qh->exception = 1;
                        if (eptype == USB_ENDPOINT_XFER_BULK)
                                start_unlink_async(ehci, qh);
@@ -1048,6 +1055,19 @@ static int ehci_get_frame (struct usb_hcd *hcd)
 
 /*-------------------------------------------------------------------------*/
 
+/* Device addition and removal */
+
+static void ehci_remove_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+       struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
+
+       spin_lock_irq(&ehci->lock);
+       drop_tt(udev);
+       spin_unlock_irq(&ehci->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
 #ifdef CONFIG_PM
 
 /* suspend/resume, section 4.3 */
@@ -1075,6 +1095,14 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
        spin_unlock_irq(&ehci->lock);
 
+       synchronize_irq(hcd->irq);
+
+       /* Check for race with a wakeup request */
+       if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+               ehci_resume(hcd, false);
+               return -EBUSY;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(ehci_suspend);
@@ -1158,7 +1186,7 @@ static const struct hc_driver ehci_hc_driver = {
         * generic hardware linkage
         */
        .irq =                  ehci_irq,
-       .flags =                HCD_MEMORY | HCD_USB2,
+       .flags =                HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
@@ -1191,6 +1219,11 @@ static const struct hc_driver ehci_hc_driver = {
        .bus_resume =           ehci_bus_resume,
        .relinquish_port =      ehci_relinquish_port,
        .port_handed_over =     ehci_port_handed_over,
+
+       /*
+        * device support
+        */
+       .free_dev =             ehci_remove_device,
 };
 
 void ehci_init_driver(struct hc_driver *drv,
@@ -1238,11 +1271,6 @@ MODULE_LICENSE ("GPL");
 #define XILINX_OF_PLATFORM_DRIVER      ehci_hcd_xilinx_of_driver
 #endif
 
-#ifdef CONFIG_USB_W90X900_EHCI
-#include "ehci-w90x900.c"
-#define        PLATFORM_DRIVER         ehci_hcd_w90x900_driver
-#endif
-
 #ifdef CONFIG_USB_OCTEON_EHCI
 #include "ehci-octeon.c"
 #define PLATFORM_DRIVER                ehci_octeon_driver
index 52a77734a225fa05a8afc6ede8df5deb4a7089dd..c0fb6a8ae6a3935e367e4a9c386e051acc739338 100644 (file)
@@ -224,11 +224,11 @@ static int ehci_mem_init (struct ehci_hcd *ehci, gfp_t flags)
                hw->hw_next = EHCI_LIST_END(ehci);
                hw->hw_qtd_next = EHCI_LIST_END(ehci);
                hw->hw_alt_next = EHCI_LIST_END(ehci);
-               hw->hw_token &= ~QTD_STS_ACTIVE;
                ehci->dummy->hw = hw;
 
                for (i = 0; i < ehci->periodic_size; i++)
-                       ehci->periodic[i] = ehci->dummy->qh_dma;
+                       ehci->periodic[i] = cpu_to_hc32(ehci,
+                                       ehci->dummy->qh_dma);
        } else {
                for (i = 0; i < ehci->periodic_size; i++)
                        ehci->periodic[i] = EHCI_LIST_END(ehci);
index 0f717dc688b7276fb75f65a9920bb799facfb164..f341651d6f6ce6e4ae67a2d53ab2cab3aefeb81e 100644 (file)
@@ -42,7 +42,6 @@
 
 static const char hcd_name[] = "ehci-msm";
 static struct hc_driver __read_mostly msm_hc_driver;
-static struct usb_phy *phy;
 
 static int ehci_msm_reset(struct usb_hcd *hcd)
 {
@@ -70,6 +69,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
 {
        struct usb_hcd *hcd;
        struct resource *res;
+       struct usb_phy *phy;
        int ret;
 
        dev_dbg(&pdev->dev, "ehci_msm proble\n");
@@ -108,10 +108,14 @@ static int ehci_msm_probe(struct platform_device *pdev)
         * powering up VBUS, mapping of registers address space and power
         * management.
         */
-       phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+       if (pdev->dev.of_node)
+               phy = devm_usb_get_phy_by_phandle(&pdev->dev, "usb-phy", 0);
+       else
+               phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
+
        if (IS_ERR(phy)) {
                dev_err(&pdev->dev, "unable to find transceiver\n");
-               ret = -ENODEV;
+               ret = -EPROBE_DEFER;
                goto put_hcd;
        }
 
@@ -121,6 +125,7 @@ static int ehci_msm_probe(struct platform_device *pdev)
                goto put_hcd;
        }
 
+       hcd->phy = phy;
        device_init_wakeup(&pdev->dev, 1);
        /*
         * OTG device parent of HCD takes care of putting
@@ -147,7 +152,7 @@ static int ehci_msm_remove(struct platform_device *pdev)
        pm_runtime_disable(&pdev->dev);
        pm_runtime_set_suspended(&pdev->dev);
 
-       otg_set_host(phy->otg, NULL);
+       otg_set_host(hcd->phy->otg, NULL);
 
        /* FIXME: need to call usb_remove_hcd() here? */
 
@@ -186,12 +191,19 @@ static const struct dev_pm_ops ehci_msm_dev_pm_ops = {
        .resume          = ehci_msm_pm_resume,
 };
 
+static struct of_device_id msm_ehci_dt_match[] = {
+       { .compatible = "qcom,ehci-host", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, msm_ehci_dt_match);
+
 static struct platform_driver ehci_msm_driver = {
        .probe  = ehci_msm_probe,
        .remove = ehci_msm_remove,
        .driver = {
                   .name = "msm_hsusb_host",
                   .pm = &ehci_msm_dev_pm_ops,
+                  .of_match_table = msm_ehci_dt_match,
        },
 };
 
index 35cdbd88bbbef62a93a3aa869c12dbfda8183bf2..417c10da945078e37ddf20e7be290e996936074e 100644 (file)
@@ -96,7 +96,7 @@ static const struct hc_driver mv_ehci_hc_driver = {
         * generic hardware linkage
         */
        .irq = ehci_irq,
-       .flags = HCD_MEMORY | HCD_USB2,
+       .flags = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 323a02b1a0a65778b0e7dd74ad9423e632444402..4c528b2c033ad57c409286b376b9994ba2a2aa67 100644 (file)
@@ -51,7 +51,7 @@ static const struct hc_driver ehci_octeon_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 854c2ec7b699d4effe2c9ca6ae4ab264e1a73cd8..3e86bf4371b3901de76ed7e5db8e8a8de5a72cd3 100644 (file)
@@ -58,8 +58,6 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
 {
        struct ehci_hcd         *ehci = hcd_to_ehci(hcd);
        struct pci_dev          *pdev = to_pci_dev(hcd->self.controller);
-       struct pci_dev          *p_smbus;
-       u8                      rev;
        u32                     temp;
        int                     retval;
 
@@ -175,22 +173,12 @@ static int ehci_pci_setup(struct usb_hcd *hcd)
                /* SB600 and old version of SB700 have a bug in EHCI controller,
                 * which causes usb devices lose response in some cases.
                 */
-               if ((pdev->device == 0x4386) || (pdev->device == 0x4396)) {
-                       p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
-                                                PCI_DEVICE_ID_ATI_SBX00_SMBUS,
-                                                NULL);
-                       if (!p_smbus)
-                               break;
-                       rev = p_smbus->revision;
-                       if ((pdev->device == 0x4386) || (rev == 0x3a)
-                           || (rev == 0x3b)) {
-                               u8 tmp;
-                               ehci_info(ehci, "applying AMD SB600/SB700 USB "
-                                       "freeze workaround\n");
-                               pci_read_config_byte(pdev, 0x53, &tmp);
-                               pci_write_config_byte(pdev, 0x53, tmp | (1<<3));
-                       }
-                       pci_dev_put(p_smbus);
+               if ((pdev->device == 0x4386 || pdev->device == 0x4396) &&
+                               usb_amd_hang_symptom_quirk()) {
+                       u8 tmp;
+                       ehci_info(ehci, "applying AMD SB600/SB700 USB freeze workaround\n");
+                       pci_read_config_byte(pdev, 0x53, &tmp);
+                       pci_write_config_byte(pdev, 0x53, tmp | (1<<3));
                }
                break;
        case PCI_VENDOR_ID_NETMOS:
index 601e208bd782c07e9d0bb1b60d238ccbb7774758..893b707f0000abf0e323f39b28b6060a3293bcef 100644 (file)
@@ -286,7 +286,7 @@ static const struct hc_driver ehci_msp_hc_driver = {
 #else
        .irq =                  ehci_irq,
 #endif
-       .flags =                HCD_MEMORY | HCD_USB2,
+       .flags =                HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 2b1694e6b4809b0fb0af261a140f3677952731a7..875d2fcc9e0e83de6ba7e17d007b347bf1ca34a4 100644 (file)
@@ -30,7 +30,7 @@ static const struct hc_driver ehci_ppc_of_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index fd983771b02559cb56c6210e6813d9b223a80f7a..8188542ba17ea01214a3ab0f269fe07cb6cb1744 100644 (file)
@@ -71,7 +71,7 @@ static const struct hc_driver ps3_ehci_hc_driver = {
        .product_desc           = "PS3 EHCI Host Controller",
        .hcd_priv_size          = sizeof(struct ehci_hcd),
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
        .reset                  = ps3_ehci_hc_reset,
        .start                  = ehci_run,
        .stop                   = ehci_stop,
index a7f776a13eb17133459f23ac584b60287e544197..db05bd8ee9d59e4a739720912ee4ca8faaa61eac 100644 (file)
@@ -105,9 +105,9 @@ qh_update (struct ehci_hcd *ehci, struct ehci_qh *qh, struct ehci_qtd *qtd)
 
                is_out = qh->is_out;
                epnum = (hc32_to_cpup(ehci, &hw->hw_info1) >> 8) & 0x0f;
-               if (unlikely (!usb_gettoggle (qh->dev, epnum, is_out))) {
+               if (unlikely(!usb_gettoggle(qh->ps.udev, epnum, is_out))) {
                        hw->hw_token &= ~cpu_to_hc32(ehci, QTD_TOGGLE);
-                       usb_settoggle (qh->dev, epnum, is_out, 1);
+                       usb_settoggle(qh->ps.udev, epnum, is_out, 1);
                }
        }
 
@@ -247,8 +247,6 @@ static int qtd_copy_status (
 
 static void
 ehci_urb_done(struct ehci_hcd *ehci, struct urb *urb, int status)
-__releases(ehci->lock)
-__acquires(ehci->lock)
 {
        if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
                /* ... update hc-wide periodic stats */
@@ -274,11 +272,8 @@ __acquires(ehci->lock)
                urb->actual_length, urb->transfer_buffer_length);
 #endif
 
-       /* complete() can reenter this HCD */
        usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
-       spin_unlock (&ehci->lock);
        usb_hcd_giveback_urb(ehci_to_hcd(ehci), urb, status);
-       spin_lock (&ehci->lock);
 }
 
 static int qh_schedule (struct ehci_hcd *ehci, struct ehci_qh *qh);
@@ -802,26 +797,35 @@ qh_make (
         * For control/bulk requests, the HC or TT handles these.
         */
        if (type == PIPE_INTERRUPT) {
-               qh->usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
+               unsigned        tmp;
+
+               qh->ps.usecs = NS_TO_US(usb_calc_bus_time(USB_SPEED_HIGH,
                                is_input, 0,
                                hb_mult(maxp) * max_packet(maxp)));
-               qh->start = NO_FRAME;
+               qh->ps.phase = NO_FRAME;
 
                if (urb->dev->speed == USB_SPEED_HIGH) {
-                       qh->c_usecs = 0;
+                       qh->ps.c_usecs = 0;
                        qh->gap_uf = 0;
 
-                       qh->period = urb->interval >> 3;
-                       if (qh->period == 0 && urb->interval != 1) {
+                       if (urb->interval > 1 && urb->interval < 8) {
                                /* NOTE interval 2 or 4 uframes could work.
                                 * But interval 1 scheduling is simpler, and
                                 * includes high bandwidth.
                                 */
                                urb->interval = 1;
-                       } else if (qh->period > ehci->periodic_size) {
-                               qh->period = ehci->periodic_size;
-                               urb->interval = qh->period << 3;
+                       } else if (urb->interval > ehci->periodic_size << 3) {
+                               urb->interval = ehci->periodic_size << 3;
                        }
+                       qh->ps.period = urb->interval >> 3;
+
+                       /* period for bandwidth allocation */
+                       tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE,
+                                       1 << (urb->ep->desc.bInterval - 1));
+
+                       /* Allow urb->interval to override */
+                       qh->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval);
+                       qh->ps.bw_period = qh->ps.bw_uperiod >> 3;
                } else {
                        int             think_time;
 
@@ -831,27 +835,35 @@ qh_make (
 
                        /* FIXME this just approximates SPLIT/CSPLIT times */
                        if (is_input) {         // SPLIT, gap, CSPLIT+DATA
-                               qh->c_usecs = qh->usecs + HS_USECS (0);
-                               qh->usecs = HS_USECS (1);
+                               qh->ps.c_usecs = qh->ps.usecs + HS_USECS(0);
+                               qh->ps.usecs = HS_USECS(1);
                        } else {                // SPLIT+DATA, gap, CSPLIT
-                               qh->usecs += HS_USECS (1);
-                               qh->c_usecs = HS_USECS (0);
+                               qh->ps.usecs += HS_USECS(1);
+                               qh->ps.c_usecs = HS_USECS(0);
                        }
 
                        think_time = tt ? tt->think_time : 0;
-                       qh->tt_usecs = NS_TO_US (think_time +
+                       qh->ps.tt_usecs = NS_TO_US(think_time +
                                        usb_calc_bus_time (urb->dev->speed,
                                        is_input, 0, max_packet (maxp)));
-                       qh->period = urb->interval;
-                       if (qh->period > ehci->periodic_size) {
-                               qh->period = ehci->periodic_size;
-                               urb->interval = qh->period;
-                       }
+                       if (urb->interval > ehci->periodic_size)
+                               urb->interval = ehci->periodic_size;
+                       qh->ps.period = urb->interval;
+
+                       /* period for bandwidth allocation */
+                       tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES,
+                                       urb->ep->desc.bInterval);
+                       tmp = rounddown_pow_of_two(tmp);
+
+                       /* Allow urb->interval to override */
+                       qh->ps.bw_period = min_t(unsigned, tmp, urb->interval);
+                       qh->ps.bw_uperiod = qh->ps.bw_period << 3;
                }
        }
 
        /* support for tt scheduling, and access to toggles */
-       qh->dev = urb->dev;
+       qh->ps.udev = urb->dev;
+       qh->ps.ep = urb->ep;
 
        /* using TT? */
        switch (urb->dev->speed) {
index 85dd24ed97a6d6cf6700405797da29d313e76958..e113fd73aeae7148b0cbcd0d424aebb16d84a694 100644 (file)
@@ -103,83 +103,210 @@ static void periodic_unlink (struct ehci_hcd *ehci, unsigned frame, void *ptr)
                *hw_p = *shadow_next_periodic(ehci, &here,
                                Q_NEXT_TYPE(ehci, *hw_p));
        else
-               *hw_p = ehci->dummy->qh_dma;
+               *hw_p = cpu_to_hc32(ehci, ehci->dummy->qh_dma);
 }
 
-/* how many of the uframe's 125 usecs are allocated? */
-static unsigned short
-periodic_usecs (struct ehci_hcd *ehci, unsigned frame, unsigned uframe)
+/*-------------------------------------------------------------------------*/
+
+/* Bandwidth and TT management */
+
+/* Find the TT data structure for this device; create it if necessary */
+static struct ehci_tt *find_tt(struct usb_device *udev)
 {
-       __hc32                  *hw_p = &ehci->periodic [frame];
-       union ehci_shadow       *q = &ehci->pshadow [frame];
-       unsigned                usecs = 0;
-       struct ehci_qh_hw       *hw;
-
-       while (q->ptr) {
-               switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
-               case Q_TYPE_QH:
-                       hw = q->qh->hw;
-                       /* is it in the S-mask? */
-                       if (hw->hw_info2 & cpu_to_hc32(ehci, 1 << uframe))
-                               usecs += q->qh->usecs;
-                       /* ... or C-mask? */
-                       if (hw->hw_info2 & cpu_to_hc32(ehci,
-                                       1 << (8 + uframe)))
-                               usecs += q->qh->c_usecs;
-                       hw_p = &hw->hw_next;
-                       q = &q->qh->qh_next;
-                       break;
-               // case Q_TYPE_FSTN:
-               default:
-                       /* for "save place" FSTNs, count the relevant INTR
-                        * bandwidth from the previous frame
-                        */
-                       if (q->fstn->hw_prev != EHCI_LIST_END(ehci)) {
-                               ehci_dbg (ehci, "ignoring FSTN cost ...\n");
-                       }
-                       hw_p = &q->fstn->hw_next;
-                       q = &q->fstn->fstn_next;
-                       break;
-               case Q_TYPE_ITD:
-                       if (q->itd->hw_transaction[uframe])
-                               usecs += q->itd->stream->usecs;
-                       hw_p = &q->itd->hw_next;
-                       q = &q->itd->itd_next;
-                       break;
-               case Q_TYPE_SITD:
-                       /* is it in the S-mask?  (count SPLIT, DATA) */
-                       if (q->sitd->hw_uframe & cpu_to_hc32(ehci,
-                                       1 << uframe)) {
-                               if (q->sitd->hw_fullspeed_ep &
-                                               cpu_to_hc32(ehci, 1<<31))
-                                       usecs += q->sitd->stream->usecs;
-                               else    /* worst case for OUT start-split */
-                                       usecs += HS_USECS_ISO (188);
-                       }
+       struct usb_tt           *utt = udev->tt;
+       struct ehci_tt          *tt, **tt_index, **ptt;
+       unsigned                port;
+       bool                    allocated_index = false;
+
+       if (!utt)
+               return NULL;            /* Not below a TT */
+
+       /*
+        * Find/create our data structure.
+        * For hubs with a single TT, we get it directly.
+        * For hubs with multiple TTs, there's an extra level of pointers.
+        */
+       tt_index = NULL;
+       if (utt->multi) {
+               tt_index = utt->hcpriv;
+               if (!tt_index) {                /* Create the index array */
+                       tt_index = kzalloc(utt->hub->maxchild *
+                                       sizeof(*tt_index), GFP_ATOMIC);
+                       if (!tt_index)
+                               return ERR_PTR(-ENOMEM);
+                       utt->hcpriv = tt_index;
+                       allocated_index = true;
+               }
+               port = udev->ttport - 1;
+               ptt = &tt_index[port];
+       } else {
+               port = 0;
+               ptt = (struct ehci_tt **) &utt->hcpriv;
+       }
+
+       tt = *ptt;
+       if (!tt) {                              /* Create the ehci_tt */
+               struct ehci_hcd         *ehci =
+                               hcd_to_ehci(bus_to_hcd(udev->bus));
 
-                       /* ... C-mask?  (count CSPLIT, DATA) */
-                       if (q->sitd->hw_uframe &
-                                       cpu_to_hc32(ehci, 1 << (8 + uframe))) {
-                               /* worst case for IN complete-split */
-                               usecs += q->sitd->stream->c_usecs;
+               tt = kzalloc(sizeof(*tt), GFP_ATOMIC);
+               if (!tt) {
+                       if (allocated_index) {
+                               utt->hcpriv = NULL;
+                               kfree(tt_index);
                        }
+                       return ERR_PTR(-ENOMEM);
+               }
+               list_add_tail(&tt->tt_list, &ehci->tt_list);
+               INIT_LIST_HEAD(&tt->ps_list);
+               tt->usb_tt = utt;
+               tt->tt_port = port;
+               *ptt = tt;
+       }
 
-                       hw_p = &q->sitd->hw_next;
-                       q = &q->sitd->sitd_next;
-                       break;
+       return tt;
+}
+
+/* Release the TT above udev, if it's not in use */
+static void drop_tt(struct usb_device *udev)
+{
+       struct usb_tt           *utt = udev->tt;
+       struct ehci_tt          *tt, **tt_index, **ptt;
+       int                     cnt, i;
+
+       if (!utt || !utt->hcpriv)
+               return;         /* Not below a TT, or never allocated */
+
+       cnt = 0;
+       if (utt->multi) {
+               tt_index = utt->hcpriv;
+               ptt = &tt_index[udev->ttport - 1];
+
+               /* How many entries are left in tt_index? */
+               for (i = 0; i < utt->hub->maxchild; ++i)
+                       cnt += !!tt_index[i];
+       } else {
+               tt_index = NULL;
+               ptt = (struct ehci_tt **) &utt->hcpriv;
+       }
+
+       tt = *ptt;
+       if (!tt || !list_empty(&tt->ps_list))
+               return;         /* never allocated, or still in use */
+
+       list_del(&tt->tt_list);
+       *ptt = NULL;
+       kfree(tt);
+       if (cnt == 1) {
+               utt->hcpriv = NULL;
+               kfree(tt_index);
+       }
+}
+
+static void bandwidth_dbg(struct ehci_hcd *ehci, int sign, char *type,
+               struct ehci_per_sched *ps)
+{
+       dev_dbg(&ps->udev->dev,
+                       "ep %02x: %s %s @ %u+%u (%u.%u+%u) [%u/%u us] mask %04x\n",
+                       ps->ep->desc.bEndpointAddress,
+                       (sign >= 0 ? "reserve" : "release"), type,
+                       (ps->bw_phase << 3) + ps->phase_uf, ps->bw_uperiod,
+                       ps->phase, ps->phase_uf, ps->period,
+                       ps->usecs, ps->c_usecs, ps->cs_mask);
+}
+
+static void reserve_release_intr_bandwidth(struct ehci_hcd *ehci,
+               struct ehci_qh *qh, int sign)
+{
+       unsigned                start_uf;
+       unsigned                i, j, m;
+       int                     usecs = qh->ps.usecs;
+       int                     c_usecs = qh->ps.c_usecs;
+       int                     tt_usecs = qh->ps.tt_usecs;
+       struct ehci_tt          *tt;
+
+       if (qh->ps.phase == NO_FRAME)   /* Bandwidth wasn't reserved */
+               return;
+       start_uf = qh->ps.bw_phase << 3;
+
+       bandwidth_dbg(ehci, sign, "intr", &qh->ps);
+
+       if (sign < 0) {         /* Release bandwidth */
+               usecs = -usecs;
+               c_usecs = -c_usecs;
+               tt_usecs = -tt_usecs;
+       }
+
+       /* Entire transaction (high speed) or start-split (full/low speed) */
+       for (i = start_uf + qh->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE;
+                       i += qh->ps.bw_uperiod)
+               ehci->bandwidth[i] += usecs;
+
+       /* Complete-split (full/low speed) */
+       if (qh->ps.c_usecs) {
+               /* NOTE: adjustments needed for FSTN */
+               for (i = start_uf; i < EHCI_BANDWIDTH_SIZE;
+                               i += qh->ps.bw_uperiod) {
+                       for ((j = 2, m = 1 << (j+8)); j < 8; (++j, m <<= 1)) {
+                               if (qh->ps.cs_mask & m)
+                                       ehci->bandwidth[i+j] += c_usecs;
+                       }
                }
        }
-#if defined(DEBUG) || defined(CONFIG_DYNAMIC_DEBUG)
-       if (usecs > ehci->uframe_periodic_max)
-               ehci_err (ehci, "uframe %d sched overrun: %d usecs\n",
-                       frame * 8 + uframe, usecs);
-#endif
-       return usecs;
+
+       /* FS/LS bus bandwidth */
+       if (tt_usecs) {
+               tt = find_tt(qh->ps.udev);
+               if (sign > 0)
+                       list_add_tail(&qh->ps.ps_list, &tt->ps_list);
+               else
+                       list_del(&qh->ps.ps_list);
+
+               for (i = start_uf >> 3; i < EHCI_BANDWIDTH_FRAMES;
+                               i += qh->ps.bw_period)
+                       tt->bandwidth[i] += tt_usecs;
+       }
 }
 
 /*-------------------------------------------------------------------------*/
 
-static int same_tt (struct usb_device *dev1, struct usb_device *dev2)
+static void compute_tt_budget(u8 budget_table[EHCI_BANDWIDTH_SIZE],
+               struct ehci_tt *tt)
+{
+       struct ehci_per_sched   *ps;
+       unsigned                uframe, uf, x;
+       u8                      *budget_line;
+
+       if (!tt)
+               return;
+       memset(budget_table, 0, EHCI_BANDWIDTH_SIZE);
+
+       /* Add up the contributions from all the endpoints using this TT */
+       list_for_each_entry(ps, &tt->ps_list, ps_list) {
+               for (uframe = ps->bw_phase << 3; uframe < EHCI_BANDWIDTH_SIZE;
+                               uframe += ps->bw_uperiod) {
+                       budget_line = &budget_table[uframe];
+                       x = ps->tt_usecs;
+
+                       /* propagate the time forward */
+                       for (uf = ps->phase_uf; uf < 8; ++uf) {
+                               x += budget_line[uf];
+
+                               /* Each microframe lasts 125 us */
+                               if (x <= 125) {
+                                       budget_line[uf] = x;
+                                       break;
+                               } else {
+                                       budget_line[uf] = 125;
+                                       x -= 125;
+                               }
+                       }
+               }
+       }
+}
+
+static int __maybe_unused same_tt(struct usb_device *dev1,
+               struct usb_device *dev2)
 {
        if (!dev1->tt || !dev2->tt)
                return 0;
@@ -227,68 +354,6 @@ static inline void carryover_tt_bandwidth(unsigned short tt_usecs[8])
        }
 }
 
-/* How many of the tt's periodic downstream 1000 usecs are allocated?
- *
- * While this measures the bandwidth in terms of usecs/uframe,
- * the low/fullspeed bus has no notion of uframes, so any particular
- * low/fullspeed transfer can "carry over" from one uframe to the next,
- * since the TT just performs downstream transfers in sequence.
- *
- * For example two separate 100 usec transfers can start in the same uframe,
- * and the second one would "carry over" 75 usecs into the next uframe.
- */
-static void
-periodic_tt_usecs (
-       struct ehci_hcd *ehci,
-       struct usb_device *dev,
-       unsigned frame,
-       unsigned short tt_usecs[8]
-)
-{
-       __hc32                  *hw_p = &ehci->periodic [frame];
-       union ehci_shadow       *q = &ehci->pshadow [frame];
-       unsigned char           uf;
-
-       memset(tt_usecs, 0, 16);
-
-       while (q->ptr) {
-               switch (hc32_to_cpu(ehci, Q_NEXT_TYPE(ehci, *hw_p))) {
-               case Q_TYPE_ITD:
-                       hw_p = &q->itd->hw_next;
-                       q = &q->itd->itd_next;
-                       continue;
-               case Q_TYPE_QH:
-                       if (same_tt(dev, q->qh->dev)) {
-                               uf = tt_start_uframe(ehci, q->qh->hw->hw_info2);
-                               tt_usecs[uf] += q->qh->tt_usecs;
-                       }
-                       hw_p = &q->qh->hw->hw_next;
-                       q = &q->qh->qh_next;
-                       continue;
-               case Q_TYPE_SITD:
-                       if (same_tt(dev, q->sitd->urb->dev)) {
-                               uf = tt_start_uframe(ehci, q->sitd->hw_uframe);
-                               tt_usecs[uf] += q->sitd->stream->tt_usecs;
-                       }
-                       hw_p = &q->sitd->hw_next;
-                       q = &q->sitd->sitd_next;
-                       continue;
-               // case Q_TYPE_FSTN:
-               default:
-                       ehci_dbg(ehci, "ignoring periodic frame %d FSTN\n",
-                                       frame);
-                       hw_p = &q->fstn->hw_next;
-                       q = &q->fstn->fstn_next;
-               }
-       }
-
-       carryover_tt_bandwidth(tt_usecs);
-
-       if (max_tt_usecs[7] < tt_usecs[7])
-               ehci_err(ehci, "frame %d tt sched overrun: %d usecs\n",
-                       frame, tt_usecs[7] - max_tt_usecs[7]);
-}
-
 /*
  * Return true if the device's tt's downstream bus is available for a
  * periodic transfer of the specified length (usecs), starting at the
@@ -312,20 +377,29 @@ periodic_tt_usecs (
  */
 static int tt_available (
        struct ehci_hcd         *ehci,
-       unsigned                period,
-       struct usb_device       *dev,
+       struct ehci_per_sched   *ps,
+       struct ehci_tt          *tt,
        unsigned                frame,
-       unsigned                uframe,
-       u16                     usecs
+       unsigned                uframe
 )
 {
+       unsigned                period = ps->bw_period;
+       unsigned                usecs = ps->tt_usecs;
+
        if ((period == 0) || (uframe >= 7))     /* error */
                return 0;
 
-       for (; frame < ehci->periodic_size; frame += period) {
-               unsigned short tt_usecs[8];
+       for (frame &= period - 1; frame < EHCI_BANDWIDTH_FRAMES;
+                       frame += period) {
+               unsigned        i, uf;
+               unsigned short  tt_usecs[8];
 
-               periodic_tt_usecs (ehci, dev, frame, tt_usecs);
+               if (tt->bandwidth[frame] + usecs > 900)
+                       return 0;
+
+               uf = frame << 3;
+               for (i = 0; i < 8; (++i, ++uf))
+                       tt_usecs[i] = ehci->tt_budget[uf];
 
                if (max_tt_usecs[uframe] <= tt_usecs[uframe])
                        return 0;
@@ -337,7 +411,7 @@ static int tt_available (
                 */
                if (125 < usecs) {
                        int ufs = (usecs / 125);
-                       int i;
+
                        for (i = uframe; i < (uframe + ufs) && i < 8; i++)
                                if (0 < tt_usecs[i])
                                        return 0;
@@ -391,7 +465,7 @@ static int tt_no_collision (
                                continue;
                        case Q_TYPE_QH:
                                hw = here.qh->hw;
-                               if (same_tt (dev, here.qh->dev)) {
+                               if (same_tt(dev, here.qh->ps.udev)) {
                                        u32             mask;
 
                                        mask = hc32_to_cpu(ehci,
@@ -471,19 +545,19 @@ static void disable_periodic(struct ehci_hcd *ehci)
 static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
        unsigned        i;
-       unsigned        period = qh->period;
+       unsigned        period = qh->ps.period;
 
-       dev_dbg (&qh->dev->dev,
+       dev_dbg(&qh->ps.udev->dev,
                "link qh%d-%04x/%p start %d [%d/%d us]\n",
                period, hc32_to_cpup(ehci, &qh->hw->hw_info2)
                        & (QH_CMASK | QH_SMASK),
-               qh, qh->start, qh->usecs, qh->c_usecs);
+               qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs);
 
        /* high bandwidth, or otherwise every microframe */
        if (period == 0)
                period = 1;
 
-       for (i = qh->start; i < ehci->periodic_size; i += period) {
+       for (i = qh->ps.phase; i < ehci->periodic_size; i += period) {
                union ehci_shadow       *prev = &ehci->pshadow[i];
                __hc32                  *hw_p = &ehci->periodic[i];
                union ehci_shadow       here = *prev;
@@ -503,7 +577,7 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
                 * enables sharing interior tree nodes
                 */
                while (here.ptr && qh != here.qh) {
-                       if (qh->period > here.qh->period)
+                       if (qh->ps.period > here.qh->ps.period)
                                break;
                        prev = &here.qh->qh_next;
                        hw_p = &here.qh->hw->hw_next;
@@ -523,10 +597,10 @@ static void qh_link_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
        qh->xacterrs = 0;
        qh->exception = 0;
 
-       /* update per-qh bandwidth for usbfs */
-       ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->period
-               ? ((qh->usecs + qh->c_usecs) / qh->period)
-               : (qh->usecs * 8);
+       /* update per-qh bandwidth for debugfs */
+       ehci_to_hcd(ehci)->self.bandwidth_allocated += qh->ps.bw_period
+               ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period)
+               : (qh->ps.usecs * 8);
 
        list_add(&qh->intr_node, &ehci->intr_qh_list);
 
@@ -556,22 +630,21 @@ static void qh_unlink_periodic(struct ehci_hcd *ehci, struct ehci_qh *qh)
         */
 
        /* high bandwidth, or otherwise part of every microframe */
-       if ((period = qh->period) == 0)
-               period = 1;
+       period = qh->ps.period ? : 1;
 
-       for (i = qh->start; i < ehci->periodic_size; i += period)
+       for (i = qh->ps.phase; i < ehci->periodic_size; i += period)
                periodic_unlink (ehci, i, qh);
 
-       /* update per-qh bandwidth for usbfs */
-       ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->period
-               ? ((qh->usecs + qh->c_usecs) / qh->period)
-               : (qh->usecs * 8);
+       /* update per-qh bandwidth for debugfs */
+       ehci_to_hcd(ehci)->self.bandwidth_allocated -= qh->ps.bw_period
+               ? ((qh->ps.usecs + qh->ps.c_usecs) / qh->ps.bw_period)
+               : (qh->ps.usecs * 8);
 
-       dev_dbg (&qh->dev->dev,
+       dev_dbg(&qh->ps.udev->dev,
                "unlink qh%d-%04x/%p start %d [%d/%d us]\n",
-               qh->period,
+               qh->ps.period,
                hc32_to_cpup(ehci, &qh->hw->hw_info2) & (QH_CMASK | QH_SMASK),
-               qh, qh->start, qh->usecs, qh->c_usecs);
+               qh, qh->ps.phase, qh->ps.usecs, qh->ps.c_usecs);
 
        /* qh->qh_next still "live" to HC */
        qh->qh_state = QH_STATE_UNLINK;
@@ -694,11 +767,9 @@ static int check_period (
        struct ehci_hcd *ehci,
        unsigned        frame,
        unsigned        uframe,
-       unsigned        period,
+       unsigned        uperiod,
        unsigned        usecs
 ) {
-       int             claimed;
-
        /* complete split running into next frame?
         * given FSTN support, we could sometimes check...
         */
@@ -708,25 +779,10 @@ static int check_period (
        /* convert "usecs we need" to "max already claimed" */
        usecs = ehci->uframe_periodic_max - usecs;
 
-       /* we "know" 2 and 4 uframe intervals were rejected; so
-        * for period 0, check _every_ microframe in the schedule.
-        */
-       if (unlikely (period == 0)) {
-               do {
-                       for (uframe = 0; uframe < 7; uframe++) {
-                               claimed = periodic_usecs (ehci, frame, uframe);
-                               if (claimed > usecs)
-                                       return 0;
-                       }
-               } while ((frame += 1) < ehci->periodic_size);
-
-       /* just check the specified uframe, at that period */
-       } else {
-               do {
-                       claimed = periodic_usecs (ehci, frame, uframe);
-                       if (claimed > usecs)
-                               return 0;
-               } while ((frame += period) < ehci->periodic_size);
+       for (uframe += frame << 3; uframe < EHCI_BANDWIDTH_SIZE;
+                       uframe += uperiod) {
+               if (ehci->bandwidth[uframe] > usecs)
+                       return 0;
        }
 
        // success!
@@ -737,40 +793,40 @@ static int check_intr_schedule (
        struct ehci_hcd         *ehci,
        unsigned                frame,
        unsigned                uframe,
-       const struct ehci_qh    *qh,
-       __hc32                  *c_maskp
+       struct ehci_qh          *qh,
+       unsigned                *c_maskp,
+       struct ehci_tt          *tt
 )
 {
        int             retval = -ENOSPC;
        u8              mask = 0;
 
-       if (qh->c_usecs && uframe >= 6)         /* FSTN territory? */
+       if (qh->ps.c_usecs && uframe >= 6)      /* FSTN territory? */
                goto done;
 
-       if (!check_period (ehci, frame, uframe, qh->period, qh->usecs))
+       if (!check_period(ehci, frame, uframe, qh->ps.bw_uperiod, qh->ps.usecs))
                goto done;
-       if (!qh->c_usecs) {
+       if (!qh->ps.c_usecs) {
                retval = 0;
                *c_maskp = 0;
                goto done;
        }
 
 #ifdef CONFIG_USB_EHCI_TT_NEWSCHED
-       if (tt_available (ehci, qh->period, qh->dev, frame, uframe,
-                               qh->tt_usecs)) {
+       if (tt_available(ehci, &qh->ps, tt, frame, uframe)) {
                unsigned i;
 
                /* TODO : this may need FSTN for SSPLIT in uframe 5. */
-               for (i=uframe+1; i<8 && i<uframe+4; i++)
-                       if (!check_period (ehci, frame, i,
-                                               qh->period, qh->c_usecs))
+               for (i = uframe+2; i < 8 && i <= uframe+4; i++)
+                       if (!check_period(ehci, frame, i,
+                                       qh->ps.bw_uperiod, qh->ps.c_usecs))
                                goto done;
                        else
                                mask |= 1 << i;
 
                retval = 0;
 
-               *c_maskp = cpu_to_hc32(ehci, mask << 8);
+               *c_maskp = mask;
        }
 #else
        /* Make sure this tt's buffer is also available for CSPLITs.
@@ -781,15 +837,15 @@ static int check_intr_schedule (
         * one smart pass...
         */
        mask = 0x03 << (uframe + qh->gap_uf);
-       *c_maskp = cpu_to_hc32(ehci, mask << 8);
+       *c_maskp = mask;
 
        mask |= 1 << uframe;
-       if (tt_no_collision (ehci, qh->period, qh->dev, frame, mask)) {
-               if (!check_period (ehci, frame, uframe + qh->gap_uf + 1,
-                                       qh->period, qh->c_usecs))
+       if (tt_no_collision(ehci, qh->ps.bw_period, qh->ps.udev, frame, mask)) {
+               if (!check_period(ehci, frame, uframe + qh->gap_uf + 1,
+                               qh->ps.bw_uperiod, qh->ps.c_usecs))
                        goto done;
-               if (!check_period (ehci, frame, uframe + qh->gap_uf,
-                                       qh->period, qh->c_usecs))
+               if (!check_period(ehci, frame, uframe + qh->gap_uf,
+                               qh->ps.bw_uperiod, qh->ps.c_usecs))
                        goto done;
                retval = 0;
        }
@@ -803,62 +859,67 @@ done:
  */
 static int qh_schedule(struct ehci_hcd *ehci, struct ehci_qh *qh)
 {
-       int             status;
+       int             status = 0;
        unsigned        uframe;
-       __hc32          c_mask;
-       unsigned        frame;          /* 0..(qh->period - 1), or NO_FRAME */
+       unsigned        c_mask;
        struct ehci_qh_hw       *hw = qh->hw;
+       struct ehci_tt          *tt;
 
        hw->hw_next = EHCI_LIST_END(ehci);
-       frame = qh->start;
 
        /* reuse the previous schedule slots, if we can */
-       if (frame < qh->period) {
-               uframe = ffs(hc32_to_cpup(ehci, &hw->hw_info2) & QH_SMASK);
-               status = check_intr_schedule (ehci, frame, --uframe,
-                               qh, &c_mask);
-       } else {
-               uframe = 0;
-               c_mask = 0;
-               status = -ENOSPC;
+       if (qh->ps.phase != NO_FRAME) {
+               ehci_dbg(ehci, "reused qh %p schedule\n", qh);
+               return 0;
+       }
+
+       uframe = 0;
+       c_mask = 0;
+       tt = find_tt(qh->ps.udev);
+       if (IS_ERR(tt)) {
+               status = PTR_ERR(tt);
+               goto done;
        }
+       compute_tt_budget(ehci->tt_budget, tt);
 
        /* else scan the schedule to find a group of slots such that all
         * uframes have enough periodic bandwidth available.
         */
-       if (status) {
-               /* "normal" case, uframing flexible except with splits */
-               if (qh->period) {
-                       int             i;
-
-                       for (i = qh->period; status && i > 0; --i) {
-                               frame = ++ehci->random_frame % qh->period;
-                               for (uframe = 0; uframe < 8; uframe++) {
-                                       status = check_intr_schedule (ehci,
-                                                       frame, uframe, qh,
-                                                       &c_mask);
-                                       if (status == 0)
-                                               break;
-                               }
+       /* "normal" case, uframing flexible except with splits */
+       if (qh->ps.bw_period) {
+               int             i;
+               unsigned        frame;
+
+               for (i = qh->ps.bw_period; i > 0; --i) {
+                       frame = ++ehci->random_frame & (qh->ps.bw_period - 1);
+                       for (uframe = 0; uframe < 8; uframe++) {
+                               status = check_intr_schedule(ehci,
+                                               frame, uframe, qh, &c_mask, tt);
+                               if (status == 0)
+                                       goto got_it;
                        }
-
-               /* qh->period == 0 means every uframe */
-               } else {
-                       frame = 0;
-                       status = check_intr_schedule (ehci, 0, 0, qh, &c_mask);
                }
-               if (status)
-                       goto done;
-               qh->start = frame;
 
-               /* reset S-frame and (maybe) C-frame masks */
-               hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
-               hw->hw_info2 |= qh->period
-                       ? cpu_to_hc32(ehci, 1 << uframe)
-                       : cpu_to_hc32(ehci, QH_SMASK);
-               hw->hw_info2 |= c_mask;
-       } else
-               ehci_dbg (ehci, "reused qh %p schedule\n", qh);
+       /* qh->ps.bw_period == 0 means every uframe */
+       } else {
+               status = check_intr_schedule(ehci, 0, 0, qh, &c_mask, tt);
+       }
+       if (status)
+               goto done;
+
+ got_it:
+       qh->ps.phase = (qh->ps.period ? ehci->random_frame &
+                       (qh->ps.period - 1) : 0);
+       qh->ps.bw_phase = qh->ps.phase & (qh->ps.bw_period - 1);
+       qh->ps.phase_uf = uframe;
+       qh->ps.cs_mask = qh->ps.period ?
+                       (c_mask << 8) | (1 << uframe) :
+                       QH_SMASK;
+
+       /* reset S-frame and (maybe) C-frame masks */
+       hw->hw_info2 &= cpu_to_hc32(ehci, ~(QH_CMASK | QH_SMASK));
+       hw->hw_info2 |= cpu_to_hc32(ehci, qh->ps.cs_mask);
+       reserve_release_intr_bandwidth(ehci, qh, 1);
 
 done:
        return status;
@@ -969,7 +1030,8 @@ iso_stream_alloc (gfp_t mem_flags)
        if (likely (stream != NULL)) {
                INIT_LIST_HEAD(&stream->td_list);
                INIT_LIST_HEAD(&stream->free_list);
-               stream->next_uframe = -1;
+               stream->next_uframe = NO_FRAME;
+               stream->ps.phase = NO_FRAME;
        }
        return stream;
 }
@@ -978,25 +1040,24 @@ static void
 iso_stream_init (
        struct ehci_hcd         *ehci,
        struct ehci_iso_stream  *stream,
-       struct usb_device       *dev,
-       int                     pipe,
-       unsigned                interval
+       struct urb              *urb
 )
 {
        static const u8 smask_out [] = { 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f };
 
+       struct usb_device       *dev = urb->dev;
        u32                     buf1;
        unsigned                epnum, maxp;
        int                     is_input;
-       long                    bandwidth;
+       unsigned                tmp;
 
        /*
         * this might be a "high bandwidth" highspeed endpoint,
         * as encoded in the ep descriptor's wMaxPacket field
         */
-       epnum = usb_pipeendpoint (pipe);
-       is_input = usb_pipein (pipe) ? USB_DIR_IN : 0;
-       maxp = usb_maxpacket(dev, pipe, !is_input);
+       epnum = usb_pipeendpoint(urb->pipe);
+       is_input = usb_pipein(urb->pipe) ? USB_DIR_IN : 0;
+       maxp = usb_endpoint_maxp(&urb->ep->desc);
        if (is_input) {
                buf1 = (1 << 11);
        } else {
@@ -1020,9 +1081,19 @@ iso_stream_init (
                /* usbfs wants to report the average usecs per frame tied up
                 * when transfers on this endpoint are scheduled ...
                 */
-               stream->usecs = HS_USECS_ISO (maxp);
-               bandwidth = stream->usecs * 8;
-               bandwidth /= interval;
+               stream->ps.usecs = HS_USECS_ISO(maxp);
+
+               /* period for bandwidth allocation */
+               tmp = min_t(unsigned, EHCI_BANDWIDTH_SIZE,
+                               1 << (urb->ep->desc.bInterval - 1));
+
+               /* Allow urb->interval to override */
+               stream->ps.bw_uperiod = min_t(unsigned, tmp, urb->interval);
+
+               stream->uperiod = urb->interval;
+               stream->ps.period = urb->interval >> 3;
+               stream->bandwidth = stream->ps.usecs * 8 /
+                               stream->ps.bw_uperiod;
 
        } else {
                u32             addr;
@@ -1036,36 +1107,46 @@ iso_stream_init (
                        addr |= dev->tt->hub->devnum << 16;
                addr |= epnum << 8;
                addr |= dev->devnum;
-               stream->usecs = HS_USECS_ISO (maxp);
+               stream->ps.usecs = HS_USECS_ISO(maxp);
                think_time = dev->tt ? dev->tt->think_time : 0;
-               stream->tt_usecs = NS_TO_US (think_time + usb_calc_bus_time (
+               stream->ps.tt_usecs = NS_TO_US(think_time + usb_calc_bus_time(
                                dev->speed, is_input, 1, maxp));
                hs_transfers = max (1u, (maxp + 187) / 188);
                if (is_input) {
                        u32     tmp;
 
                        addr |= 1 << 31;
-                       stream->c_usecs = stream->usecs;
-                       stream->usecs = HS_USECS_ISO (1);
-                       stream->raw_mask = 1;
+                       stream->ps.c_usecs = stream->ps.usecs;
+                       stream->ps.usecs = HS_USECS_ISO(1);
+                       stream->ps.cs_mask = 1;
 
                        /* c-mask as specified in USB 2.0 11.18.4 3.c */
                        tmp = (1 << (hs_transfers + 2)) - 1;
-                       stream->raw_mask |= tmp << (8 + 2);
+                       stream->ps.cs_mask |= tmp << (8 + 2);
                } else
-                       stream->raw_mask = smask_out [hs_transfers - 1];
-               bandwidth = stream->usecs + stream->c_usecs;
-               bandwidth /= interval << 3;
+                       stream->ps.cs_mask = smask_out[hs_transfers - 1];
+
+               /* period for bandwidth allocation */
+               tmp = min_t(unsigned, EHCI_BANDWIDTH_FRAMES,
+                               1 << (urb->ep->desc.bInterval - 1));
+
+               /* Allow urb->interval to override */
+               stream->ps.bw_period = min_t(unsigned, tmp, urb->interval);
+               stream->ps.bw_uperiod = stream->ps.bw_period << 3;
 
-               /* stream->splits gets created from raw_mask later */
+               stream->ps.period = urb->interval;
+               stream->uperiod = urb->interval << 3;
+               stream->bandwidth = (stream->ps.usecs + stream->ps.c_usecs) /
+                               stream->ps.bw_period;
+
+               /* stream->splits gets created from cs_mask later */
                stream->address = cpu_to_hc32(ehci, addr);
        }
-       stream->bandwidth = bandwidth;
 
-       stream->udev = dev;
+       stream->ps.udev = dev;
+       stream->ps.ep = urb->ep;
 
        stream->bEndpointAddress = is_input | epnum;
-       stream->interval = interval;
        stream->maxp = maxp;
 }
 
@@ -1090,9 +1171,7 @@ iso_stream_find (struct ehci_hcd *ehci, struct urb *urb)
                stream = iso_stream_alloc(GFP_ATOMIC);
                if (likely (stream != NULL)) {
                        ep->hcpriv = stream;
-                       stream->ep = ep;
-                       iso_stream_init(ehci, stream, urb->dev, urb->pipe,
-                                       urb->interval);
+                       iso_stream_init(ehci, stream, urb);
                }
 
        /* if dev->ep [epnum] is a QH, hw is set */
@@ -1137,7 +1216,7 @@ itd_sched_init(
        dma_addr_t      dma = urb->transfer_dma;
 
        /* how many uframes are needed for these transfers */
-       iso_sched->span = urb->number_of_packets * stream->interval;
+       iso_sched->span = urb->number_of_packets * stream->uperiod;
 
        /* figure out per-uframe itd fields that we'll need later
         * when we fit new itds into the schedule.
@@ -1236,7 +1315,7 @@ itd_urb_transaction (
 
                memset (itd, 0, sizeof *itd);
                itd->itd_dma = itd_dma;
-               itd->frame = 9999;              /* an invalid value */
+               itd->frame = NO_FRAME;
                list_add (&itd->itd_list, &sched->td_list);
        }
        spin_unlock_irqrestore (&ehci->lock, flags);
@@ -1249,49 +1328,106 @@ itd_urb_transaction (
 
 /*-------------------------------------------------------------------------*/
 
+static void reserve_release_iso_bandwidth(struct ehci_hcd *ehci,
+               struct ehci_iso_stream *stream, int sign)
+{
+       unsigned                uframe;
+       unsigned                i, j;
+       unsigned                s_mask, c_mask, m;
+       int                     usecs = stream->ps.usecs;
+       int                     c_usecs = stream->ps.c_usecs;
+       int                     tt_usecs = stream->ps.tt_usecs;
+       struct ehci_tt          *tt;
+
+       if (stream->ps.phase == NO_FRAME)       /* Bandwidth wasn't reserved */
+               return;
+       uframe = stream->ps.bw_phase << 3;
+
+       bandwidth_dbg(ehci, sign, "iso", &stream->ps);
+
+       if (sign < 0) {         /* Release bandwidth */
+               usecs = -usecs;
+               c_usecs = -c_usecs;
+               tt_usecs = -tt_usecs;
+       }
+
+       if (!stream->splits) {          /* High speed */
+               for (i = uframe + stream->ps.phase_uf; i < EHCI_BANDWIDTH_SIZE;
+                               i += stream->ps.bw_uperiod)
+                       ehci->bandwidth[i] += usecs;
+
+       } else {                        /* Full speed */
+               s_mask = stream->ps.cs_mask;
+               c_mask = s_mask >> 8;
+
+               /* NOTE: adjustment needed for frame overflow */
+               for (i = uframe; i < EHCI_BANDWIDTH_SIZE;
+                               i += stream->ps.bw_uperiod) {
+                       for ((j = stream->ps.phase_uf, m = 1 << j); j < 8;
+                                       (++j, m <<= 1)) {
+                               if (s_mask & m)
+                                       ehci->bandwidth[i+j] += usecs;
+                               else if (c_mask & m)
+                                       ehci->bandwidth[i+j] += c_usecs;
+                       }
+               }
+
+               tt = find_tt(stream->ps.udev);
+               if (sign > 0)
+                       list_add_tail(&stream->ps.ps_list, &tt->ps_list);
+               else
+                       list_del(&stream->ps.ps_list);
+
+               for (i = uframe >> 3; i < EHCI_BANDWIDTH_FRAMES;
+                               i += stream->ps.bw_period)
+                       tt->bandwidth[i] += tt_usecs;
+       }
+}
+
 static inline int
 itd_slot_ok (
        struct ehci_hcd         *ehci,
-       u32                     mod,
-       u32                     uframe,
-       u8                      usecs,
-       u32                     period
+       struct ehci_iso_stream  *stream,
+       unsigned                uframe
 )
 {
-       uframe %= period;
-       do {
-               /* can't commit more than uframe_periodic_max usec */
-               if (periodic_usecs (ehci, uframe >> 3, uframe & 0x7)
-                               > (ehci->uframe_periodic_max - usecs))
-                       return 0;
+       unsigned                usecs;
+
+       /* convert "usecs we need" to "max already claimed" */
+       usecs = ehci->uframe_periodic_max - stream->ps.usecs;
 
-               /* we know urb->interval is 2^N uframes */
-               uframe += period;
-       } while (uframe < mod);
+       for (uframe &= stream->ps.bw_uperiod - 1; uframe < EHCI_BANDWIDTH_SIZE;
+                       uframe += stream->ps.bw_uperiod) {
+               if (ehci->bandwidth[uframe] > usecs)
+                       return 0;
+       }
        return 1;
 }
 
 static inline int
 sitd_slot_ok (
        struct ehci_hcd         *ehci,
-       u32                     mod,
        struct ehci_iso_stream  *stream,
-       u32                     uframe,
+       unsigned                uframe,
        struct ehci_iso_sched   *sched,
-       u32                     period_uframes
+       struct ehci_tt          *tt
 )
 {
-       u32                     mask, tmp;
-       u32                     frame, uf;
+       unsigned                mask, tmp;
+       unsigned                frame, uf;
+
+       mask = stream->ps.cs_mask << (uframe & 7);
 
-       mask = stream->raw_mask << (uframe & 7);
+       /* for OUT, don't wrap SSPLIT into H-microframe 7 */
+       if (((stream->ps.cs_mask & 0xff) << (uframe & 7)) >= (1 << 7))
+               return 0;
 
        /* for IN, don't wrap CSPLIT into the next frame */
        if (mask & ~0xffff)
                return 0;
 
        /* check bandwidth */
-       uframe %= period_uframes;
+       uframe &= stream->ps.bw_uperiod - 1;
        frame = uframe >> 3;
 
 #ifdef CONFIG_USB_EHCI_TT_NEWSCHED
@@ -1299,54 +1435,48 @@ sitd_slot_ok (
         * tt_available scheduling guarantees 10+% for control/bulk.
         */
        uf = uframe & 7;
-       if (!tt_available(ehci, period_uframes >> 3,
-                       stream->udev, frame, uf, stream->tt_usecs))
+       if (!tt_available(ehci, &stream->ps, tt, frame, uf))
                return 0;
 #else
        /* tt must be idle for start(s), any gap, and csplit.
         * assume scheduling slop leaves 10+% for control/bulk.
         */
-       if (!tt_no_collision(ehci, period_uframes >> 3,
-                       stream->udev, frame, mask))
+       if (!tt_no_collision(ehci, stream->ps.bw_period,
+                       stream->ps.udev, frame, mask))
                return 0;
 #endif
 
-       /* this multi-pass logic is simple, but performance may
-        * suffer when the schedule data isn't cached.
-        */
        do {
-               u32             max_used;
-
-               frame = uframe >> 3;
-               uf = uframe & 7;
+               unsigned        max_used;
+               unsigned        i;
 
                /* check starts (OUT uses more than one) */
-               max_used = ehci->uframe_periodic_max - stream->usecs;
-               for (tmp = stream->raw_mask & 0xff; tmp; tmp >>= 1, uf++) {
-                       if (periodic_usecs (ehci, frame, uf) > max_used)
+               uf = uframe;
+               max_used = ehci->uframe_periodic_max - stream->ps.usecs;
+               for (tmp = stream->ps.cs_mask & 0xff; tmp; tmp >>= 1, uf++) {
+                       if (ehci->bandwidth[uf] > max_used)
                                return 0;
                }
 
                /* for IN, check CSPLIT */
-               if (stream->c_usecs) {
-                       uf = uframe & 7;
-                       max_used = ehci->uframe_periodic_max - stream->c_usecs;
-                       do {
-                               tmp = 1 << uf;
-                               tmp <<= 8;
-                               if ((stream->raw_mask & tmp) == 0)
+               if (stream->ps.c_usecs) {
+                       max_used = ehci->uframe_periodic_max -
+                                       stream->ps.c_usecs;
+                       uf = uframe & ~7;
+                       tmp = 1 << (2+8);
+                       for (i = (uframe & 7) + 2; i < 8; (++i, tmp <<= 1)) {
+                               if ((stream->ps.cs_mask & tmp) == 0)
                                        continue;
-                               if (periodic_usecs (ehci, frame, uf)
-                                               > max_used)
+                               if (ehci->bandwidth[uf+i] > max_used)
                                        return 0;
-                       } while (++uf < 8);
+                       }
                }
 
-               /* we know urb->interval is 2^N uframes */
-               uframe += period_uframes;
-       } while (uframe < mod);
+               uframe += stream->ps.bw_uperiod;
+       } while (uframe < EHCI_BANDWIDTH_SIZE);
 
-       stream->splits = cpu_to_hc32(ehci, stream->raw_mask << (uframe & 7));
+       stream->ps.cs_mask <<= uframe & 7;
+       stream->splits = cpu_to_hc32(ehci, stream->ps.cs_mask);
        return 1;
 }
 
@@ -1361,8 +1491,6 @@ sitd_slot_ok (
  * given EHCI_TUNE_FLS and the slop).  Or, write a smarter scheduler!
  */
 
-#define SCHEDULING_DELAY       40      /* microframes */
-
 static int
 iso_stream_schedule (
        struct ehci_hcd         *ehci,
@@ -1370,134 +1498,184 @@ iso_stream_schedule (
        struct ehci_iso_stream  *stream
 )
 {
-       u32                     now, base, next, start, period, span;
-       int                     status;
+       u32                     now, base, next, start, period, span, now2;
+       u32                     wrap = 0, skip = 0;
+       int                     status = 0;
        unsigned                mod = ehci->periodic_size << 3;
        struct ehci_iso_sched   *sched = urb->hcpriv;
+       bool                    empty = list_empty(&stream->td_list);
+       bool                    new_stream = false;
 
-       period = urb->interval;
+       period = stream->uperiod;
        span = sched->span;
-       if (!stream->highspeed) {
-               period <<= 3;
+       if (!stream->highspeed)
                span <<= 3;
-       }
 
-       now = ehci_read_frame_index(ehci) & (mod - 1);
+       /* Start a new isochronous stream? */
+       if (unlikely(empty && !hcd_periodic_completion_in_progress(
+                       ehci_to_hcd(ehci), urb->ep))) {
 
-       /* Typical case: reuse current schedule, stream is still active.
-        * Hopefully there are no gaps from the host falling behind
-        * (irq delays etc).  If there are, the behavior depends on
-        * whether URB_ISO_ASAP is set.
-        */
-       if (likely (!list_empty (&stream->td_list))) {
+               /* Schedule the endpoint */
+               if (stream->ps.phase == NO_FRAME) {
+                       int             done = 0;
+                       struct ehci_tt  *tt = find_tt(stream->ps.udev);
 
-               /* Take the isochronous scheduling threshold into account */
-               if (ehci->i_thresh)
-                       next = now + ehci->i_thresh;    /* uframe cache */
-               else
-                       next = (now + 2 + 7) & ~0x07;   /* full frame cache */
-
-               /*
-                * Use ehci->last_iso_frame as the base.  There can't be any
-                * TDs scheduled for earlier than that.
-                */
-               base = ehci->last_iso_frame << 3;
-               next = (next - base) & (mod - 1);
-               start = (stream->next_uframe - base) & (mod - 1);
-
-               /* Is the schedule already full? */
-               if (unlikely(start < period)) {
-                       ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n",
-                                       urb, stream->next_uframe, base,
-                                       period, mod);
-                       status = -ENOSPC;
-                       goto fail;
-               }
-
-               /* Behind the scheduling threshold? */
-               if (unlikely(start < next)) {
-                       unsigned now2 = (now - base) & (mod - 1);
+                       if (IS_ERR(tt)) {
+                               status = PTR_ERR(tt);
+                               goto fail;
+                       }
+                       compute_tt_budget(ehci->tt_budget, tt);
 
-                       /* USB_ISO_ASAP: Round up to the first available slot */
-                       if (urb->transfer_flags & URB_ISO_ASAP)
-                               start += (next - start + period - 1) & -period;
+                       start = ((-(++ehci->random_frame)) << 3) & (period - 1);
 
-                       /*
-                        * Not ASAP: Use the next slot in the stream,
-                        * no matter what.
+                       /* find a uframe slot with enough bandwidth.
+                        * Early uframes are more precious because full-speed
+                        * iso IN transfers can't use late uframes,
+                        * and therefore they should be allocated last.
                         */
-                       else if (start + span - period < now2) {
-                               ehci_dbg(ehci, "iso underrun %p (%u+%u < %u)\n",
-                                               urb, start + base,
-                                               span - period, now2 + base);
+                       next = start;
+                       start += period;
+                       do {
+                               start--;
+                               /* check schedule: enough space? */
+                               if (stream->highspeed) {
+                                       if (itd_slot_ok(ehci, stream, start))
+                                               done = 1;
+                               } else {
+                                       if ((start % 8) >= 6)
+                                               continue;
+                                       if (sitd_slot_ok(ehci, stream, start,
+                                                       sched, tt))
+                                               done = 1;
+                               }
+                       } while (start > next && !done);
+
+                       /* no room in the schedule */
+                       if (!done) {
+                               ehci_dbg(ehci, "iso sched full %p", urb);
+                               status = -ENOSPC;
+                               goto fail;
                        }
+                       stream->ps.phase = (start >> 3) &
+                                       (stream->ps.period - 1);
+                       stream->ps.bw_phase = stream->ps.phase &
+                                       (stream->ps.bw_period - 1);
+                       stream->ps.phase_uf = start & 7;
+                       reserve_release_iso_bandwidth(ehci, stream, 1);
+               }
+
+               /* New stream is already scheduled; use the upcoming slot */
+               else {
+                       start = (stream->ps.phase << 3) + stream->ps.phase_uf;
                }
 
-               start += base;
+               stream->next_uframe = start;
+               new_stream = true;
        }
 
-       /* need to schedule; when's the next (u)frame we could start?
-        * this is bigger than ehci->i_thresh allows; scheduling itself
-        * isn't free, the delay should handle reasonably slow cpus.  it
-        * can also help high bandwidth if the dma and irq loads don't
-        * jump until after the queue is primed.
+       now = ehci_read_frame_index(ehci) & (mod - 1);
+
+       /* Take the isochronous scheduling threshold into account */
+       if (ehci->i_thresh)
+               next = now + ehci->i_thresh;    /* uframe cache */
+       else
+               next = (now + 2 + 7) & ~0x07;   /* full frame cache */
+
+       /*
+        * Use ehci->last_iso_frame as the base.  There can't be any
+        * TDs scheduled for earlier than that.
         */
-       else {
-               int done = 0;
+       base = ehci->last_iso_frame << 3;
+       next = (next - base) & (mod - 1);
+       start = (stream->next_uframe - base) & (mod - 1);
 
-               base = now & ~0x07;
-               start = base + SCHEDULING_DELAY;
+       if (unlikely(new_stream))
+               goto do_ASAP;
 
-               /* find a uframe slot with enough bandwidth.
-                * Early uframes are more precious because full-speed
-                * iso IN transfers can't use late uframes,
-                * and therefore they should be allocated last.
-                */
-               next = start;
-               start += period;
-               do {
-                       start--;
-                       /* check schedule: enough space? */
-                       if (stream->highspeed) {
-                               if (itd_slot_ok(ehci, mod, start,
-                                               stream->usecs, period))
-                                       done = 1;
-                       } else {
-                               if ((start % 8) >= 6)
-                                       continue;
-                               if (sitd_slot_ok(ehci, mod, stream,
-                                               start, sched, period))
-                                       done = 1;
-                       }
-               } while (start > next && !done);
+       /*
+        * Typical case: reuse current schedule, stream may still be active.
+        * Hopefully there are no gaps from the host falling behind
+        * (irq delays etc).  If there are, the behavior depends on
+        * whether URB_ISO_ASAP is set.
+        */
+       now2 = (now - base) & (mod - 1);
+
+       /* Is the schedule already full? */
+       if (unlikely(!empty && start < period)) {
+               ehci_dbg(ehci, "iso sched full %p (%u-%u < %u mod %u)\n",
+                               urb, stream->next_uframe, base, period, mod);
+               status = -ENOSPC;
+               goto fail;
+       }
+
+       /* Is the next packet scheduled after the base time? */
+       if (likely(!empty || start <= now2 + period)) {
+
+               /* URB_ISO_ASAP: make sure that start >= next */
+               if (unlikely(start < next &&
+                               (urb->transfer_flags & URB_ISO_ASAP)))
+                       goto do_ASAP;
+
+               /* Otherwise use start, if it's not in the past */
+               if (likely(start >= now2))
+                       goto use_start;
 
-               /* no room in the schedule */
-               if (!done) {
-                       ehci_dbg(ehci, "iso sched full %p", urb);
-                       status = -ENOSPC;
-                       goto fail;
+       /* Otherwise we got an underrun while the queue was empty */
+       } else {
+               if (urb->transfer_flags & URB_ISO_ASAP)
+                       goto do_ASAP;
+               wrap = mod;
+               now2 += mod;
+       }
+
+       /* How many uframes and packets do we need to skip? */
+       skip = (now2 - start + period - 1) & -period;
+       if (skip >= span) {             /* Entirely in the past? */
+               ehci_dbg(ehci, "iso underrun %p (%u+%u < %u) [%u]\n",
+                               urb, start + base, span - period, now2 + base,
+                               base);
+
+               /* Try to keep the last TD intact for scanning later */
+               skip = span - period;
+
+               /* Will it come before the current scan position? */
+               if (empty) {
+                       skip = span;    /* Skip the entire URB */
+                       status = 1;     /* and give it back immediately */
+                       iso_sched_free(stream, sched);
+                       sched = NULL;
                }
        }
+       urb->error_count = skip / period;
+       if (sched)
+               sched->first_packet = urb->error_count;
+       goto use_start;
 
+ do_ASAP:
+       /* Use the first slot after "next" */
+       start = next + ((start - next) & (period - 1));
+
+ use_start:
        /* Tried to schedule too far into the future? */
-       if (unlikely(start - base + span - period >= mod)) {
+       if (unlikely(start + span - period >= mod + wrap)) {
                ehci_dbg(ehci, "request %p would overflow (%u+%u >= %u)\n",
-                               urb, start - base, span - period, mod);
+                               urb, start, span - period, mod + wrap);
                status = -EFBIG;
                goto fail;
        }
 
-       stream->next_uframe = start & (mod - 1);
+       start += base;
+       stream->next_uframe = (start + skip) & (mod - 1);
 
        /* report high speed start in uframes; full speed, in frames */
-       urb->start_frame = stream->next_uframe;
+       urb->start_frame = start & (mod - 1);
        if (!stream->highspeed)
                urb->start_frame >>= 3;
 
        /* Make sure scan_isoc() sees these */
        if (ehci->isoc_count == 0)
                ehci->last_iso_frame = now >> 3;
-       return 0;
+       return status;
 
  fail:
        iso_sched_free(stream, sched);
@@ -1610,7 +1788,8 @@ static void itd_link_urb(
        ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
 
        /* fill iTDs uframe by uframe */
-       for (packet = 0, itd = NULL; packet < urb->number_of_packets; ) {
+       for (packet = iso_sched->first_packet, itd = NULL;
+                       packet < urb->number_of_packets;) {
                if (itd == NULL) {
                        /* ASSERT:  we have all necessary itds */
                        // BUG_ON (list_empty (&iso_sched->td_list));
@@ -1630,7 +1809,7 @@ static void itd_link_urb(
 
                itd_patch(ehci, itd, iso_sched, packet, uframe);
 
-               next_uframe += stream->interval;
+               next_uframe += stream->uperiod;
                next_uframe &= mod - 1;
                packet++;
 
@@ -1770,9 +1949,9 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
                ehci_dbg (ehci, "can't get iso stream\n");
                return -ENOMEM;
        }
-       if (unlikely (urb->interval != stream->interval)) {
+       if (unlikely(urb->interval != stream->uperiod)) {
                ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
-                       stream->interval, urb->interval);
+                       stream->uperiod, urb->interval);
                goto done;
        }
 
@@ -1804,10 +1983,14 @@ static int itd_submit (struct ehci_hcd *ehci, struct urb *urb,
        if (unlikely(status))
                goto done_not_linked;
        status = iso_stream_schedule(ehci, urb, stream);
-       if (likely (status == 0))
+       if (likely(status == 0)) {
                itd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
-       else
+       } else if (status > 0) {
+               status = 0;
+               ehci_urb_done(ehci, urb, 0);
+       } else {
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+       }
  done_not_linked:
        spin_unlock_irqrestore (&ehci->lock, flags);
  done:
@@ -1833,7 +2016,7 @@ sitd_sched_init(
        dma_addr_t      dma = urb->transfer_dma;
 
        /* how many frames are needed for these transfers */
-       iso_sched->span = urb->number_of_packets * stream->interval;
+       iso_sched->span = urb->number_of_packets * stream->ps.period;
 
        /* figure out per-frame sitd fields that we'll need later
         * when we fit new sitds into the schedule.
@@ -1925,7 +2108,7 @@ sitd_urb_transaction (
 
                memset (sitd, 0, sizeof *sitd);
                sitd->sitd_dma = sitd_dma;
-               sitd->frame = 9999;             /* an invalid value */
+               sitd->frame = NO_FRAME;
                list_add (&sitd->sitd_list, &iso_sched->td_list);
        }
 
@@ -2008,7 +2191,7 @@ static void sitd_link_urb(
        ehci_to_hcd(ehci)->self.bandwidth_isoc_reqs++;
 
        /* fill sITDs frame by frame */
-       for (packet = 0, sitd = NULL;
+       for (packet = sched->first_packet, sitd = NULL;
                        packet < urb->number_of_packets;
                        packet++) {
 
@@ -2027,7 +2210,7 @@ static void sitd_link_urb(
                sitd_link(ehci, (next_uframe >> 3) & (ehci->periodic_size - 1),
                                sitd);
 
-               next_uframe += stream->interval << 3;
+               next_uframe += stream->uperiod;
        }
        stream->next_uframe = next_uframe & (mod - 1);
 
@@ -2146,9 +2329,9 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
                ehci_dbg (ehci, "can't get iso stream\n");
                return -ENOMEM;
        }
-       if (urb->interval != stream->interval) {
+       if (urb->interval != stream->ps.period) {
                ehci_dbg (ehci, "can't change iso interval %d --> %d\n",
-                       stream->interval, urb->interval);
+                       stream->ps.period, urb->interval);
                goto done;
        }
 
@@ -2178,10 +2361,14 @@ static int sitd_submit (struct ehci_hcd *ehci, struct urb *urb,
        if (unlikely(status))
                goto done_not_linked;
        status = iso_stream_schedule(ehci, urb, stream);
-       if (status == 0)
+       if (likely(status == 0)) {
                sitd_link_urb (ehci, urb, ehci->periodic_size << 3, stream);
-       else
+       } else if (status > 0) {
+               status = 0;
+               ehci_urb_done(ehci, urb, 0);
+       } else {
                usb_hcd_unlink_urb_from_ep(ehci_to_hcd(ehci), urb);
+       }
  done_not_linked:
        spin_unlock_irqrestore (&ehci->lock, flags);
  done:
@@ -2259,7 +2446,8 @@ restart:
                                    q.itd->hw_next != EHCI_LIST_END(ehci))
                                        *hw_p = q.itd->hw_next;
                                else
-                                       *hw_p = ehci->dummy->qh_dma;
+                                       *hw_p = cpu_to_hc32(ehci,
+                                                       ehci->dummy->qh_dma);
                                type = Q_NEXT_TYPE(ehci, q.itd->hw_next);
                                wmb();
                                modified = itd_complete (ehci, q.itd);
@@ -2294,7 +2482,8 @@ restart:
                                    q.sitd->hw_next != EHCI_LIST_END(ehci))
                                        *hw_p = q.sitd->hw_next;
                                else
-                                       *hw_p = ehci->dummy->qh_dma;
+                                       *hw_p = cpu_to_hc32(ehci,
+                                                       ehci->dummy->qh_dma);
                                type = Q_NEXT_TYPE(ehci, q.sitd->hw_next);
                                wmb();
                                modified = sitd_complete (ehci, q.sitd);
index b2de52d3961488f249aeb9d4026efe2d603b72bb..8a734498079bc176938c57a6b132c146e9be00dd 100644 (file)
@@ -55,7 +55,7 @@ const struct hc_driver ehci_sead3_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 93e59a13bc1fec919ef81690bbce9f4bdcd3e951..dc899eb2b86183561351d78e8dba1ceffc9cbb18 100644 (file)
@@ -36,7 +36,7 @@ static const struct hc_driver ehci_sh_hc_driver = {
         * generic hardware linkage
         */
        .irq                            = ehci_irq,
-       .flags                          = HCD_USB2 | HCD_MEMORY,
+       .flags                          = HCD_USB2 | HCD_MEMORY | HCD_BH,
 
        /*
         * basic lifecycle operations
index 14ced00ba220abded902fe7832d598fa37eeffb3..f6459dfb6f54fd1a08c2780dd804a906ccca539b 100644 (file)
@@ -97,8 +97,7 @@ static ssize_t store_uframe_periodic_max(struct device *dev,
 {
        struct ehci_hcd         *ehci;
        unsigned                uframe_periodic_max;
-       unsigned                frame, uframe;
-       unsigned short          allocated_max;
+       unsigned                uframe;
        unsigned long           flags;
        ssize_t                 ret;
 
@@ -122,16 +121,14 @@ static ssize_t store_uframe_periodic_max(struct device *dev,
 
        /*
         * for request to decrease max periodic bandwidth, we have to check
-        * every microframe in the schedule to see whether the decrease is
-        * possible.
+        * to see whether the decrease is possible.
         */
        if (uframe_periodic_max < ehci->uframe_periodic_max) {
-               allocated_max = 0;
+               u8              allocated_max = 0;
 
-               for (frame = 0; frame < ehci->periodic_size; ++frame)
-                       for (uframe = 0; uframe < 7; ++uframe)
-                               allocated_max = max(allocated_max,
-                                                   periodic_usecs (ehci, frame, uframe));
+               for (uframe = 0; uframe < EHCI_BANDWIDTH_SIZE; ++uframe)
+                       allocated_max = max(allocated_max,
+                                       ehci->bandwidth[uframe]);
 
                if (allocated_max > uframe_periodic_max) {
                        ehci_info(ehci,
index e74aaf3f016450a84ee42600ce584bebab41d9b0..b9fd0396011e54b79405cbac891540a6cd4c8964 100644 (file)
@@ -387,7 +387,7 @@ static int tegra_ehci_probe(struct platform_device *pdev)
 
        err = clk_prepare_enable(tegra->clk);
        if (err)
-               goto cleanup_clk_get;
+               goto cleanup_hcd_create;
 
        tegra_periph_reset_assert(tegra->clk);
        udelay(1);
@@ -464,8 +464,6 @@ cleanup_phy:
        usb_phy_shutdown(hcd->phy);
 cleanup_clk_en:
        clk_disable_unprepare(tegra->clk);
-cleanup_clk_get:
-       clk_put(tegra->clk);
 cleanup_hcd_create:
        usb_put_hcd(hcd);
        return err;
index cca4be90a864dba009c852f606653fb5fe60d568..67026ffbf9a871c9780a4b0b7f0d6c739c904e73 100644 (file)
@@ -61,7 +61,7 @@ static const struct hc_driver ehci_tilegx_hc_driver = {
         * Generic hardware linkage.
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * Basic lifecycle operations.
index 59e0e24c753febfb76369be8f872cb3731c2f365..cdad8438c02b3171567f5e0084ba46ee480db669 100644 (file)
  *
  */
 
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ehci.h"
 
 /* enable phy0 and phy1 for w90p910 */
 #define        ENPHY           (0x01<<8)
 #define PHY0_CTR       (0xA4)
 #define PHY1_CTR       (0xA8)
 
+#define DRIVER_DESC "EHCI w90x900 driver"
+
+static const char hcd_name[] = "ehci-w90x900 ";
+
+static struct hc_driver __read_mostly ehci_w90x900_hc_driver;
+
 static int usb_w90x900_probe(const struct hc_driver *driver,
                      struct platform_device *pdev)
 {
@@ -90,8 +105,8 @@ err1:
        return retval;
 }
 
-static
-void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev)
+static void usb_w90x900_remove(struct usb_hcd *hcd,
+                       struct platform_device *pdev)
 {
        usb_remove_hcd(hcd);
        iounmap(hcd->regs);
@@ -99,54 +114,6 @@ void usb_w90x900_remove(struct usb_hcd *hcd, struct platform_device *pdev)
        usb_put_hcd(hcd);
 }
 
-static const struct hc_driver ehci_w90x900_hc_driver = {
-       .description = hcd_name,
-       .product_desc = "Nuvoton w90x900 EHCI Host Controller",
-       .hcd_priv_size = sizeof(struct ehci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq = ehci_irq,
-       .flags = HCD_USB2|HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset = ehci_setup,
-       .start = ehci_run,
-
-       .stop = ehci_stop,
-       .shutdown = ehci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue = ehci_urb_enqueue,
-       .urb_dequeue = ehci_urb_dequeue,
-       .endpoint_disable = ehci_endpoint_disable,
-       .endpoint_reset = ehci_endpoint_reset,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number = ehci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data = ehci_hub_status_data,
-       .hub_control = ehci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend = ehci_bus_suspend,
-       .bus_resume = ehci_bus_resume,
-#endif
-       .relinquish_port        = ehci_relinquish_port,
-       .port_handed_over       = ehci_port_handed_over,
-
-       .clear_tt_buffer_complete = ehci_clear_tt_buffer_complete,
-};
-
 static int ehci_w90x900_probe(struct platform_device *pdev)
 {
        if (usb_disabled())
@@ -173,7 +140,25 @@ static struct platform_driver ehci_hcd_w90x900_driver = {
        },
 };
 
+static int __init ehci_w90X900_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ehci_init_driver(&ehci_w90x900_hc_driver, NULL);
+       return platform_driver_register(&ehci_hcd_w90x900_driver);
+}
+module_init(ehci_w90X900_init);
+
+static void __exit ehci_w90X900_cleanup(void)
+{
+       platform_driver_unregister(&ehci_hcd_w90x900_driver);
+}
+module_exit(ehci_w90X900_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
-MODULE_DESCRIPTION("w90p910 usb ehci driver!");
-MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:w90p910-ehci");
+MODULE_LICENSE("GPL v2");
index eba962e6ebfbbd8ffdd46a85e0fed5ec9b18b78a..95979f9f4381d8e8e573c7e0fe254d5585d23ab8 100644 (file)
@@ -79,7 +79,7 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
         * generic hardware linkage
         */
        .irq                    = ehci_irq,
-       .flags                  = HCD_MEMORY | HCD_USB2,
+       .flags                  = HCD_MEMORY | HCD_USB2 | HCD_BH,
 
        /*
         * basic lifecycle operations
index 291db7d09f220fe24dfa8bf9c21f1528a0d46a59..e8f41c5e771b978ddcd42b49cd1b638fdcc89a57 100644 (file)
@@ -54,6 +54,28 @@ struct ehci_stats {
        unsigned long           unlink;
 };
 
+/*
+ * Scheduling and budgeting information for periodic transfers, for both
+ * high-speed devices and full/low-speed devices lying behind a TT.
+ */
+struct ehci_per_sched {
+       struct usb_device       *udev;          /* access to the TT */
+       struct usb_host_endpoint *ep;
+       struct list_head        ps_list;        /* node on ehci_tt's ps_list */
+       u16                     tt_usecs;       /* time on the FS/LS bus */
+       u16                     cs_mask;        /* C-mask and S-mask bytes */
+       u16                     period;         /* actual period in frames */
+       u16                     phase;          /* actual phase, frame part */
+       u8                      bw_phase;       /* same, for bandwidth
+                                                  reservation */
+       u8                      phase_uf;       /* uframe part of the phase */
+       u8                      usecs, c_usecs; /* times on the HS bus */
+       u8                      bw_uperiod;     /* period in microframes, for
+                                                  bandwidth reservation */
+       u8                      bw_period;      /* same, in frames */
+};
+#define NO_FRAME       29999                   /* frame not assigned yet */
+
 /* ehci_hcd->lock guards shared data against other CPUs:
  *   ehci_hcd: async, unlink, periodic (and shadow), ...
  *   usb_host_endpoint: hcpriv
@@ -230,6 +252,15 @@ struct ehci_hcd {                  /* one per controller */
        struct dentry           *debug_dir;
 #endif
 
+       /* bandwidth usage */
+#define EHCI_BANDWIDTH_SIZE    64
+#define EHCI_BANDWIDTH_FRAMES  (EHCI_BANDWIDTH_SIZE >> 3)
+       u8                      bandwidth[EHCI_BANDWIDTH_SIZE];
+                                               /* us allocated per uframe */
+       u8                      tt_budget[EHCI_BANDWIDTH_SIZE];
+                                               /* us budgeted per uframe */
+       struct list_head        tt_list;
+
        /* platform-specific data -- must come last */
        unsigned long           priv[0] __aligned(sizeof(s64));
 };
@@ -385,6 +416,7 @@ struct ehci_qh {
        struct list_head        intr_node;      /* list of intr QHs */
        struct ehci_qtd         *dummy;
        struct list_head        unlink_node;
+       struct ehci_per_sched   ps;             /* scheduling info */
 
        unsigned                unlink_cycle;
 
@@ -398,16 +430,8 @@ struct ehci_qh {
        u8                      xacterrs;       /* XactErr retry counter */
 #define        QH_XACTERR_MAX          32              /* XactErr retry limit */
 
-       /* periodic schedule info */
-       u8                      usecs;          /* intr bandwidth */
        u8                      gap_uf;         /* uframes split/csplit gap */
-       u8                      c_usecs;        /* ... split completion bw */
-       u16                     tt_usecs;       /* tt downstream bandwidth */
-       unsigned short          period;         /* polling interval */
-       unsigned short          start;          /* where polling starts */
-#define NO_FRAME ((unsigned short)~0)                  /* pick new start */
 
-       struct usb_device       *dev;           /* access to TT */
        unsigned                is_out:1;       /* bulk or intr OUT */
        unsigned                clearing_tt:1;  /* Clear-TT-Buf in progress */
        unsigned                dequeue_during_giveback:1;
@@ -434,6 +458,7 @@ struct ehci_iso_packet {
 struct ehci_iso_sched {
        struct list_head        td_list;
        unsigned                span;
+       unsigned                first_packet;
        struct ehci_iso_packet  packet [0];
 };
 
@@ -449,22 +474,17 @@ struct ehci_iso_stream {
        u8                      highspeed;
        struct list_head        td_list;        /* queued itds/sitds */
        struct list_head        free_list;      /* list of unused itds/sitds */
-       struct usb_device       *udev;
-       struct usb_host_endpoint *ep;
 
        /* output of (re)scheduling */
-       int                     next_uframe;
+       struct ehci_per_sched   ps;             /* scheduling info */
+       unsigned                next_uframe;
        __hc32                  splits;
 
        /* the rest is derived from the endpoint descriptor,
-        * trusting urb->interval == f(epdesc->bInterval) and
         * including the extra info for hw_bufp[0..2]
         */
-       u8                      usecs, c_usecs;
-       u16                     interval;
-       u16                     tt_usecs;
+       u16                     uperiod;        /* period in uframes */
        u16                     maxp;
-       u16                     raw_mask;
        unsigned                bandwidth;
 
        /* This is used to initialize iTD's hw_bufp fields */
@@ -579,6 +599,35 @@ struct ehci_fstn {
 
 /*-------------------------------------------------------------------------*/
 
+/*
+ * USB-2.0 Specification Sections 11.14 and 11.18
+ * Scheduling and budgeting split transactions using TTs
+ *
+ * A hub can have a single TT for all its ports, or multiple TTs (one for each
+ * port).  The bandwidth and budgeting information for the full/low-speed bus
+ * below each TT is self-contained and independent of the other TTs or the
+ * high-speed bus.
+ *
+ * "Bandwidth" refers to the number of microseconds on the FS/LS bus allocated
+ * to an interrupt or isochronous endpoint for each frame.  "Budget" refers to
+ * the best-case estimate of the number of full-speed bytes allocated to an
+ * endpoint for each microframe within an allocated frame.
+ *
+ * Removal of an endpoint invalidates a TT's budget.  Instead of trying to
+ * keep an up-to-date record, we recompute the budget when it is needed.
+ */
+
+struct ehci_tt {
+       u16                     bandwidth[EHCI_BANDWIDTH_FRAMES];
+
+       struct list_head        tt_list;        /* List of all ehci_tt's */
+       struct list_head        ps_list;        /* Items using this TT */
+       struct usb_tt           *usb_tt;
+       int                     tt_port;        /* TT port number */
+};
+
+/*-------------------------------------------------------------------------*/
+
 /* Prepare the PORTSC wakeup flags during controller suspend/resume */
 
 #define ehci_prepare_ports_for_controller_suspend(ehci, do_wakeup)     \
index fce13bcc4a3e69e23eae70d3f49c512101798ed0..55486bd23cf1f7c0917e0a61794a4db3a807e828 100644 (file)
@@ -412,7 +412,7 @@ struct debug_buffer {
                        tmp = 'h'; break; \
                default:                \
                        tmp = '?'; break; \
-               }; tmp; })
+               } tmp; })
 
 static inline char token_mark(struct fotg210_hcd *fotg210, __hc32 token)
 {
index 299253c826c7f50db1f3fddca6ebe7e5b86f53e7..e1c6d850a7e1320fa38ffcd518afee2dac46b78b 100644 (file)
@@ -402,7 +402,7 @@ struct debug_buffer {
                case QH_LOW_SPEED:  tmp = 'l'; break; \
                case QH_HIGH_SPEED: tmp = 'h'; break; \
                default: tmp = '?'; break; \
-               }; tmp; })
+               } tmp; })
 
 static inline char token_mark(struct fusbh200_hcd *fusbh200, __hc32 token)
 {
index 5b86ffb88f1cb53205061a08e339c65620293a9f..e5fb3cfd57a98a8d2987009ce571bae705fce83f 100644 (file)
@@ -199,10 +199,14 @@ static int hwahc_op_get_frame_number(struct usb_hcd *usb_hcd)
 {
        struct wusbhc *wusbhc = usb_hcd_to_wusbhc(usb_hcd);
        struct hwahc *hwahc = container_of(wusbhc, struct hwahc, wusbhc);
+       struct wahc *wa = &hwahc->wa;
 
-       dev_err(wusbhc->dev, "%s (%p [%p]) UNIMPLEMENTED\n", __func__,
-               usb_hcd, hwahc);
-       return -ENOSYS;
+       /*
+        * We cannot query the HWA for the WUSB time since that requires sending
+        * a synchronous URB and this function can be called in_interrupt.
+        * Instead, query the USB frame number for our parent and use that.
+        */
+       return usb_get_current_frame_number(wa->usb_dev);
 }
 
 static int hwahc_op_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
@@ -566,14 +570,10 @@ found:
                goto error;
        }
        wa->wa_descr = wa_descr = (struct usb_wa_descriptor *) hdr;
-       /* Make LE fields CPU order */
-       wa_descr->bcdWAVersion = le16_to_cpu(wa_descr->bcdWAVersion);
-       wa_descr->wNumRPipes = le16_to_cpu(wa_descr->wNumRPipes);
-       wa_descr->wRPipeMaxBlock = le16_to_cpu(wa_descr->wRPipeMaxBlock);
-       if (wa_descr->bcdWAVersion > 0x0100)
+       if (le16_to_cpu(wa_descr->bcdWAVersion) > 0x0100)
                dev_warn(dev, "Wire Adapter v%d.%d newer than groked v1.0\n",
-                        wa_descr->bcdWAVersion & 0xff00 >> 8,
-                        wa_descr->bcdWAVersion & 0x00ff);
+                        le16_to_cpu(wa_descr->bcdWAVersion) & 0xff00 >> 8,
+                        le16_to_cpu(wa_descr->bcdWAVersion) & 0x00ff);
        result = 0;
 error:
        return result;
index 6f29abad6815578a6282eb6e6d9cbe3b6469a8d8..935a2dd367a81fb682d820fdf017aa3b7776596f 100644 (file)
@@ -2108,7 +2108,7 @@ static int isp1362_show(struct seq_file *s, void *unused)
                                   default:
                                           s = "?";
                                           break;
-                                  };
+                                  }
                                   s;}), ep->maxpacket) ;
                list_for_each_entry(urb, &ep->hep->urb_list, urb_list) {
                        seq_printf(s, "  urb%p, %d/%d\n", urb,
index 36423db63073bc9bce3d6fa28dbf624006f9b6e9..418444ebb1b8bb1fd1862fc9233c7562c5c40d81 100644 (file)
  */
 
 #include <linux/clk.h>
-#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
+#include <linux/platform_device.h>
 #include <linux/platform_data/atmel.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
 #include <mach/hardware.h>
 #include <asm/gpio.h>
 
 #include <mach/cpu.h>
 
-#ifndef CONFIG_ARCH_AT91
-#error "CONFIG_ARCH_AT91 must be defined."
-#endif
+
+#include "ohci.h"
 
 #define valid_port(index)      ((index) >= 0 && (index) < AT91_MAX_USBH_PORTS)
 #define at91_for_each_port(index)      \
 
 /* interface, function and usb clocks; sometimes also an AHB clock */
 static struct clk *iclk, *fclk, *uclk, *hclk;
+/* interface and function clocks; sometimes also an AHB clock */
+
+#define DRIVER_DESC "OHCI Atmel driver"
+
+static const char hcd_name[] = "ohci-atmel";
+
+static struct hc_driver __read_mostly ohci_at91_hc_driver;
 static int clocked;
+static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
+                       u16 wValue, u16 wIndex, char *buf, u16 wLength);
+static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
 
 extern int usb_disabled(void);
 
@@ -117,6 +132,8 @@ static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *);
 static int usb_hcd_at91_probe(const struct hc_driver *driver,
                        struct platform_device *pdev)
 {
+       struct at91_usbh_data *board;
+       struct ohci_hcd *ohci;
        int retval;
        struct usb_hcd *hcd = NULL;
 
@@ -177,8 +194,10 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
                }
        }
 
+       board = hcd->self.controller->platform_data;
+       ohci = hcd_to_ohci(hcd);
+       ohci->num_ports = board->ports;
        at91_start_hc(pdev);
-       ohci_hcd_init(hcd_to_ohci(hcd));
 
        retval = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
        if (retval == 0)
@@ -238,36 +257,6 @@ static void usb_hcd_at91_remove(struct usb_hcd *hcd,
 }
 
 /*-------------------------------------------------------------------------*/
-
-static int
-ohci_at91_reset (struct usb_hcd *hcd)
-{
-       struct at91_usbh_data   *board = dev_get_platdata(hcd->self.controller);
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-       int                     ret;
-
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
-
-       ohci->num_ports = board->ports;
-       return 0;
-}
-
-static int
-ohci_at91_start (struct usb_hcd *hcd)
-{
-       struct ohci_hcd         *ohci = hcd_to_ohci (hcd);
-       int                     ret;
-
-       if ((ret = ohci_run(ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
-       return 0;
-}
-
 static void ohci_at91_usb_set_power(struct at91_usbh_data *pdata, int port, int enable)
 {
        if (!valid_port(port))
@@ -297,8 +286,8 @@ static int ohci_at91_usb_get_power(struct at91_usbh_data *pdata, int port)
  */
 static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf)
 {
-       struct at91_usbh_data *pdata = dev_get_platdata(hcd->self.controller);
-       int length = ohci_hub_status_data(hcd, buf);
+       struct at91_usbh_data *pdata = hcd->self.controller->platform_data;
+       int length = orig_ohci_hub_status_data(hcd, buf);
        int port;
 
        at91_for_each_port(port) {
@@ -376,7 +365,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                break;
        }
 
-       ret = ohci_hub_control(hcd, typeReq, wValue, wIndex + 1, buf, wLength);
+       ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex + 1,
+                               buf, wLength);
        if (ret)
                goto out;
 
@@ -430,51 +420,6 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
 /*-------------------------------------------------------------------------*/
 
-static const struct hc_driver ohci_at91_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "AT91 OHCI",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset =                ohci_at91_reset,
-       .start =                ohci_at91_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_at91_hub_status_data,
-       .hub_control =          ohci_at91_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
-
 static irqreturn_t ohci_hcd_at91_overcurrent_irq(int irq, void *data)
 {
        struct platform_device *pdev = data;
@@ -702,7 +647,11 @@ ohci_hcd_at91_drv_suspend(struct platform_device *pdev, pm_message_t mesg)
         * REVISIT: some boards will be able to turn VBUS off...
         */
        if (at91_suspend_entering_slow_clock()) {
-               ohci_usb_reset (ohci);
+               ohci->hc_control = ohci_readl(ohci, &ohci->regs->control);
+               ohci->hc_control &= OHCI_CTRL_RWC;
+               ohci_writel(ohci, ohci->hc_control, &ohci->regs->control);
+               ohci->rh_state = OHCI_RH_HALTED;
+
                /* flush the writes */
                (void) ohci_readl (ohci, &ohci->regs->control);
                at91_stop_clock();
@@ -729,8 +678,6 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev)
 #define ohci_hcd_at91_drv_resume  NULL
 #endif
 
-MODULE_ALIAS("platform:at91_ohci");
-
 static struct platform_driver ohci_hcd_at91_driver = {
        .probe          = ohci_hcd_at91_drv_probe,
        .remove         = ohci_hcd_at91_drv_remove,
@@ -743,3 +690,40 @@ static struct platform_driver ohci_hcd_at91_driver = {
                .of_match_table = of_match_ptr(at91_ohci_dt_ids),
        },
 };
+
+static int __init ohci_at91_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&ohci_at91_hc_driver, NULL);
+
+       /*
+        * The Atmel HW has some unusual quirks, which require Atmel-specific
+        * workarounds. We override certain hc_driver functions here to
+        * achieve that. We explicitly do not enhance ohci_driver_overrides to
+        * allow this more easily, since this is an unusual case, and we don't
+        * want to encourage others to override these functions by making it
+        * too easy.
+        */
+
+       orig_ohci_hub_control = ohci_at91_hc_driver.hub_control;
+       orig_ohci_hub_status_data = ohci_at91_hc_driver.hub_status_data;
+
+       ohci_at91_hc_driver.hub_status_data     = ohci_at91_hub_status_data;
+       ohci_at91_hc_driver.hub_control         = ohci_at91_hub_control;
+
+       return platform_driver_register(&ohci_hcd_at91_driver);
+}
+module_init(ohci_at91_init);
+
+static void __exit ohci_at91_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_at91_driver);
+}
+module_exit(ohci_at91_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:at91_ohci");
index 31b81f9eacdc90cff804bcb89d4ecefff9a3a16f..3fca52ec02ac0b2d0859e96699958030938fce06 100644 (file)
@@ -17,7 +17,7 @@
        case PIPE_BULK:         temp = "bulk"; break; \
        case PIPE_INTERRUPT:    temp = "intr"; break; \
        default:                temp = "isoc"; break; \
-       }; temp;})
+       } temp;})
 #define pipestring(pipe) edstring(usb_pipetype(pipe))
 
 /* debug| print the main components of an URB
index 84a20d5223b962b869e0c99499eed28230497822..492f681c70f2b550faeee2238bc16a3b7032fdb2 100644 (file)
 
 #include <linux/clk.h>
 #include <linux/device.h>
-#include <linux/signal.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
-static struct clk *usb_host_clock;
+#include "ohci.h"
 
-static int ohci_ep93xx_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
+#define DRIVER_DESC "OHCI EP93xx driver"
 
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
+static const char hcd_name[] = "ohci-ep93xx";
 
-       if ((ret = ohci_run(ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
+static struct hc_driver __read_mostly ohci_ep93xx_hc_driver;
 
-       return 0;
-}
-
-static struct hc_driver ohci_ep93xx_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "EP93xx OHCI",
-       .hcd_priv_size          = sizeof(struct ohci_hcd),
-       .irq                    = ohci_irq,
-       .flags                  = HCD_USB11 | HCD_MEMORY,
-       .start                  = ohci_ep93xx_start,
-       .stop                   = ohci_stop,
-       .shutdown               = ohci_shutdown,
-       .urb_enqueue            = ohci_urb_enqueue,
-       .urb_dequeue            = ohci_urb_dequeue,
-       .endpoint_disable       = ohci_endpoint_disable,
-       .get_frame_number       = ohci_get_frame,
-       .hub_status_data        = ohci_hub_status_data,
-       .hub_control            = ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend            = ohci_bus_suspend,
-       .bus_resume             = ohci_bus_resume,
-#endif
-       .start_port_reset       = ohci_start_port_reset,
-};
+static struct clk *usb_host_clock;
 
 static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev)
 {
@@ -109,8 +82,6 @@ static int ohci_hcd_ep93xx_drv_probe(struct platform_device *pdev)
 
        clk_enable(usb_host_clock);
 
-       ohci_hcd_init(hcd_to_ohci(hcd));
-
        ret = usb_add_hcd(hcd, irq, 0);
        if (ret)
                goto err_clk_disable;
@@ -166,7 +137,6 @@ static int ohci_hcd_ep93xx_drv_resume(struct platform_device *pdev)
 }
 #endif
 
-
 static struct platform_driver ohci_hcd_ep93xx_driver = {
        .probe          = ohci_hcd_ep93xx_drv_probe,
        .remove         = ohci_hcd_ep93xx_drv_remove,
@@ -181,4 +151,24 @@ static struct platform_driver ohci_hcd_ep93xx_driver = {
        },
 };
 
+static int __init ohci_ep93xx_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_ep93xx_hc_driver, NULL);
+       return platform_driver_register(&ohci_hcd_ep93xx_driver);
+}
+module_init(ohci_ep93xx_init);
+
+static void __exit ohci_ep93xx_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_ep93xx_driver);
+}
+module_exit(ohci_ep93xx_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:ep93xx-ohci");
index 866f2464f9de64c84ac96114c96dccb52a472d32..91ec9b2cd37868f600eca4dd41cdf6180eed806e 100644 (file)
  */
 
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
-#include <linux/platform_data/usb-ohci-exynos.h>
 #include <linux/usb/phy.h>
 #include <linux/usb/samsung_usb_phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI EXYNOS driver"
+
+static const char hcd_name[] = "ohci-exynos";
+static struct hc_driver __read_mostly exynos_ohci_hc_driver;
+
+#define to_exynos_ohci(hcd) (struct exynos_ohci_hcd *)(hcd_to_ohci(hcd)->priv)
 
 struct exynos_ohci_hcd {
-       struct device *dev;
-       struct usb_hcd *hcd;
        struct clk *clk;
        struct usb_phy *phy;
        struct usb_otg *otg;
-       struct exynos4_ohci_platdata *pdata;
 };
 
-static void exynos_ohci_phy_enable(struct exynos_ohci_hcd *exynos_ohci)
+static void exynos_ohci_phy_enable(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
 
        if (exynos_ohci->phy)
                usb_phy_init(exynos_ohci->phy);
-       else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_init)
-               exynos_ohci->pdata->phy_init(pdev, USB_PHY_TYPE_HOST);
 }
 
-static void exynos_ohci_phy_disable(struct exynos_ohci_hcd *exynos_ohci)
+static void exynos_ohci_phy_disable(struct platform_device *pdev)
 {
-       struct platform_device *pdev = to_platform_device(exynos_ohci->dev);
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
 
        if (exynos_ohci->phy)
                usb_phy_shutdown(exynos_ohci->phy);
-       else if (exynos_ohci->pdata && exynos_ohci->pdata->phy_exit)
-               exynos_ohci->pdata->phy_exit(pdev, USB_PHY_TYPE_HOST);
-}
-
-static int ohci_exynos_reset(struct usb_hcd *hcd)
-{
-       return ohci_init(hcd_to_ohci(hcd));
 }
 
-static int ohci_exynos_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       ohci_dbg(ohci, "ohci_exynos_start, ohci:%p", ohci);
-
-       ret = ohci_run(ohci);
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
-
-       return 0;
-}
-
-static const struct hc_driver exynos_ohci_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "EXYNOS OHCI Host Controller",
-       .hcd_priv_size          = sizeof(struct ohci_hcd),
-
-       .irq                    = ohci_irq,
-       .flags                  = HCD_MEMORY|HCD_USB11,
-
-       .reset                  = ohci_exynos_reset,
-       .start                  = ohci_exynos_start,
-       .stop                   = ohci_stop,
-       .shutdown               = ohci_shutdown,
-
-       .get_frame_number       = ohci_get_frame,
-
-       .urb_enqueue            = ohci_urb_enqueue,
-       .urb_dequeue            = ohci_urb_dequeue,
-       .endpoint_disable       = ohci_endpoint_disable,
-
-       .hub_status_data        = ohci_hub_status_data,
-       .hub_control            = ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend            = ohci_bus_suspend,
-       .bus_resume             = ohci_bus_resume,
-#endif
-       .start_port_reset       = ohci_start_port_reset,
-};
-
 static int exynos_ohci_probe(struct platform_device *pdev)
 {
-       struct exynos4_ohci_platdata *pdata = dev_get_platdata(&pdev->dev);
        struct exynos_ohci_hcd *exynos_ohci;
        struct usb_hcd *hcd;
-       struct ohci_hcd *ohci;
        struct resource *res;
        struct usb_phy *phy;
        int irq;
@@ -118,10 +75,14 @@ static int exynos_ohci_probe(struct platform_device *pdev)
        if (err)
                return err;
 
-       exynos_ohci = devm_kzalloc(&pdev->dev, sizeof(struct exynos_ohci_hcd),
-                                       GFP_KERNEL);
-       if (!exynos_ohci)
+       hcd = usb_create_hcd(&exynos_ohci_hc_driver,
+                               &pdev->dev, dev_name(&pdev->dev));
+       if (!hcd) {
+               dev_err(&pdev->dev, "Unable to create HCD\n");
                return -ENOMEM;
+       }
+
+       exynos_ohci = to_exynos_ohci(hcd);
 
        if (of_device_is_compatible(pdev->dev.of_node,
                                        "samsung,exynos5440-ohci"))
@@ -129,30 +90,15 @@ static int exynos_ohci_probe(struct platform_device *pdev)
 
        phy = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
        if (IS_ERR(phy)) {
-               /* Fallback to pdata */
-               if (!pdata) {
-                       dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
-                       return -EPROBE_DEFER;
-               } else {
-                       exynos_ohci->pdata = pdata;
-               }
+               usb_put_hcd(hcd);
+               dev_warn(&pdev->dev, "no platform data or transceiver defined\n");
+               return -EPROBE_DEFER;
        } else {
                exynos_ohci->phy = phy;
                exynos_ohci->otg = phy->otg;
        }
 
 skip_phy:
-
-       exynos_ohci->dev = &pdev->dev;
-
-       hcd = usb_create_hcd(&exynos_ohci_hc_driver, &pdev->dev,
-                                       dev_name(&pdev->dev));
-       if (!hcd) {
-               dev_err(&pdev->dev, "Unable to create HCD\n");
-               return -ENOMEM;
-       }
-
-       exynos_ohci->hcd = hcd;
        exynos_ohci->clk = devm_clk_get(&pdev->dev, "usbhost");
 
        if (IS_ERR(exynos_ohci->clk)) {
@@ -189,26 +135,21 @@ skip_phy:
        }
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_enable(exynos_ohci);
+       platform_set_drvdata(pdev, hcd);
 
-       ohci = hcd_to_ohci(hcd);
-       ohci_hcd_init(ohci);
+       exynos_ohci_phy_enable(pdev);
 
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err) {
                dev_err(&pdev->dev, "Failed to add USB HCD\n");
                goto fail_add_hcd;
        }
-
-       platform_set_drvdata(pdev, exynos_ohci);
-
        return 0;
 
 fail_add_hcd:
-       exynos_ohci_phy_disable(exynos_ohci);
+       exynos_ohci_phy_disable(pdev);
 fail_io:
        clk_disable_unprepare(exynos_ohci->clk);
 fail_clk:
@@ -218,16 +159,15 @@ fail_clk:
 
 static int exynos_ohci_remove(struct platform_device *pdev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
 
        usb_remove_hcd(hcd);
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_disable(exynos_ohci);
+       exynos_ohci_phy_disable(pdev);
 
        clk_disable_unprepare(exynos_ohci->clk);
 
@@ -238,8 +178,7 @@ static int exynos_ohci_remove(struct platform_device *pdev)
 
 static void exynos_ohci_shutdown(struct platform_device *pdev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = platform_get_drvdata(pdev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
        if (hcd->driver->shutdown)
                hcd->driver->shutdown(hcd);
@@ -248,9 +187,10 @@ static void exynos_ohci_shutdown(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int exynos_ohci_suspend(struct device *dev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct exynos_ohci_hcd *exynos_ohci = to_exynos_ohci(hcd);
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       struct platform_device *pdev = to_platform_device(dev);
        unsigned long flags;
        int rc = 0;
 
@@ -270,10 +210,9 @@ static int exynos_ohci_suspend(struct device *dev)
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_disable(exynos_ohci);
+       exynos_ohci_phy_disable(pdev);
 
        clk_disable_unprepare(exynos_ohci->clk);
 
@@ -285,16 +224,16 @@ fail:
 
 static int exynos_ohci_resume(struct device *dev)
 {
-       struct exynos_ohci_hcd *exynos_ohci = dev_get_drvdata(dev);
-       struct usb_hcd *hcd = exynos_ohci->hcd;
+       struct usb_hcd *hcd                     = dev_get_drvdata(dev);
+       struct exynos_ohci_hcd *exynos_ohci     = to_exynos_ohci(hcd);
+       struct platform_device *pdev            = to_platform_device(dev);
 
        clk_prepare_enable(exynos_ohci->clk);
 
        if (exynos_ohci->otg)
-               exynos_ohci->otg->set_host(exynos_ohci->otg,
-                                       &exynos_ohci->hcd->self);
+               exynos_ohci->otg->set_host(exynos_ohci->otg, &hcd->self);
 
-       exynos_ohci_phy_enable(exynos_ohci);
+       exynos_ohci_phy_enable(pdev);
 
        ohci_resume(hcd, false);
 
@@ -305,6 +244,10 @@ static int exynos_ohci_resume(struct device *dev)
 #define exynos_ohci_resume     NULL
 #endif
 
+static const struct ohci_driver_overrides exynos_overrides __initconst = {
+       .extra_priv_size =      sizeof(struct exynos_ohci_hcd),
+};
+
 static const struct dev_pm_ops exynos_ohci_pm_ops = {
        .suspend        = exynos_ohci_suspend,
        .resume         = exynos_ohci_resume,
@@ -330,6 +273,23 @@ static struct platform_driver exynos_ohci_driver = {
                .of_match_table = of_match_ptr(exynos_ohci_match),
        }
 };
+static int __init ohci_exynos_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&exynos_ohci_hc_driver, &exynos_overrides);
+       return platform_driver_register(&exynos_ohci_driver);
+}
+module_init(ohci_exynos_init);
+
+static void __exit ohci_exynos_cleanup(void)
+{
+       platform_driver_unregister(&exynos_ohci_driver);
+}
+module_exit(ohci_exynos_cleanup);
 
 MODULE_ALIAS("platform:exynos-ohci");
 MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_LICENSE("GPL v2");
index 604cad1bcf9cd984666aaf0b2b797b48ee6e3382..8ada13f8dde2c7350977cf343987fbb14c2f303b 100644 (file)
@@ -1161,10 +1161,12 @@ void ohci_init_driver(struct hc_driver *drv,
        /* Copy the generic table to drv and then apply the overrides */
        *drv = ohci_hc_driver;
 
-       drv->product_desc = over->product_desc;
-       drv->hcd_priv_size += over->extra_priv_size;
-       if (over->reset)
-               drv->reset = over->reset;
+       if (over) {
+               drv->product_desc = over->product_desc;
+               drv->hcd_priv_size += over->extra_priv_size;
+               if (over->reset)
+                       drv->reset = over->reset;
+       }
 }
 EXPORT_SYMBOL_GPL(ohci_init_driver);
 
@@ -1179,46 +1181,6 @@ MODULE_LICENSE ("GPL");
 #define SA1111_DRIVER          ohci_hcd_sa1111_driver
 #endif
 
-#if defined(CONFIG_ARCH_S3C24XX) || defined(CONFIG_ARCH_S3C64XX)
-#include "ohci-s3c2410.c"
-#define S3C2410_PLATFORM_DRIVER        ohci_hcd_s3c2410_driver
-#endif
-
-#ifdef CONFIG_USB_OHCI_EXYNOS
-#include "ohci-exynos.c"
-#define EXYNOS_PLATFORM_DRIVER exynos_ohci_driver
-#endif
-
-#ifdef CONFIG_USB_OHCI_HCD_OMAP1
-#include "ohci-omap.c"
-#define OMAP1_PLATFORM_DRIVER  ohci_hcd_omap_driver
-#endif
-
-#ifdef CONFIG_USB_OHCI_HCD_OMAP3
-#include "ohci-omap3.c"
-#define OMAP3_PLATFORM_DRIVER  ohci_hcd_omap3_driver
-#endif
-
-#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx)
-#include "ohci-pxa27x.c"
-#define PLATFORM_DRIVER                ohci_hcd_pxa27x_driver
-#endif
-
-#ifdef CONFIG_ARCH_EP93XX
-#include "ohci-ep93xx.c"
-#define EP93XX_PLATFORM_DRIVER ohci_hcd_ep93xx_driver
-#endif
-
-#ifdef CONFIG_ARCH_AT91
-#include "ohci-at91.c"
-#define AT91_PLATFORM_DRIVER   ohci_hcd_at91_driver
-#endif
-
-#ifdef CONFIG_ARCH_LPC32XX
-#include "ohci-nxp.c"
-#define NXP_PLATFORM_DRIVER    usb_hcd_nxp_driver
-#endif
-
 #ifdef CONFIG_ARCH_DAVINCI_DA8XX
 #include "ohci-da8xx.c"
 #define DAVINCI_PLATFORM_DRIVER        ohci_hcd_da8xx_driver
@@ -1229,11 +1191,6 @@ MODULE_LICENSE ("GPL");
 #define OF_PLATFORM_DRIVER     ohci_hcd_ppc_of_driver
 #endif
 
-#ifdef CONFIG_PLAT_SPEAR
-#include "ohci-spear.c"
-#define SPEAR_PLATFORM_DRIVER  spear_ohci_hcd_driver
-#endif
-
 #ifdef CONFIG_PPC_PS3
 #include "ohci-ps3.c"
 #define PS3_SYSTEM_BUS_DRIVER  ps3_ohci_driver
@@ -1296,18 +1253,6 @@ static int __init ohci_hcd_mod_init(void)
                goto error_platform;
 #endif
 
-#ifdef OMAP1_PLATFORM_DRIVER
-       retval = platform_driver_register(&OMAP1_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_omap1_platform;
-#endif
-
-#ifdef OMAP3_PLATFORM_DRIVER
-       retval = platform_driver_register(&OMAP3_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_omap3_platform;
-#endif
-
 #ifdef OF_PLATFORM_DRIVER
        retval = platform_driver_register(&OF_PLATFORM_DRIVER);
        if (retval < 0)
@@ -1332,79 +1277,19 @@ static int __init ohci_hcd_mod_init(void)
                goto error_tmio;
 #endif
 
-#ifdef S3C2410_PLATFORM_DRIVER
-       retval = platform_driver_register(&S3C2410_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_s3c2410;
-#endif
-
-#ifdef EXYNOS_PLATFORM_DRIVER
-       retval = platform_driver_register(&EXYNOS_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_exynos;
-#endif
-
-#ifdef EP93XX_PLATFORM_DRIVER
-       retval = platform_driver_register(&EP93XX_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_ep93xx;
-#endif
-
-#ifdef AT91_PLATFORM_DRIVER
-       retval = platform_driver_register(&AT91_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_at91;
-#endif
-
-#ifdef NXP_PLATFORM_DRIVER
-       retval = platform_driver_register(&NXP_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_nxp;
-#endif
-
 #ifdef DAVINCI_PLATFORM_DRIVER
        retval = platform_driver_register(&DAVINCI_PLATFORM_DRIVER);
        if (retval < 0)
                goto error_davinci;
 #endif
 
-#ifdef SPEAR_PLATFORM_DRIVER
-       retval = platform_driver_register(&SPEAR_PLATFORM_DRIVER);
-       if (retval < 0)
-               goto error_spear;
-#endif
-
        return retval;
 
        /* Error path */
-#ifdef SPEAR_PLATFORM_DRIVER
-       platform_driver_unregister(&SPEAR_PLATFORM_DRIVER);
- error_spear:
-#endif
 #ifdef DAVINCI_PLATFORM_DRIVER
        platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
  error_davinci:
 #endif
-#ifdef NXP_PLATFORM_DRIVER
-       platform_driver_unregister(&NXP_PLATFORM_DRIVER);
- error_nxp:
-#endif
-#ifdef AT91_PLATFORM_DRIVER
-       platform_driver_unregister(&AT91_PLATFORM_DRIVER);
- error_at91:
-#endif
-#ifdef EP93XX_PLATFORM_DRIVER
-       platform_driver_unregister(&EP93XX_PLATFORM_DRIVER);
- error_ep93xx:
-#endif
-#ifdef EXYNOS_PLATFORM_DRIVER
-       platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER);
- error_exynos:
-#endif
-#ifdef S3C2410_PLATFORM_DRIVER
-       platform_driver_unregister(&S3C2410_PLATFORM_DRIVER);
- error_s3c2410:
-#endif
 #ifdef TMIO_OHCI_DRIVER
        platform_driver_unregister(&TMIO_OHCI_DRIVER);
  error_tmio:
@@ -1421,14 +1306,6 @@ static int __init ohci_hcd_mod_init(void)
        platform_driver_unregister(&OF_PLATFORM_DRIVER);
  error_of_platform:
 #endif
-#ifdef OMAP3_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP3_PLATFORM_DRIVER);
- error_omap3_platform:
-#endif
-#ifdef OMAP1_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP1_PLATFORM_DRIVER);
- error_omap1_platform:
-#endif
 #ifdef PLATFORM_DRIVER
        platform_driver_unregister(&PLATFORM_DRIVER);
  error_platform:
@@ -1450,27 +1327,9 @@ module_init(ohci_hcd_mod_init);
 
 static void __exit ohci_hcd_mod_exit(void)
 {
-#ifdef SPEAR_PLATFORM_DRIVER
-       platform_driver_unregister(&SPEAR_PLATFORM_DRIVER);
-#endif
 #ifdef DAVINCI_PLATFORM_DRIVER
        platform_driver_unregister(&DAVINCI_PLATFORM_DRIVER);
 #endif
-#ifdef NXP_PLATFORM_DRIVER
-       platform_driver_unregister(&NXP_PLATFORM_DRIVER);
-#endif
-#ifdef AT91_PLATFORM_DRIVER
-       platform_driver_unregister(&AT91_PLATFORM_DRIVER);
-#endif
-#ifdef EP93XX_PLATFORM_DRIVER
-       platform_driver_unregister(&EP93XX_PLATFORM_DRIVER);
-#endif
-#ifdef EXYNOS_PLATFORM_DRIVER
-       platform_driver_unregister(&EXYNOS_PLATFORM_DRIVER);
-#endif
-#ifdef S3C2410_PLATFORM_DRIVER
-       platform_driver_unregister(&S3C2410_PLATFORM_DRIVER);
-#endif
 #ifdef TMIO_OHCI_DRIVER
        platform_driver_unregister(&TMIO_OHCI_DRIVER);
 #endif
@@ -1483,12 +1342,6 @@ static void __exit ohci_hcd_mod_exit(void)
 #ifdef OF_PLATFORM_DRIVER
        platform_driver_unregister(&OF_PLATFORM_DRIVER);
 #endif
-#ifdef OMAP3_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP3_PLATFORM_DRIVER);
-#endif
-#ifdef OMAP1_PLATFORM_DRIVER
-       platform_driver_unregister(&OMAP1_PLATFORM_DRIVER);
-#endif
 #ifdef PLATFORM_DRIVER
        platform_driver_unregister(&PLATFORM_DRIVER);
 #endif
index 2347ab83f046f7c7ce561aaec5562f47399efd59..61705a760e7db5d68fed2df19f94b3bdef96697e 100644 (file)
@@ -212,10 +212,11 @@ __acquires(ohci->lock)
        /* Sometimes PCI D3 suspend trashes frame timings ... */
        periodic_reinit (ohci);
 
-       /* the following code is executed with ohci->lock held and
-        * irqs disabled if and only if autostopped is true
+       /*
+        * The following code is executed with ohci->lock held and
+        * irqs disabled if and only if autostopped is true.  This
+        * will cause sparse to warn about a "context imbalance".
         */
-
 skip_resume:
        /* interrupts might have been disabled */
        ohci_writel (ohci, OHCI_INTR_INIT, &ohci->regs->intrenable);
@@ -531,7 +532,7 @@ ohci_hub_descriptor (
            temp |= 0x0010;
        else if (rh & RH_A_OCPM)        /* per-port overcurrent reporting? */
            temp |= 0x0008;
-       desc->wHubCharacteristics = (__force __u16)cpu_to_hc16(ohci, temp);
+       desc->wHubCharacteristics = cpu_to_le16(temp);
 
        /* ports removable, and usb 1.0 legacy PortPwrCtrlMask */
        rh = roothub_b (ohci);
index df3eb3e0324ea9ffde44836ed1788cd509d30d19..e99db8a6d55fc8b2fa965d548090c4b824ee11a3 100644 (file)
  * or implied.
  */
 #include <linux/clk.h>
-#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
 #include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/usb/isp1301.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
 
 #include <mach/hardware.h>
 #include <asm/mach-types.h>
 #define start_int_umask(irq)
 #endif
 
+#define DRIVER_DESC "OHCI NXP driver"
+
+static const char hcd_name[] = "ohci-nxp";
+static struct hc_driver __read_mostly ohci_nxp_hc_driver;
+
 static struct i2c_client *isp1301_i2c_client;
 
 extern int usb_disabled(void);
@@ -132,14 +146,14 @@ static inline void isp1301_vbus_off(void)
                OTG1_VBUS_DRV);
 }
 
-static void nxp_start_hc(void)
+static void ohci_nxp_start_hc(void)
 {
        unsigned long tmp = __raw_readl(USB_OTG_STAT_CONTROL) | HOST_EN;
        __raw_writel(tmp, USB_OTG_STAT_CONTROL);
        isp1301_vbus_on();
 }
 
-static void nxp_stop_hc(void)
+static void ohci_nxp_stop_hc(void)
 {
        unsigned long tmp;
        isp1301_vbus_off();
@@ -147,68 +161,9 @@ static void nxp_stop_hc(void)
        __raw_writel(tmp, USB_OTG_STAT_CONTROL);
 }
 
-static int ohci_nxp_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
-
-       if ((ret = ohci_run(ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop(hcd);
-               return ret;
-       }
-       return 0;
-}
-
-static const struct hc_driver ohci_nxp_hc_driver = {
-       .description = hcd_name,
-       .product_desc =         "nxp OHCI",
-
-       /*
-        * generic hardware linkage
-        */
-       .irq = ohci_irq,
-       .flags = HCD_USB11 | HCD_MEMORY,
-
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-       /*
-        * basic lifecycle operations
-        */
-       .start = ohci_nxp_start,
-       .stop = ohci_stop,
-       .shutdown = ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue = ohci_urb_enqueue,
-       .urb_dequeue = ohci_urb_dequeue,
-       .endpoint_disable = ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number = ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data = ohci_hub_status_data,
-       .hub_control = ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend = ohci_bus_suspend,
-       .bus_resume = ohci_bus_resume,
-#endif
-       .start_port_reset = ohci_start_port_reset,
-};
-
-static int usb_hcd_nxp_probe(struct platform_device *pdev)
+static int ohci_hcd_nxp_probe(struct platform_device *pdev)
 {
        struct usb_hcd *hcd = 0;
-       struct ohci_hcd *ohci;
        const struct hc_driver *driver = &ohci_nxp_hc_driver;
        struct resource *res;
        int ret = 0, irq;
@@ -314,17 +269,15 @@ static int usb_hcd_nxp_probe(struct platform_device *pdev)
                goto fail_resource;
        }
 
-       nxp_start_hc();
+       ohci_nxp_start_hc();
        platform_set_drvdata(pdev, hcd);
-       ohci = hcd_to_ohci(hcd);
-       ohci_hcd_init(ohci);
 
        dev_info(&pdev->dev, "at 0x%p, irq %d\n", hcd->regs, hcd->irq);
        ret = usb_add_hcd(hcd, irq, 0);
        if (ret == 0)
                return ret;
 
-       nxp_stop_hc();
+       ohci_nxp_stop_hc();
 fail_resource:
        usb_put_hcd(hcd);
 fail_hcd:
@@ -346,12 +299,12 @@ fail_disable:
        return ret;
 }
 
-static int usb_hcd_nxp_remove(struct platform_device *pdev)
+static int ohci_hcd_nxp_remove(struct platform_device *pdev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
 
        usb_remove_hcd(hcd);
-       nxp_stop_hc();
+       ohci_nxp_stop_hc();
        usb_put_hcd(hcd);
        clk_disable(usb_pll_clk);
        clk_put(usb_pll_clk);
@@ -367,20 +320,40 @@ static int usb_hcd_nxp_remove(struct platform_device *pdev)
 MODULE_ALIAS("platform:usb-ohci");
 
 #ifdef CONFIG_OF
-static const struct of_device_id usb_hcd_nxp_match[] = {
+static const struct of_device_id ohci_hcd_nxp_match[] = {
        { .compatible = "nxp,ohci-nxp" },
        {},
 };
-MODULE_DEVICE_TABLE(of, usb_hcd_nxp_match);
+MODULE_DEVICE_TABLE(of, ohci_hcd_nxp_match);
 #endif
 
-static struct platform_driver usb_hcd_nxp_driver = {
+static struct platform_driver ohci_hcd_nxp_driver = {
        .driver = {
                .name = "usb-ohci",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(usb_hcd_nxp_match),
+               .of_match_table = of_match_ptr(ohci_hcd_nxp_match),
        },
-       .probe = usb_hcd_nxp_probe,
-       .remove = usb_hcd_nxp_remove,
+       .probe = ohci_hcd_nxp_probe,
+       .remove = ohci_hcd_nxp_remove,
 };
 
+static int __init ohci_nxp_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_nxp_hc_driver, NULL);
+       return platform_driver_register(&ohci_hcd_nxp_driver);
+}
+module_init(ohci_nxp_init);
+
+static void __exit ohci_nxp_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_nxp_driver);
+}
+module_exit(ohci_nxp_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL v2");
index 31d3a12eb4867e56872f71cbea9f61ef1431dba5..f253214741ba014c35e28fa498894f8c5cac7847 100644 (file)
  * This file is licenced under the GPL.
  */
 
-#include <linux/signal.h>
-#include <linux/jiffies.h>
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/otg.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
 
 #include <asm/io.h>
 #include <asm/mach-types.h>
 #define OMAP1510_LB_MMU_RAM_H  0xfffec234
 #define OMAP1510_LB_MMU_RAM_L  0xfffec238
 
-
-#ifndef CONFIG_ARCH_OMAP
-#error "This file is OMAP bus glue.  CONFIG_OMAP must be defined."
-#endif
+#define DRIVER_DESC "OHCI OMAP driver"
 
 #ifdef CONFIG_TPS65010
 #include <linux/i2c/tps65010.h>
@@ -68,8 +74,9 @@ extern int ocpi_enable(void);
 
 static struct clk *usb_host_ck;
 static struct clk *usb_dc_ck;
-static int host_enabled;
-static int host_initialized;
+
+static const char hcd_name[] = "ohci-omap";
+static struct hc_driver __read_mostly ohci_omap_hc_driver;
 
 static void omap_ohci_clock_power(int on)
 {
@@ -188,7 +195,7 @@ static void start_hnp(struct ohci_hcd *ohci)
 
 /*-------------------------------------------------------------------------*/
 
-static int ohci_omap_init(struct usb_hcd *hcd)
+static int ohci_omap_reset(struct usb_hcd *hcd)
 {
        struct ohci_hcd         *ohci = hcd_to_ohci(hcd);
        struct omap_usb_config  *config = dev_get_platdata(hcd->self.controller);
@@ -198,9 +205,9 @@ static int ohci_omap_init(struct usb_hcd *hcd)
        dev_dbg(hcd->self.controller, "starting USB Controller\n");
 
        if (config->otg) {
-               ohci_to_hcd(ohci)->self.otg_port = config->otg;
+               hcd->self.otg_port = config->otg;
                /* default/minimum OTG power budget:  8 mA */
-               ohci_to_hcd(ohci)->power_budget = 8;
+               hcd->power_budget = 8;
        }
 
        /* boards can use OTG transceivers in non-OTG modes */
@@ -238,9 +245,15 @@ static int ohci_omap_init(struct usb_hcd *hcd)
                omap_1510_local_bus_init();
        }
 
-       if ((ret = ohci_init(ohci)) < 0)
+       ret = ohci_setup(hcd);
+       if (ret < 0)
                return ret;
 
+       if (config->otg || config->rwc) {
+               ohci->hc_control = OHCI_CTRL_RWC;
+               writel(OHCI_CTRL_RWC, &ohci->regs->control);
+       }
+
        /* board-specific power switching and overcurrent support */
        if (machine_is_omap_osk() || machine_is_omap_innovator()) {
                u32     rh = roothub_a (ohci);
@@ -281,14 +294,6 @@ static int ohci_omap_init(struct usb_hcd *hcd)
        return 0;
 }
 
-static void ohci_omap_stop(struct usb_hcd *hcd)
-{
-       dev_dbg(hcd->self.controller, "stopping USB Controller\n");
-       ohci_stop(hcd);
-       omap_ohci_clock_power(0);
-}
-
-
 /*-------------------------------------------------------------------------*/
 
 /**
@@ -304,7 +309,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
 {
        int retval, irq;
        struct usb_hcd *hcd = 0;
-       struct ohci_hcd *ohci;
 
        if (pdev->num_resources != 2) {
                printk(KERN_ERR "hcd probe: invalid num_resources: %i\n",
@@ -354,12 +358,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
                goto err2;
        }
 
-       ohci = hcd_to_ohci(hcd);
-       ohci_hcd_init(ohci);
-
-       host_initialized = 0;
-       host_enabled = 1;
-
        irq = platform_get_irq(pdev, 0);
        if (irq < 0) {
                retval = -ENXIO;
@@ -369,11 +367,6 @@ static int usb_hcd_omap_probe (const struct hc_driver *driver,
        if (retval)
                goto err3;
 
-       host_initialized = 1;
-
-       if (!host_enabled)
-               omap_ohci_clock_power(0);
-
        return 0;
 err3:
        iounmap(hcd->regs);
@@ -402,7 +395,9 @@ err0:
 static inline void
 usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 {
+       dev_dbg(hcd->self.controller, "stopping USB Controller\n");
        usb_remove_hcd(hcd);
+       omap_ohci_clock_power(0);
        if (!IS_ERR_OR_NULL(hcd->phy)) {
                (void) otg_set_host(hcd->phy->otg, 0);
                usb_put_phy(hcd->phy);
@@ -418,76 +413,6 @@ usb_hcd_omap_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 
 /*-------------------------------------------------------------------------*/
 
-static int
-ohci_omap_start (struct usb_hcd *hcd)
-{
-       struct omap_usb_config *config;
-       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-       int             ret;
-
-       if (!host_enabled)
-               return 0;
-       config = dev_get_platdata(hcd->self.controller);
-       if (config->otg || config->rwc) {
-               ohci->hc_control = OHCI_CTRL_RWC;
-               writel(OHCI_CTRL_RWC, &ohci->regs->control);
-       }
-
-       if ((ret = ohci_run (ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop (hcd);
-               return ret;
-       }
-       return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static const struct hc_driver ohci_omap_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "OMAP OHCI",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset =                ohci_omap_init,
-       .start =                ohci_omap_start,
-       .stop =                 ohci_omap_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_hub_status_data,
-       .hub_control =          ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
-
 static int ohci_hcd_omap_drv_probe(struct platform_device *dev)
 {
        return usb_hcd_omap_probe(&ohci_omap_hc_driver, dev);
@@ -506,16 +431,23 @@ static int ohci_hcd_omap_drv_remove(struct platform_device *dev)
 
 #ifdef CONFIG_PM
 
-static int ohci_omap_suspend(struct platform_device *dev, pm_message_t message)
+static int ohci_omap_suspend(struct platform_device *pdev, pm_message_t message)
 {
-       struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(dev));
+       struct usb_hcd *hcd = platform_get_drvdata(pdev);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       bool do_wakeup = device_may_wakeup(&pdev->dev);
+       int ret;
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
+
        omap_ohci_clock_power(0);
-       return 0;
+       return ret;
 }
 
 static int ohci_omap_resume(struct platform_device *dev)
@@ -553,4 +485,29 @@ static struct platform_driver ohci_hcd_omap_driver = {
        },
 };
 
+static const struct ohci_driver_overrides omap_overrides __initconst = {
+       .product_desc   = "OMAP OHCI",
+       .reset          = ohci_omap_reset
+};
+
+static int __init ohci_omap_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_omap_hc_driver, &omap_overrides);
+       return platform_driver_register(&ohci_hcd_omap_driver);
+}
+module_init(ohci_omap_init);
+
+static void __exit ohci_omap_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_omap_driver);
+}
+module_exit(ohci_omap_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_ALIAS("platform:ohci");
+MODULE_LICENSE("GPL");
index db9bd6bc97b99106a80093d97262401c6862e167..21457417a85639051e566068566bfbe53937670d 100644 (file)
  *     - add kernel-doc
  */
 
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/usb/otg.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/dma-mapping.h>
-
-/*-------------------------------------------------------------------------*/
-
-static int ohci_omap3_init(struct usb_hcd *hcd)
-{
-       dev_dbg(hcd->self.controller, "starting OHCI controller\n");
-
-       return ohci_init(hcd_to_ohci(hcd));
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int ohci_omap3_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       /*
-        * RemoteWakeupConnected has to be set explicitly before
-        * calling ohci_run. The reset value of RWC is 0.
-        */
-       ohci->hc_control = OHCI_CTRL_RWC;
-       writel(OHCI_CTRL_RWC, &ohci->regs->control);
-
-       ret = ohci_run(ohci);
-
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop(hcd);
-       }
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
 
-       return ret;
-}
+#include "ohci.h"
 
-/*-------------------------------------------------------------------------*/
+#define DRIVER_DESC "OHCI OMAP3 driver"
 
-static const struct hc_driver ohci_omap3_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "OMAP3 OHCI Host Controller",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .reset =                ohci_omap3_init,
-       .start =                ohci_omap3_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_hub_status_data,
-       .hub_control =          ohci_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
+static const char hcd_name[] = "ohci-omap3";
+static struct hc_driver __read_mostly ohci_omap3_hc_driver;
 
 /*
  * configure so an HC device and id are always provided
@@ -129,6 +61,7 @@ static const struct hc_driver ohci_omap3_hc_driver = {
 static int ohci_hcd_omap3_probe(struct platform_device *pdev)
 {
        struct device           *dev = &pdev->dev;
+       struct ohci_hcd         *ohci;
        struct usb_hcd          *hcd = NULL;
        void __iomem            *regs = NULL;
        struct resource         *res;
@@ -185,7 +118,12 @@ static int ohci_hcd_omap3_probe(struct platform_device *pdev)
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
 
-       ohci_hcd_init(hcd_to_ohci(hcd));
+       ohci = hcd_to_ohci(hcd);
+       /*
+        * RemoteWakeupConnected has to be set explicitly before
+        * calling ohci_run. The reset value of RWC is 0.
+        */
+       ohci->hc_control = OHCI_CTRL_RWC;
 
        ret = usb_add_hcd(hcd, irq, 0);
        if (ret) {
@@ -248,5 +186,25 @@ static struct platform_driver ohci_hcd_omap3_driver = {
        },
 };
 
+static int __init ohci_omap3_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_omap3_hc_driver, NULL);
+       return platform_driver_register(&ohci_hcd_omap3_driver);
+}
+module_init(ohci_omap3_init);
+
+static void __exit ohci_omap3_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_omap3_driver);
+}
+module_exit(ohci_omap3_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_ALIAS("platform:ohci-omap3");
 MODULE_AUTHOR("Anand Gadiyar <gadiyar@ti.com>");
+MODULE_LICENSE("GPL");
index ec337c2bd5e0b5294efa467691ef1807742aa1bc..90879e9ccbec302e8c5272d45e4847009b3730c4 100644 (file)
@@ -150,28 +150,16 @@ static int ohci_quirk_nec(struct usb_hcd *hcd)
 static int ohci_quirk_amd700(struct usb_hcd *hcd)
 {
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       struct pci_dev *amd_smbus_dev;
-       u8 rev;
 
        if (usb_amd_find_chipset_info())
                ohci->flags |= OHCI_QUIRK_AMD_PLL;
 
-       amd_smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
-                       PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
-       if (!amd_smbus_dev)
-               return 0;
-
-       rev = amd_smbus_dev->revision;
-
        /* SB800 needs pre-fetch fix */
-       if ((rev >= 0x40) && (rev <= 0x4f)) {
+       if (usb_amd_prefetch_quirk()) {
                ohci->flags |= OHCI_QUIRK_AMD_PREFETCH;
                ohci_dbg(ohci, "enabled AMD prefetch quirk\n");
        }
 
-       pci_dev_put(amd_smbus_dev);
-       amd_smbus_dev = NULL;
-
        return 0;
 }
 
@@ -323,3 +311,4 @@ module_exit(ohci_pci_cleanup);
 
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_LICENSE("GPL");
+MODULE_SOFTDEP("pre: ehci_pci");
index a4c6410f0ed494bfb6fb492849d1ed5f3b4fb1d2..f351ff5b171f752f121119cdc1e51d5846d4d6f0 100644 (file)
@@ -139,14 +139,21 @@ static int ohci_platform_remove(struct platform_device *dev)
 
 static int ohci_platform_suspend(struct device *dev)
 {
-       struct usb_ohci_pdata *pdata = dev_get_platdata(dev);
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct usb_ohci_pdata *pdata = dev->platform_data;
        struct platform_device *pdev =
                container_of(dev, struct platform_device, dev);
+       bool do_wakeup = device_may_wakeup(dev);
+       int ret;
+
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
 
        if (pdata->power_suspend)
                pdata->power_suspend(pdev);
 
-       return 0;
+       return ret;
 }
 
 static int ohci_platform_resume(struct device *dev)
index b64949bc43e2ebacc1620a76faf6d9db2935b16e..e89ac4d4b87e5be4d4a134e389dbdfa20931eb9c 100644 (file)
  * This file is licenced under the GPL.
  */
 
-#include <linux/device.h>
-#include <linux/signal.h>
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of_platform.h>
 #include <linux/of_gpio.h>
-#include <mach/hardware.h>
 #include <linux/platform_data/usb-ohci-pxa27x.h>
 #include <linux/platform_data/usb-pxa3xx-ulpi.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/otg.h>
+
+#include <mach/hardware.h>
+
+#include "ohci.h"
+
+#define DRIVER_DESC "OHCI PXA27x/PXA3x driver"
 
 /*
  * UHC: USB Host Controller (OHCI-like) register definitions
 
 #define PXA_UHC_MAX_PORTNUM    3
 
-struct pxa27x_ohci {
-       /* must be 1st member here for hcd_to_ohci() to work */
-       struct ohci_hcd ohci;
+static const char hcd_name[] = "ohci-pxa27x";
 
-       struct device   *dev;
+static struct hc_driver __read_mostly ohci_pxa27x_hc_driver;
+
+struct pxa27x_ohci {
        struct clk      *clk;
        void __iomem    *mmio_base;
 };
 
-#define to_pxa27x_ohci(hcd)    (struct pxa27x_ohci *)hcd_to_ohci(hcd)
+#define to_pxa27x_ohci(hcd)    (struct pxa27x_ohci *)(hcd_to_ohci(hcd)->priv)
 
 /*
   PMM_NPS_MODE -- PMM Non-power switching mode
@@ -122,10 +133,10 @@ struct pxa27x_ohci {
   PMM_PERPORT_MODE -- PMM per port switching mode
       Ports are powered individually.
  */
-static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode)
+static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *pxa_ohci, int mode)
 {
-       uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA);
-       uint32_t uhcrhdb = __raw_readl(ohci->mmio_base + UHCRHDB);
+       uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA);
+       uint32_t uhcrhdb = __raw_readl(pxa_ohci->mmio_base + UHCRHDB);
 
        switch (mode) {
        case PMM_NPS_MODE:
@@ -149,20 +160,18 @@ static int pxa27x_ohci_select_pmm(struct pxa27x_ohci *ohci, int mode)
                uhcrhda |= RH_A_NPS;
        }
 
-       __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA);
-       __raw_writel(uhcrhdb, ohci->mmio_base + UHCRHDB);
+       __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA);
+       __raw_writel(uhcrhdb, pxa_ohci->mmio_base + UHCRHDB);
        return 0;
 }
 
-extern int usb_disabled(void);
-
 /*-------------------------------------------------------------------------*/
 
-static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci,
+static inline void pxa27x_setup_hc(struct pxa27x_ohci *pxa_ohci,
                                   struct pxaohci_platform_data *inf)
 {
-       uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR);
-       uint32_t uhcrhda = __raw_readl(ohci->mmio_base + UHCRHDA);
+       uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR);
+       uint32_t uhcrhda = __raw_readl(pxa_ohci->mmio_base + UHCRHDA);
 
        if (inf->flags & ENABLE_PORT1)
                uhchr &= ~UHCHR_SSEP1;
@@ -194,17 +203,17 @@ static inline void pxa27x_setup_hc(struct pxa27x_ohci *ohci,
                uhcrhda |= UHCRHDA_POTPGT(inf->power_on_delay / 2);
        }
 
-       __raw_writel(uhchr, ohci->mmio_base + UHCHR);
-       __raw_writel(uhcrhda, ohci->mmio_base + UHCRHDA);
+       __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
+       __raw_writel(uhcrhda, pxa_ohci->mmio_base + UHCRHDA);
 }
 
-static inline void pxa27x_reset_hc(struct pxa27x_ohci *ohci)
+static inline void pxa27x_reset_hc(struct pxa27x_ohci *pxa_ohci)
 {
-       uint32_t uhchr = __raw_readl(ohci->mmio_base + UHCHR);
+       uint32_t uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR);
 
-       __raw_writel(uhchr | UHCHR_FHR, ohci->mmio_base + UHCHR);
+       __raw_writel(uhchr | UHCHR_FHR, pxa_ohci->mmio_base + UHCHR);
        udelay(11);
-       __raw_writel(uhchr & ~UHCHR_FHR, ohci->mmio_base + UHCHR);
+       __raw_writel(uhchr & ~UHCHR_FHR, pxa_ohci->mmio_base + UHCHR);
 }
 
 #ifdef CONFIG_PXA27x
@@ -213,25 +222,26 @@ extern void pxa27x_clear_otgph(void);
 #define pxa27x_clear_otgph()   do {} while (0)
 #endif
 
-static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev)
+static int pxa27x_start_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev)
 {
        int retval = 0;
        struct pxaohci_platform_data *inf;
        uint32_t uhchr;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
 
        inf = dev_get_platdata(dev);
 
-       clk_prepare_enable(ohci->clk);
+       clk_prepare_enable(pxa_ohci->clk);
 
-       pxa27x_reset_hc(ohci);
+       pxa27x_reset_hc(pxa_ohci);
 
-       uhchr = __raw_readl(ohci->mmio_base + UHCHR) | UHCHR_FSBIR;
-       __raw_writel(uhchr, ohci->mmio_base + UHCHR);
+       uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) | UHCHR_FSBIR;
+       __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
 
-       while (__raw_readl(ohci->mmio_base + UHCHR) & UHCHR_FSBIR)
+       while (__raw_readl(pxa_ohci->mmio_base + UHCHR) & UHCHR_FSBIR)
                cpu_relax();
 
-       pxa27x_setup_hc(ohci, inf);
+       pxa27x_setup_hc(pxa_ohci, inf);
 
        if (inf->init)
                retval = inf->init(dev);
@@ -240,38 +250,39 @@ static int pxa27x_start_hc(struct pxa27x_ohci *ohci, struct device *dev)
                return retval;
 
        if (cpu_is_pxa3xx())
-               pxa3xx_u2d_start_hc(&ohci_to_hcd(&ohci->ohci)->self);
+               pxa3xx_u2d_start_hc(&hcd->self);
 
-       uhchr = __raw_readl(ohci->mmio_base + UHCHR) & ~UHCHR_SSE;
-       __raw_writel(uhchr, ohci->mmio_base + UHCHR);
-       __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, ohci->mmio_base + UHCHIE);
+       uhchr = __raw_readl(pxa_ohci->mmio_base + UHCHR) & ~UHCHR_SSE;
+       __raw_writel(uhchr, pxa_ohci->mmio_base + UHCHR);
+       __raw_writel(UHCHIE_UPRIE | UHCHIE_RWIE, pxa_ohci->mmio_base + UHCHIE);
 
        /* Clear any OTG Pin Hold */
        pxa27x_clear_otgph();
        return 0;
 }
 
-static void pxa27x_stop_hc(struct pxa27x_ohci *ohci, struct device *dev)
+static void pxa27x_stop_hc(struct pxa27x_ohci *pxa_ohci, struct device *dev)
 {
        struct pxaohci_platform_data *inf;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
        uint32_t uhccoms;
 
        inf = dev_get_platdata(dev);
 
        if (cpu_is_pxa3xx())
-               pxa3xx_u2d_stop_hc(&ohci_to_hcd(&ohci->ohci)->self);
+               pxa3xx_u2d_stop_hc(&hcd->self);
 
        if (inf->exit)
                inf->exit(dev);
 
-       pxa27x_reset_hc(ohci);
+       pxa27x_reset_hc(pxa_ohci);
 
        /* Host Controller Reset */
-       uhccoms = __raw_readl(ohci->mmio_base + UHCCOMS) | 0x01;
-       __raw_writel(uhccoms, ohci->mmio_base + UHCCOMS);
+       uhccoms = __raw_readl(pxa_ohci->mmio_base + UHCCOMS) | 0x01;
+       __raw_writel(uhccoms, pxa_ohci->mmio_base + UHCCOMS);
        udelay(10);
 
-       clk_disable_unprepare(ohci->clk);
+       clk_disable_unprepare(pxa_ohci->clk);
 }
 
 #ifdef CONFIG_OF
@@ -356,7 +367,8 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
        int retval, irq;
        struct usb_hcd *hcd;
        struct pxaohci_platform_data *inf;
-       struct pxa27x_ohci *ohci;
+       struct pxa27x_ohci *pxa_ohci;
+       struct ohci_hcd *ohci;
        struct resource *r;
        struct clk *usb_clk;
 
@@ -409,29 +421,31 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
        }
 
        /* initialize "struct pxa27x_ohci" */
-       ohci = (struct pxa27x_ohci *)hcd_to_ohci(hcd);
-       ohci->dev = &pdev->dev;
-       ohci->clk = usb_clk;
-       ohci->mmio_base = (void __iomem *)hcd->regs;
+       pxa_ohci = to_pxa27x_ohci(hcd);
+       pxa_ohci->clk = usb_clk;
+       pxa_ohci->mmio_base = (void __iomem *)hcd->regs;
 
-       if ((retval = pxa27x_start_hc(ohci, &pdev->dev)) < 0) {
+       retval = pxa27x_start_hc(pxa_ohci, &pdev->dev);
+       if (retval < 0) {
                pr_debug("pxa27x_start_hc failed");
                goto err3;
        }
 
        /* Select Power Management Mode */
-       pxa27x_ohci_select_pmm(ohci, inf->port_mode);
+       pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode);
 
        if (inf->power_budget)
                hcd->power_budget = inf->power_budget;
 
-       ohci_hcd_init(hcd_to_ohci(hcd));
+       /* The value of NDP in roothub_a is incorrect on this hardware */
+       ohci = hcd_to_ohci(hcd);
+       ohci->num_ports = 3;
 
        retval = usb_add_hcd(hcd, irq, 0);
        if (retval == 0)
                return retval;
 
-       pxa27x_stop_hc(ohci, &pdev->dev);
+       pxa27x_stop_hc(pxa_ohci, &pdev->dev);
  err3:
        iounmap(hcd->regs);
  err2:
@@ -459,88 +473,18 @@ int usb_hcd_pxa27x_probe (const struct hc_driver *driver, struct platform_device
  */
 void usb_hcd_pxa27x_remove (struct usb_hcd *hcd, struct platform_device *pdev)
 {
-       struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);
+       struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
 
        usb_remove_hcd(hcd);
-       pxa27x_stop_hc(ohci, &pdev->dev);
+       pxa27x_stop_hc(pxa_ohci, &pdev->dev);
        iounmap(hcd->regs);
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+       clk_put(pxa_ohci->clk);
        usb_put_hcd(hcd);
-       clk_put(ohci->clk);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-ohci_pxa27x_start (struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci (hcd);
-       int             ret;
-
-       ohci_dbg (ohci, "ohci_pxa27x_start, ohci:%p", ohci);
-
-       /* The value of NDP in roothub_a is incorrect on this hardware */
-       ohci->num_ports = 3;
-
-       if ((ret = ohci_init(ohci)) < 0)
-               return ret;
-
-       if ((ret = ohci_run (ohci)) < 0) {
-               dev_err(hcd->self.controller, "can't start %s",
-                       hcd->self.bus_name);
-               ohci_stop (hcd);
-               return ret;
-       }
-
-       return 0;
 }
 
 /*-------------------------------------------------------------------------*/
 
-static const struct hc_driver ohci_pxa27x_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "PXA27x OHCI",
-       .hcd_priv_size =        sizeof(struct pxa27x_ohci),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .start =                ohci_pxa27x_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_hub_status_data,
-       .hub_control =          ohci_hub_control,
-#ifdef  CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/*-------------------------------------------------------------------------*/
-
 static int ohci_hcd_pxa27x_drv_probe(struct platform_device *pdev)
 {
        pr_debug ("In ohci_hcd_pxa27x_drv_probe");
@@ -563,32 +507,42 @@ static int ohci_hcd_pxa27x_drv_remove(struct platform_device *pdev)
 static int ohci_hcd_pxa27x_drv_suspend(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);
+       struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       bool do_wakeup = device_may_wakeup(dev);
+       int ret;
 
-       if (time_before(jiffies, ohci->ohci.next_statechange))
+
+       if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
-       ohci->ohci.next_statechange = jiffies;
+       ohci->next_statechange = jiffies;
 
-       pxa27x_stop_hc(ohci, dev);
-       return 0;
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
+
+       pxa27x_stop_hc(pxa_ohci, dev);
+       return ret;
 }
 
 static int ohci_hcd_pxa27x_drv_resume(struct device *dev)
 {
        struct usb_hcd *hcd = dev_get_drvdata(dev);
-       struct pxa27x_ohci *ohci = to_pxa27x_ohci(hcd);
+       struct pxa27x_ohci *pxa_ohci = to_pxa27x_ohci(hcd);
        struct pxaohci_platform_data *inf = dev_get_platdata(dev);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
        int status;
 
-       if (time_before(jiffies, ohci->ohci.next_statechange))
+       if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
-       ohci->ohci.next_statechange = jiffies;
+       ohci->next_statechange = jiffies;
 
-       if ((status = pxa27x_start_hc(ohci, dev)) < 0)
+       status = pxa27x_start_hc(pxa_ohci, dev);
+       if (status < 0)
                return status;
 
        /* Select Power Management Mode */
-       pxa27x_ohci_select_pmm(ohci, inf->port_mode);
+       pxa27x_ohci_select_pmm(pxa_ohci, inf->port_mode);
 
        ohci_resume(hcd, false);
        return 0;
@@ -600,9 +554,6 @@ static const struct dev_pm_ops ohci_hcd_pxa27x_pm_ops = {
 };
 #endif
 
-/* work with hotplug and coldplug */
-MODULE_ALIAS("platform:pxa27x-ohci");
-
 static struct platform_driver ohci_hcd_pxa27x_driver = {
        .probe          = ohci_hcd_pxa27x_drv_probe,
        .remove         = ohci_hcd_pxa27x_drv_remove,
@@ -617,3 +568,27 @@ static struct platform_driver ohci_hcd_pxa27x_driver = {
        },
 };
 
+static const struct ohci_driver_overrides pxa27x_overrides __initconst = {
+       .extra_priv_size =      sizeof(struct pxa27x_ohci),
+};
+
+static int __init ohci_pxa27x_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&ohci_pxa27x_hc_driver, &pxa27x_overrides);
+       return platform_driver_register(&ohci_hcd_pxa27x_driver);
+}
+module_init(ohci_pxa27x_init);
+
+static void __exit ohci_pxa27x_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_pxa27x_driver);
+}
+module_exit(ohci_pxa27x_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:pxa27x-ohci");
index 4919afa4125e36eaeed297277da4631cb9cb5c7b..be3429e08d909a58b3b2d3fb9923ca592d4eabc4 100644 (file)
  * This file is licenced under the GPL.
 */
 
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/platform_data/usb-ohci-s3c2410.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
+
 
 #define valid_port(idx) ((idx) == 1 || (idx) == 2)
 
 /* clock device associated with the hcd */
 
+
+#define DRIVER_DESC "OHCI S3C2410 driver"
+
+static const char hcd_name[] = "ohci-s3c2410";
+
 static struct clk *clk;
 static struct clk *usb_clk;
 
 /* forward definitions */
 
+static int (*orig_ohci_hub_control)(struct usb_hcd  *hcd, u16 typeReq,
+                       u16 wValue, u16 wIndex, char *buf, u16 wLength);
+static int (*orig_ohci_hub_status_data)(struct usb_hcd *hcd, char *buf);
+
 static void s3c2410_hcd_oc(struct s3c2410_hcd_info *info, int port_oc);
 
 /* conversion functions */
@@ -93,7 +110,7 @@ ohci_s3c2410_hub_status_data(struct usb_hcd *hcd, char *buf)
        int orig;
        int portno;
 
-       orig  = ohci_hub_status_data(hcd, buf);
+       orig = orig_ohci_hub_status_data(hcd, buf);
 
        if (info == NULL)
                return orig;
@@ -164,7 +181,7 @@ static int ohci_s3c2410_hub_control(
         * process the request straight away and exit */
 
        if (info == NULL) {
-               ret = ohci_hub_control(hcd, typeReq, wValue,
+               ret = orig_ohci_hub_control(hcd, typeReq, wValue,
                                       wIndex, buf, wLength);
                goto out;
        }
@@ -214,7 +231,7 @@ static int ohci_s3c2410_hub_control(
                break;
        }
 
-       ret = ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+       ret = orig_ohci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
        if (ret)
                goto out;
 
@@ -374,8 +391,6 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
 
        s3c2410_start_hc(dev, hcd);
 
-       ohci_hcd_init(hcd_to_ohci(hcd));
-
        retval = usb_add_hcd(hcd, dev->resource[1].start, 0);
        if (retval != 0)
                goto err_ioremap;
@@ -392,71 +407,7 @@ static int usb_hcd_s3c2410_probe(const struct hc_driver *driver,
 
 /*-------------------------------------------------------------------------*/
 
-static int
-ohci_s3c2410_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       ret = ohci_init(ohci);
-       if (ret < 0)
-               return ret;
-
-       ret = ohci_run(ohci);
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start %s\n",
-                       hcd->self.bus_name);
-               ohci_stop(hcd);
-               return ret;
-       }
-
-       return 0;
-}
-
-
-static const struct hc_driver ohci_s3c2410_hc_driver = {
-       .description =          hcd_name,
-       .product_desc =         "S3C24XX OHCI",
-       .hcd_priv_size =        sizeof(struct ohci_hcd),
-
-       /*
-        * generic hardware linkage
-        */
-       .irq =                  ohci_irq,
-       .flags =                HCD_USB11 | HCD_MEMORY,
-
-       /*
-        * basic lifecycle operations
-        */
-       .start =                ohci_s3c2410_start,
-       .stop =                 ohci_stop,
-       .shutdown =             ohci_shutdown,
-
-       /*
-        * managing i/o requests and associated device resources
-        */
-       .urb_enqueue =          ohci_urb_enqueue,
-       .urb_dequeue =          ohci_urb_dequeue,
-       .endpoint_disable =     ohci_endpoint_disable,
-
-       /*
-        * scheduling support
-        */
-       .get_frame_number =     ohci_get_frame,
-
-       /*
-        * root hub support
-        */
-       .hub_status_data =      ohci_s3c2410_hub_status_data,
-       .hub_control =          ohci_s3c2410_hub_control,
-#ifdef CONFIG_PM
-       .bus_suspend =          ohci_bus_suspend,
-       .bus_resume =           ohci_bus_resume,
-#endif
-       .start_port_reset =     ohci_start_port_reset,
-};
-
-/* device driver */
+static struct hc_driver __read_mostly ohci_s3c2410_hc_driver;
 
 static int ohci_hcd_s3c2410_drv_probe(struct platform_device *pdev)
 {
@@ -533,4 +484,39 @@ static struct platform_driver ohci_hcd_s3c2410_driver = {
        },
 };
 
+static int __init ohci_s3c2410_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+       ohci_init_driver(&ohci_s3c2410_hc_driver, NULL);
+
+       /*
+        * The Samsung HW has some unusual quirks, which require
+        * Sumsung-specific workarounds. We override certain hc_driver
+        * functions here to achieve that. We explicitly do not enhance
+        * ohci_driver_overrides to allow this more easily, since this
+        * is an unusual case, and we don't want to encourage others to
+        * override these functions by making it too easy.
+        */
+
+       orig_ohci_hub_control = ohci_s3c2410_hc_driver.hub_control;
+       orig_ohci_hub_status_data = ohci_s3c2410_hc_driver.hub_status_data;
+
+       ohci_s3c2410_hc_driver.hub_status_data  = ohci_s3c2410_hub_status_data;
+       ohci_s3c2410_hc_driver.hub_control      = ohci_s3c2410_hub_control;
+
+       return platform_driver_register(&ohci_hcd_s3c2410_driver);
+}
+module_init(ohci_s3c2410_init);
+
+static void __exit ohci_s3c2410_cleanup(void)
+{
+       platform_driver_unregister(&ohci_hcd_s3c2410_driver);
+}
+module_exit(ohci_s3c2410_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
 MODULE_ALIAS("platform:s3c2410-ohci");
index d479d5ddab8853b9844f29ffa7e15b1ca0beaca7..2a5de5fecd8f5ba3a05a449485dd3f7fae7b39b2 100644 (file)
@@ -216,14 +216,21 @@ static int ohci_hcd_sm501_drv_remove(struct platform_device *pdev)
 static int ohci_sm501_suspend(struct platform_device *pdev, pm_message_t msg)
 {
        struct device *dev = &pdev->dev;
-       struct ohci_hcd *ohci = hcd_to_ohci(platform_get_drvdata(pdev));
+       struct usb_hcd  *hcd = platform_get_drvdata(pdev);
+       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
+       bool do_wakeup = device_may_wakeup(dev);
+       int ret;
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
+       ret = ohci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
+
        sm501_unit_power(dev->parent, SM501_GATE_USB_HOST, 0);
-       return 0;
+       return ret;
 }
 
 static int ohci_sm501_resume(struct platform_device *pdev)
index 075bb5e9b43fd0c4df8ddf63cfceffcc5ba1c81f..6b02107d281d17cb5db7bbfc34c845c81b70f392 100644 (file)
 * warranty of any kind, whether express or implied.
 */
 
-#include <linux/signal.h>
-#include <linux/platform_device.h>
 #include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/signal.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+
+#include "ohci.h"
 
+#define DRIVER_DESC "OHCI SPEAr driver"
+
+static const char hcd_name[] = "SPEAr-ohci";
 struct spear_ohci {
-       struct ohci_hcd ohci;
        struct clk *clk;
 };
 
-#define to_spear_ohci(hcd)     (struct spear_ohci *)hcd_to_ohci(hcd)
-
-static void spear_start_ohci(struct spear_ohci *ohci)
-{
-       clk_prepare_enable(ohci->clk);
-}
-
-static void spear_stop_ohci(struct spear_ohci *ohci)
-{
-       clk_disable_unprepare(ohci->clk);
-}
-
-static int ohci_spear_start(struct usb_hcd *hcd)
-{
-       struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       int ret;
-
-       ret = ohci_init(ohci);
-       if (ret < 0)
-               return ret;
-       ohci->regs = hcd->regs;
-
-       ret = ohci_run(ohci);
-       if (ret < 0) {
-               dev_err(hcd->self.controller, "can't start\n");
-               ohci_stop(hcd);
-               return ret;
-       }
-
-       create_debug_files(ohci);
-
-#ifdef DEBUG
-       ohci_dump(ohci, 1);
-#endif
-       return 0;
-}
-
-static const struct hc_driver ohci_spear_hc_driver = {
-       .description            = hcd_name,
-       .product_desc           = "SPEAr OHCI",
-       .hcd_priv_size          = sizeof(struct spear_ohci),
-
-       /* generic hardware linkage */
-       .irq                    = ohci_irq,
-       .flags                  = HCD_USB11 | HCD_MEMORY,
-
-       /* basic lifecycle operations */
-       .start                  = ohci_spear_start,
-       .stop                   = ohci_stop,
-       .shutdown               = ohci_shutdown,
-#ifdef CONFIG_PM
-       .bus_suspend            = ohci_bus_suspend,
-       .bus_resume             = ohci_bus_resume,
-#endif
-
-       /* managing i/o requests and associated device resources */
-       .urb_enqueue            = ohci_urb_enqueue,
-       .urb_dequeue            = ohci_urb_dequeue,
-       .endpoint_disable       = ohci_endpoint_disable,
-
-       /* scheduling support */
-       .get_frame_number       = ohci_get_frame,
+#define to_spear_ohci(hcd)     (struct spear_ohci *)(hcd_to_ohci(hcd)->priv)
 
-       /* root hub support */
-       .hub_status_data        = ohci_hub_status_data,
-       .hub_control            = ohci_hub_control,
-
-       .start_port_reset       = ohci_start_port_reset,
-};
+static struct hc_driver __read_mostly ohci_spear_hc_driver;
 
 static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
 {
        const struct hc_driver *driver = &ohci_spear_hc_driver;
+       struct ohci_hcd *ohci;
        struct usb_hcd *hcd = NULL;
        struct clk *usbh_clk;
-       struct spear_ohci *ohci_p;
+       struct spear_ohci *sohci_p;
        struct resource *res;
        int retval, irq;
 
@@ -150,16 +95,18 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
                goto err_put_hcd;
        }
 
-       ohci_p = (struct spear_ohci *)hcd_to_ohci(hcd);
-       ohci_p->clk = usbh_clk;
-       spear_start_ohci(ohci_p);
-       ohci_hcd_init(hcd_to_ohci(hcd));
+       sohci_p = to_spear_ohci(hcd);
+       sohci_p->clk = usbh_clk;
+
+       clk_prepare_enable(sohci_p->clk);
+
+       ohci = hcd_to_ohci(hcd);
 
        retval = usb_add_hcd(hcd, platform_get_irq(pdev, 0), 0);
        if (retval == 0)
                return retval;
 
-       spear_stop_ohci(ohci_p);
+       clk_disable_unprepare(sohci_p->clk);
 err_put_hcd:
        usb_put_hcd(hcd);
 fail:
@@ -171,11 +118,11 @@ fail:
 static int spear_ohci_hcd_drv_remove(struct platform_device *pdev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(pdev);
-       struct spear_ohci *ohci_p = to_spear_ohci(hcd);
+       struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 
        usb_remove_hcd(hcd);
-       if (ohci_p->clk)
-               spear_stop_ohci(ohci_p);
+       if (sohci_p->clk)
+               clk_disable_unprepare(sohci_p->clk);
 
        usb_put_hcd(hcd);
        return 0;
@@ -187,13 +134,14 @@ static int spear_ohci_hcd_drv_suspend(struct platform_device *dev,
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       struct spear_ohci *ohci_p = to_spear_ohci(hcd);
+       struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
-       spear_stop_ohci(ohci_p);
+       clk_disable_unprepare(sohci_p->clk);
+
        return 0;
 }
 
@@ -201,13 +149,13 @@ static int spear_ohci_hcd_drv_resume(struct platform_device *dev)
 {
        struct usb_hcd *hcd = platform_get_drvdata(dev);
        struct ohci_hcd *ohci = hcd_to_ohci(hcd);
-       struct spear_ohci *ohci_p = to_spear_ohci(hcd);
+       struct spear_ohci *sohci_p = to_spear_ohci(hcd);
 
        if (time_before(jiffies, ohci->next_statechange))
                msleep(5);
        ohci->next_statechange = jiffies;
 
-       spear_start_ohci(ohci_p);
+       clk_prepare_enable(sohci_p->clk);
        ohci_resume(hcd, false);
        return 0;
 }
@@ -233,4 +181,28 @@ static struct platform_driver spear_ohci_hcd_driver = {
        },
 };
 
+static const struct ohci_driver_overrides spear_overrides __initconst = {
+       .extra_priv_size = sizeof(struct spear_ohci),
+};
+static int __init ohci_spear_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       pr_info("%s: " DRIVER_DESC "\n", hcd_name);
+
+       ohci_init_driver(&ohci_spear_hc_driver, &spear_overrides);
+       return platform_driver_register(&spear_ohci_hcd_driver);
+}
+module_init(ohci_spear_init);
+
+static void __exit ohci_spear_cleanup(void)
+{
+       platform_driver_unregister(&spear_ohci_hcd_driver);
+}
+module_exit(ohci_spear_cleanup);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Deepak Sikri");
+MODULE_LICENSE("GPL v2");
 MODULE_ALIAS("platform:spear-ohci");
index 08ef2829a7e2b1c06bbd0af51f308884c8ad9a26..dfbdd3aefe98a8079fd6faf3f5713afd74f97fc6 100644 (file)
 #define USB_INTEL_USB3_PSSEN   0xD8
 #define USB_INTEL_USB3PRM      0xDC
 
+/*
+ * amd_chipset_gen values represent AMD different chipset generations
+ */
+enum amd_chipset_gen {
+       NOT_AMD_CHIPSET = 0,
+       AMD_CHIPSET_SB600,
+       AMD_CHIPSET_SB700,
+       AMD_CHIPSET_SB800,
+       AMD_CHIPSET_HUDSON2,
+       AMD_CHIPSET_BOLTON,
+       AMD_CHIPSET_YANGTZE,
+       AMD_CHIPSET_UNKNOWN,
+};
+
+struct amd_chipset_type {
+       enum amd_chipset_gen gen;
+       u8 rev;
+};
+
 static struct amd_chipset_info {
        struct pci_dev  *nb_dev;
        struct pci_dev  *smbus_dev;
        int nb_type;
-       int sb_type;
+       struct amd_chipset_type sb_type;
        int isoc_reqs;
        int probe_count;
        int probe_result;
@@ -91,6 +110,51 @@ static struct amd_chipset_info {
 
 static DEFINE_SPINLOCK(amd_lock);
 
+/*
+ * amd_chipset_sb_type_init - initialize amd chipset southbridge type
+ *
+ * AMD FCH/SB generation and revision is identified by SMBus controller
+ * vendor, device and revision IDs.
+ *
+ * Returns: 1 if it is an AMD chipset, 0 otherwise.
+ */
+static int amd_chipset_sb_type_init(struct amd_chipset_info *pinfo)
+{
+       u8 rev = 0;
+       pinfo->sb_type.gen = AMD_CHIPSET_UNKNOWN;
+
+       pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI,
+                       PCI_DEVICE_ID_ATI_SBX00_SMBUS, NULL);
+       if (pinfo->smbus_dev) {
+               rev = pinfo->smbus_dev->revision;
+               if (rev >= 0x10 && rev <= 0x1f)
+                       pinfo->sb_type.gen = AMD_CHIPSET_SB600;
+               else if (rev >= 0x30 && rev <= 0x3f)
+                       pinfo->sb_type.gen = AMD_CHIPSET_SB700;
+               else if (rev >= 0x40 && rev <= 0x4f)
+                       pinfo->sb_type.gen = AMD_CHIPSET_SB800;
+       } else {
+               pinfo->smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
+                               PCI_DEVICE_ID_AMD_HUDSON2_SMBUS, NULL);
+
+               if (!pinfo->smbus_dev) {
+                       pinfo->sb_type.gen = NOT_AMD_CHIPSET;
+                       return 0;
+               }
+
+               rev = pinfo->smbus_dev->revision;
+               if (rev >= 0x11 && rev <= 0x14)
+                       pinfo->sb_type.gen = AMD_CHIPSET_HUDSON2;
+               else if (rev >= 0x15 && rev <= 0x18)
+                       pinfo->sb_type.gen = AMD_CHIPSET_BOLTON;
+               else if (rev >= 0x39 && rev <= 0x3a)
+                       pinfo->sb_type.gen = AMD_CHIPSET_YANGTZE;
+       }
+
+       pinfo->sb_type.rev = rev;
+       return 1;
+}
+
 void sb800_prefetch(struct device *dev, int on)
 {
        u16 misc;
@@ -106,7 +170,6 @@ EXPORT_SYMBOL_GPL(sb800_prefetch);
 
 int usb_amd_find_chipset_info(void)
 {
-       u8 rev = 0;
        unsigned long flags;
        struct amd_chipset_info info;
        int ret;
@@ -122,27 +185,17 @@ int usb_amd_find_chipset_info(void)
        memset(&info, 0, sizeof(info));
        spin_unlock_irqrestore(&amd_lock, flags);
 
-       info.smbus_dev = pci_get_device(PCI_VENDOR_ID_ATI, 0x4385, NULL);
-       if (info.smbus_dev) {
-               rev = info.smbus_dev->revision;
-               if (rev >= 0x40)
-                       info.sb_type = 1;
-               else if (rev >= 0x30 && rev <= 0x3b)
-                       info.sb_type = 3;
-       } else {
-               info.smbus_dev = pci_get_device(PCI_VENDOR_ID_AMD,
-                                               0x780b, NULL);
-               if (!info.smbus_dev) {
-                       ret = 0;
-                       goto commit;
-               }
-
-               rev = info.smbus_dev->revision;
-               if (rev >= 0x11 && rev <= 0x18)
-                       info.sb_type = 2;
+       if (!amd_chipset_sb_type_init(&info)) {
+               ret = 0;
+               goto commit;
        }
 
-       if (info.sb_type == 0) {
+       /* Below chipset generations needn't enable AMD PLL quirk */
+       if (info.sb_type.gen == AMD_CHIPSET_UNKNOWN ||
+                       info.sb_type.gen == AMD_CHIPSET_SB600 ||
+                       info.sb_type.gen == AMD_CHIPSET_YANGTZE ||
+                       (info.sb_type.gen == AMD_CHIPSET_SB700 &&
+                       info.sb_type.rev > 0x3b)) {
                if (info.smbus_dev) {
                        pci_dev_put(info.smbus_dev);
                        info.smbus_dev = NULL;
@@ -197,6 +250,39 @@ commit:
 }
 EXPORT_SYMBOL_GPL(usb_amd_find_chipset_info);
 
+int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *pdev)
+{
+       /* Make sure amd chipset type has already been initialized */
+       usb_amd_find_chipset_info();
+       if (amd_chipset.sb_type.gen != AMD_CHIPSET_YANGTZE)
+               return 0;
+
+       dev_dbg(&pdev->dev, "QUIRK: Enable AMD remote wakeup fix\n");
+       return 1;
+}
+EXPORT_SYMBOL_GPL(usb_hcd_amd_remote_wakeup_quirk);
+
+bool usb_amd_hang_symptom_quirk(void)
+{
+       u8 rev;
+
+       usb_amd_find_chipset_info();
+       rev = amd_chipset.sb_type.rev;
+       /* SB600 and old version of SB700 have hang symptom bug */
+       return amd_chipset.sb_type.gen == AMD_CHIPSET_SB600 ||
+                       (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 &&
+                        rev >= 0x3a && rev <= 0x3b);
+}
+EXPORT_SYMBOL_GPL(usb_amd_hang_symptom_quirk);
+
+bool usb_amd_prefetch_quirk(void)
+{
+       usb_amd_find_chipset_info();
+       /* SB800 needs pre-fetch fix */
+       return amd_chipset.sb_type.gen == AMD_CHIPSET_SB800;
+}
+EXPORT_SYMBOL_GPL(usb_amd_prefetch_quirk);
+
 /*
  * The hardware normally enables the A-link power management feature, which
  * lets the system lower the power consumption in idle states.
@@ -229,7 +315,9 @@ static void usb_amd_quirk_pll(int disable)
                }
        }
 
-       if (amd_chipset.sb_type == 1 || amd_chipset.sb_type == 2) {
+       if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB800 ||
+                       amd_chipset.sb_type.gen == AMD_CHIPSET_HUDSON2 ||
+                       amd_chipset.sb_type.gen == AMD_CHIPSET_BOLTON) {
                outb_p(AB_REG_BAR_LOW, 0xcd6);
                addr_low = inb_p(0xcd7);
                outb_p(AB_REG_BAR_HIGH, 0xcd6);
@@ -240,7 +328,8 @@ static void usb_amd_quirk_pll(int disable)
                outl_p(0x40, AB_DATA(addr));
                outl_p(0x34, AB_INDX(addr));
                val = inl_p(AB_DATA(addr));
-       } else if (amd_chipset.sb_type == 3) {
+       } else if (amd_chipset.sb_type.gen == AMD_CHIPSET_SB700 &&
+                       amd_chipset.sb_type.rev <= 0x3b) {
                pci_read_config_dword(amd_chipset.smbus_dev,
                                        AB_REG_BAR_SB700, &addr);
                outl(AX_INDXC, AB_INDX(addr));
@@ -353,7 +442,7 @@ void usb_amd_dev_put(void)
        amd_chipset.nb_dev = NULL;
        amd_chipset.smbus_dev = NULL;
        amd_chipset.nb_type = 0;
-       amd_chipset.sb_type = 0;
+       memset(&amd_chipset.sb_type, 0, sizeof(amd_chipset.sb_type));
        amd_chipset.isoc_reqs = 0;
        amd_chipset.probe_result = 0;
 
index ed6700d00fe6fd372f62b575b49075b5ede392f1..638e88f7a28bbce2a3e04df49cee7dedde99229d 100644 (file)
@@ -5,6 +5,8 @@
 void uhci_reset_hc(struct pci_dev *pdev, unsigned long base);
 int uhci_check_and_reset_hc(struct pci_dev *pdev, unsigned long base);
 int usb_amd_find_chipset_info(void);
+bool usb_amd_hang_symptom_quirk(void);
+bool usb_amd_prefetch_quirk(void);
 void usb_amd_dev_put(void);
 void usb_amd_quirk_pll_disable(void);
 void usb_amd_quirk_pll_enable(void);
index 5477bf5df2186549412612dc016e809fa6970124..79620c39217ec182474bf53e2e04386560ea19b7 100644 (file)
@@ -1413,7 +1413,7 @@ static int sl811h_show(struct seq_file *s, void *unused)
                        case SL11H_CTL1MASK_SE0: s = " se0/reset"; break;
                        case SL11H_CTL1MASK_K: s = " k/resume"; break;
                        default: s = "j"; break;
-                       }; s; }),
+                       } s; }),
                        (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "",
                        (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : "");
 
@@ -1446,7 +1446,7 @@ static int sl811h_show(struct seq_file *s, void *unused)
                        case USB_PID_SETUP: s = "setup"; break;
                        case USB_PID_ACK: s = "status"; break;
                        default: s = "?"; break;
-                       }; s;}),
+                       } s;}),
                        ep->maxpacket,
                        ep->nak_count, ep->error_count);
                list_for_each_entry (urb, &ep->hep->urb_list, urb_list) {
index 45573754652534b94c28cc6fa8c85d1a35ac1385..8e239cdd95d51025c8ca806da08899a420a19e09 100644 (file)
@@ -310,14 +310,14 @@ static int uhci_show_status(struct uhci_hcd *uhci, char *buf, int len)
        unsigned short portsc1, portsc2;
 
 
-       usbcmd    = uhci_readw(uhci, 0);
-       usbstat   = uhci_readw(uhci, 2);
-       usbint    = uhci_readw(uhci, 4);
-       usbfrnum  = uhci_readw(uhci, 6);
-       flbaseadd = uhci_readl(uhci, 8);
-       sof       = uhci_readb(uhci, 12);
-       portsc1   = uhci_readw(uhci, 16);
-       portsc2   = uhci_readw(uhci, 18);
+       usbcmd    = uhci_readw(uhci, USBCMD);
+       usbstat   = uhci_readw(uhci, USBSTS);
+       usbint    = uhci_readw(uhci, USBINTR);
+       usbfrnum  = uhci_readw(uhci, USBFRNUM);
+       flbaseadd = uhci_readl(uhci, USBFLBASEADD);
+       sof       = uhci_readb(uhci, USBSOF);
+       portsc1   = uhci_readw(uhci, USBPORTSC1);
+       portsc2   = uhci_readw(uhci, USBPORTSC2);
 
        out += sprintf(out, "  usbcmd    =     %04x   %s%s%s%s%s%s%s%s\n",
                usbcmd,
index 9189bc984c98ce9804d51ec94c4aebe4147b8f3f..93e17b12fb3304b0191f36b3eb2f2b6eef334132 100644 (file)
@@ -75,8 +75,6 @@ static inline int get_hub_status_data(struct uhci_hcd *uhci, char *buf)
        return !!*buf;
 }
 
-#define OK(x)                  len = (x); break
-
 #define CLR_RH_PORTSTAT(x) \
        status = uhci_readw(uhci, port_addr);   \
        status &= ~(RWC_BITS|WZ_BITS); \
@@ -244,7 +242,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                        u16 wIndex, char *buf, u16 wLength)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
-       int status, lstatus, retval = 0, len = 0;
+       int status, lstatus, retval = 0;
        unsigned int port = wIndex - 1;
        unsigned long port_addr = USBPORTSC1 + 2 * port;
        u16 wPortChange, wPortStatus;
@@ -258,7 +256,8 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
        case GetHubStatus:
                *(__le32 *)buf = cpu_to_le32(0);
-               OK(4);          /* hub power */
+               retval = 4; /* hub power */
+               break;
        case GetPortStatus:
                if (port >= uhci->rh_numports)
                        goto err;
@@ -311,13 +310,14 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
                *(__le16 *)buf = cpu_to_le16(wPortStatus);
                *(__le16 *)(buf + 2) = cpu_to_le16(wPortChange);
-               OK(4);
+               retval = 4;
+               break;
        case SetHubFeature:             /* We don't implement these */
        case ClearHubFeature:
                switch (wValue) {
                case C_HUB_OVER_CURRENT:
                case C_HUB_LOCAL_POWER:
-                       OK(0);
+                       break;
                default:
                        goto err;
                }
@@ -329,7 +329,7 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                switch (wValue) {
                case USB_PORT_FEAT_SUSPEND:
                        SET_RH_PORTSTAT(USBPORTSC_SUSP);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_RESET:
                        SET_RH_PORTSTAT(USBPORTSC_PR);
 
@@ -338,10 +338,10 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
                        /* USB v2.0 7.1.7.5 */
                        uhci->ports_timeout = jiffies + msecs_to_jiffies(50);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_POWER:
                        /* UHCI has no power switching */
-                       OK(0);
+                       break;
                default:
                        goto err;
                }
@@ -356,10 +356,10 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
 
                        /* Disable terminates Resume signalling */
                        uhci_finish_suspend(uhci, port, port_addr);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_ENABLE:
                        CLR_RH_PORTSTAT(USBPORTSC_PEC);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_SUSPEND:
                        if (!(uhci_readw(uhci, port_addr) & USBPORTSC_SUSP)) {
 
@@ -382,32 +382,32 @@ static int uhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                        uhci->ports_timeout = jiffies +
                                                msecs_to_jiffies(20);
                        }
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_SUSPEND:
                        clear_bit(port, &uhci->port_c_suspend);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_POWER:
                        /* UHCI has no power switching */
                        goto err;
                case USB_PORT_FEAT_C_CONNECTION:
                        CLR_RH_PORTSTAT(USBPORTSC_CSC);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_OVER_CURRENT:
                        CLR_RH_PORTSTAT(USBPORTSC_OCC);
-                       OK(0);
+                       break;
                case USB_PORT_FEAT_C_RESET:
                        /* this driver won't report these */
-                       OK(0);
+                       break;
                default:
                        goto err;
                }
                break;
        case GetHubDescriptor:
-               len = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
-               memcpy(buf, root_hub_hub_des, len);
-               if (len > 2)
+               retval = min_t(unsigned int, sizeof(root_hub_hub_des), wLength);
+               memcpy(buf, root_hub_hub_des, retval);
+               if (retval > 2)
                        buf[2] = uhci->rh_numports;
-               OK(len);
+               break;
        default:
 err:
                retval = -EPIPE;
index 0f228c46eedaab97c9351f784b8604d6144bb517..4cd79888804bb679b1ff85f850bb6d2e362d8fd3 100644 (file)
@@ -162,6 +162,8 @@ static void uhci_shutdown(struct pci_dev *pdev)
 
 #ifdef CONFIG_PM
 
+static int uhci_pci_resume(struct usb_hcd *hcd, bool hibernated);
+
 static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 {
        struct uhci_hcd *uhci = hcd_to_uhci(hcd);
@@ -174,12 +176,6 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
        if (!HCD_HW_ACCESSIBLE(hcd) || uhci->dead)
                goto done_okay;         /* Already suspended or dead */
 
-       if (uhci->rh_state > UHCI_RH_SUSPENDED) {
-               dev_warn(uhci_dev(uhci), "Root hub isn't suspended!\n");
-               rc = -EBUSY;
-               goto done;
-       };
-
        /* All PCI host controllers are required to disable IRQ generation
         * at the source, so we must turn off PIRQ.
         */
@@ -195,8 +191,15 @@ static int uhci_pci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 
 done_okay:
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-done:
        spin_unlock_irq(&uhci->lock);
+
+       synchronize_irq(hcd->irq);
+
+       /* Check for race with a wakeup request */
+       if (do_wakeup && HCD_WAKEUP_PENDING(hcd)) {
+               uhci_pci_resume(hcd, false);
+               rc = -EBUSY;
+       }
        return rc;
 }
 
@@ -299,3 +302,5 @@ static struct pci_driver uhci_pci_driver = {
        },
 #endif
 };
+
+MODULE_SOFTDEP("pre: ehci_pci");
index f8548b72f7089c1d3e10bebf1f8c070759798b7e..3003fefaa9647df17d66168c9f88337c45cfa3ac 100644 (file)
@@ -104,8 +104,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
 
        uhci->regs = hcd->regs;
 
-       ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_DISABLED |
-                                                               IRQF_SHARED);
+       ret = usb_add_hcd(hcd, pdev->resource[1].start, IRQF_SHARED);
        if (ret)
                goto err_uhci;
 
index ecc88db804e008ba4b7eacb48f68aa1c6a49ded4..1b0888f8da9a156a4e0ae29597568efe008ddc94 100644 (file)
@@ -134,7 +134,7 @@ static int whc_urb_enqueue(struct usb_hcd *usb_hcd, struct urb *urb,
        default:
                ret = asl_urb_enqueue(whc, urb, mem_flags);
                break;
-       };
+       }
 
        return ret;
 }
@@ -160,7 +160,7 @@ static int whc_urb_dequeue(struct usb_hcd *usb_hcd, struct urb *urb, int status)
        default:
                ret = asl_urb_dequeue(whc, urb, status);
                break;
-       };
+       }
 
        return ret;
 }
index e8b4c56dcf62adf1f5326e8609087cd387fedc2e..805f2348eeba09a3ced3e037a3af82592d7b7c30 100644 (file)
@@ -296,7 +296,7 @@ static int xhci_stop_device(struct xhci_hcd *xhci, int slot_id, int suspend)
        /* Wait for last stop endpoint command to finish */
        timeleft = wait_for_completion_interruptible_timeout(
                        cmd->completion,
-                       USB_CTRL_SET_TIMEOUT);
+                       XHCI_CMD_DEFAULT_TIMEOUT);
        if (timeleft <= 0) {
                xhci_warn(xhci, "%s while waiting for stop endpoint command\n",
                                timeleft == 0 ? "Timeout" : "Signal");
@@ -524,7 +524,8 @@ static void xhci_hub_report_usb3_link_state(u32 *status, u32 status_reg)
  * the compliance mode timer is deleted. A port won't enter
  * compliance mode if it has previously entered U0.
  */
-void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status, u16 wIndex)
+static void xhci_del_comp_mod_timer(struct xhci_hcd *xhci, u32 status,
+                                   u16 wIndex)
 {
        u32 all_ports_seen_u0 = ((1 << xhci->num_usb3_ports)-1);
        bool port_in_u0 = ((status & PORT_PLS_MASK) == XDEV_U0);
index 83bcd13622c3466e655a00166bf6e8076ae198bc..49b8bd063fab70ab2de943f77c58ac329cdad87f 100644 (file)
@@ -1693,9 +1693,7 @@ void xhci_free_command(struct xhci_hcd *xhci,
 void xhci_mem_cleanup(struct xhci_hcd *xhci)
 {
        struct pci_dev  *pdev = to_pci_dev(xhci_to_hcd(xhci)->self.controller);
-       struct dev_info *dev_info, *next;
        struct xhci_cd  *cur_cd, *next_cd;
-       unsigned long   flags;
        int size;
        int i, j, num_ports;
 
@@ -1756,13 +1754,6 @@ void xhci_mem_cleanup(struct xhci_hcd *xhci)
 
        scratchpad_free(xhci);
 
-       spin_lock_irqsave(&xhci->lock, flags);
-       list_for_each_entry_safe(dev_info, next, &xhci->lpm_failed_devs, list) {
-               list_del(&dev_info->list);
-               kfree(dev_info);
-       }
-       spin_unlock_irqrestore(&xhci->lock, flags);
-
        if (!xhci->rh_bw)
                goto no_bw;
 
@@ -2231,7 +2222,6 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
        u32 page_size, temp;
        int i;
 
-       INIT_LIST_HEAD(&xhci->lpm_failed_devs);
        INIT_LIST_HEAD(&xhci->cancel_cmd_list);
 
        page_size = xhci_readl(xhci, &xhci->op_regs->page_size);
index 6bfbd80ec2b9edfa0a079767381d7d733d8c7035..1e2f3f4958436fb120b81d41632ad7e7c76fb5b5 100644 (file)
@@ -178,7 +178,7 @@ static void inc_deq(struct xhci_hcd *xhci, struct xhci_ring *ring)
                        if (ring->type == TYPE_EVENT &&
                                        last_trb_on_last_seg(xhci, ring,
                                                ring->deq_seg, ring->dequeue)) {
-                               ring->cycle_state = (ring->cycle_state ? 0 : 1);
+                               ring->cycle_state ^= 1;
                        }
                        ring->deq_seg = ring->deq_seg->next;
                        ring->dequeue = ring->deq_seg->trbs;
@@ -726,7 +726,7 @@ static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
 
 /* Must be called with xhci->lock held in interrupt context */
 static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
-               struct xhci_td *cur_td, int status, char *adjective)
+               struct xhci_td *cur_td, int status)
 {
        struct usb_hcd *hcd;
        struct urb      *urb;
@@ -765,10 +765,9 @@ static void xhci_giveback_urb_in_irq(struct xhci_hcd *xhci,
  *  2. Otherwise, we turn all the TRBs in the TD into No-op TRBs (with the chain
  *     bit cleared) so that the HW will skip over them.
  */
-static void handle_stopped_endpoint(struct xhci_hcd *xhci,
+static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
                union xhci_trb *trb, struct xhci_event_cmd *event)
 {
-       unsigned int slot_id;
        unsigned int ep_index;
        struct xhci_virt_device *virt_dev;
        struct xhci_ring *ep_ring;
@@ -779,10 +778,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
 
        struct xhci_dequeue_state deq_state;
 
-       if (unlikely(TRB_TO_SUSPEND_PORT(
-                            le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])))) {
-               slot_id = TRB_TO_SLOT_ID(
-                       le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]));
+       if (unlikely(TRB_TO_SUSPEND_PORT(le32_to_cpu(trb->generic.field[3])))) {
                virt_dev = xhci->devs[slot_id];
                if (virt_dev)
                        handle_cmd_in_cmd_wait_list(xhci, virt_dev,
@@ -795,7 +791,6 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
        }
 
        memset(&deq_state, 0, sizeof(deq_state));
-       slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
        ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
        ep = &xhci->devs[slot_id]->eps[ep_index];
 
@@ -891,7 +886,7 @@ remove_finished_td:
                /* Doesn't matter what we pass for status, since the core will
                 * just overwrite it (because the URB has been unlinked).
                 */
-               xhci_giveback_urb_in_irq(xhci, cur_td, 0, "cancelled");
+               xhci_giveback_urb_in_irq(xhci, cur_td, 0);
 
                /* Stop processing the cancelled list if the watchdog timer is
                 * running.
@@ -1001,7 +996,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
                                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, "killed");
+                                               -ESHUTDOWN);
                        }
                        while (!list_empty(&temp_ep->cancelled_td_list)) {
                                cur_td = list_first_entry(
@@ -1010,7 +1005,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
                                                cancelled_td_list);
                                list_del_init(&cur_td->cancelled_td_list);
                                xhci_giveback_urb_in_irq(xhci, cur_td,
-                                               -ESHUTDOWN, "killed");
+                                               -ESHUTDOWN);
                        }
                }
        }
@@ -1077,11 +1072,9 @@ static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
  * endpoint doorbell to restart the ring, but only if there aren't more
  * cancellations pending.
  */
-static void handle_set_deq_completion(struct xhci_hcd *xhci,
-               struct xhci_event_cmd *event,
-               union xhci_trb *trb)
+static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id,
+               union xhci_trb *trb, u32 cmd_comp_code)
 {
-       unsigned int slot_id;
        unsigned int ep_index;
        unsigned int stream_id;
        struct xhci_ring *ep_ring;
@@ -1089,7 +1082,6 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
        struct xhci_ep_ctx *ep_ctx;
        struct xhci_slot_ctx *slot_ctx;
 
-       slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
        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];
@@ -1107,11 +1099,11 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
        ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index);
        slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx);
 
-       if (GET_COMP_CODE(le32_to_cpu(event->status)) != COMP_SUCCESS) {
+       if (cmd_comp_code != COMP_SUCCESS) {
                unsigned int ep_state;
                unsigned int slot_state;
 
-               switch (GET_COMP_CODE(le32_to_cpu(event->status))) {
+               switch (cmd_comp_code) {
                case COMP_TRB_ERR:
                        xhci_warn(xhci, "WARN Set TR Deq Ptr cmd invalid because "
                                        "of stream ID configuration\n");
@@ -1134,7 +1126,7 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
                default:
                        xhci_warn(xhci, "WARN Set TR Deq Ptr cmd with unknown "
                                        "completion code of %u.\n",
-                                 GET_COMP_CODE(le32_to_cpu(event->status)));
+                                 cmd_comp_code);
                        break;
                }
                /* OK what do we do now?  The endpoint state is hosed, and we
@@ -1171,21 +1163,17 @@ static void handle_set_deq_completion(struct xhci_hcd *xhci,
        ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
 }
 
-static void handle_reset_ep_completion(struct xhci_hcd *xhci,
-               struct xhci_event_cmd *event,
-               union xhci_trb *trb)
+static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
+               union xhci_trb *trb, u32 cmd_comp_code)
 {
-       int slot_id;
        unsigned int ep_index;
 
-       slot_id = TRB_TO_SLOT_ID(le32_to_cpu(trb->generic.field[3]));
        ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
        /* This command will only fail if the endpoint wasn't halted,
         * but we don't care.
         */
        xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
-               "Ignoring reset ep completion code of %u",
-                GET_COMP_CODE(le32_to_cpu(event->status)));
+               "Ignoring reset ep completion code of %u", cmd_comp_code);
 
        /* HW with the reset endpoint quirk needs to have a configure endpoint
         * command complete before the endpoint can be used.  Queue that here
@@ -1386,21 +1374,149 @@ static int handle_stopped_cmd_ring(struct xhci_hcd *xhci,
        return cur_trb_is_good;
 }
 
+static void xhci_handle_cmd_enable_slot(struct xhci_hcd *xhci, int slot_id,
+               u32 cmd_comp_code)
+{
+       if (cmd_comp_code == COMP_SUCCESS)
+               xhci->slot_id = slot_id;
+       else
+               xhci->slot_id = 0;
+       complete(&xhci->addr_dev);
+}
+
+static void xhci_handle_cmd_disable_slot(struct xhci_hcd *xhci, int slot_id)
+{
+       struct xhci_virt_device *virt_dev;
+
+       virt_dev = xhci->devs[slot_id];
+       if (!virt_dev)
+               return;
+       if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
+               /* Delete default control endpoint resources */
+               xhci_free_device_endpoint_resources(xhci, virt_dev, true);
+       xhci_free_virt_device(xhci, slot_id);
+}
+
+static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
+               struct xhci_event_cmd *event, u32 cmd_comp_code)
+{
+       struct xhci_virt_device *virt_dev;
+       struct xhci_input_control_ctx *ctrl_ctx;
+       unsigned int ep_index;
+       unsigned int ep_state;
+       u32 add_flags, drop_flags;
+
+       virt_dev = xhci->devs[slot_id];
+       if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
+               return;
+       /*
+        * Configure endpoint commands can come from the USB core
+        * configuration or alt setting changes, or because the HW
+        * needed an extra configure endpoint command after a reset
+        * endpoint command or streams were being configured.
+        * If the command was for a halted endpoint, the xHCI driver
+        * is not waiting on the configure endpoint command.
+        */
+       ctrl_ctx = xhci_get_input_control_ctx(xhci, virt_dev->in_ctx);
+       if (!ctrl_ctx) {
+               xhci_warn(xhci, "Could not get input context, bad type.\n");
+               return;
+       }
+
+       add_flags = le32_to_cpu(ctrl_ctx->add_flags);
+       drop_flags = le32_to_cpu(ctrl_ctx->drop_flags);
+       /* Input ctx add_flags are the endpoint index plus one */
+       ep_index = xhci_last_valid_endpoint(add_flags) - 1;
+
+       /* A usb_set_interface() call directly after clearing a halted
+        * condition may race on this quirky hardware.  Not worth
+        * worrying about, since this is prototype hardware.  Not sure
+        * if this will work for streams, but streams support was
+        * untested on this prototype.
+        */
+       if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
+                       ep_index != (unsigned int) -1 &&
+                       add_flags - SLOT_FLAG == drop_flags) {
+               ep_state = virt_dev->eps[ep_index].ep_state;
+               if (!(ep_state & EP_HALTED))
+                       goto bandwidth_change;
+               xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+                               "Completed config ep cmd - "
+                               "last ep index = %d, state = %d",
+                               ep_index, ep_state);
+               /* Clear internal halted state and restart ring(s) */
+               virt_dev->eps[ep_index].ep_state &= ~EP_HALTED;
+               ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
+               return;
+       }
+bandwidth_change:
+       xhci_dbg_trace(xhci,  trace_xhci_dbg_context_change,
+                       "Completed config ep cmd");
+       virt_dev->cmd_status = cmd_comp_code;
+       complete(&virt_dev->cmd_completion);
+       return;
+}
+
+static void xhci_handle_cmd_eval_ctx(struct xhci_hcd *xhci, int slot_id,
+               struct xhci_event_cmd *event, u32 cmd_comp_code)
+{
+       struct xhci_virt_device *virt_dev;
+
+       virt_dev = xhci->devs[slot_id];
+       if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
+               return;
+       virt_dev->cmd_status = cmd_comp_code;
+       complete(&virt_dev->cmd_completion);
+}
+
+static void xhci_handle_cmd_addr_dev(struct xhci_hcd *xhci, int slot_id,
+               u32 cmd_comp_code)
+{
+       xhci->devs[slot_id]->cmd_status = cmd_comp_code;
+       complete(&xhci->addr_dev);
+}
+
+static void xhci_handle_cmd_reset_dev(struct xhci_hcd *xhci, int slot_id,
+               struct xhci_event_cmd *event)
+{
+       struct xhci_virt_device *virt_dev;
+
+       xhci_dbg(xhci, "Completed reset device command.\n");
+       virt_dev = xhci->devs[slot_id];
+       if (virt_dev)
+               handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
+       else
+               xhci_warn(xhci, "Reset device command completion "
+                               "for disabled slot %u\n", slot_id);
+}
+
+static void xhci_handle_cmd_nec_get_fw(struct xhci_hcd *xhci,
+               struct xhci_event_cmd *event)
+{
+       if (!(xhci->quirks & XHCI_NEC_HOST)) {
+               xhci->error_bitmask |= 1 << 6;
+               return;
+       }
+       xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
+                       "NEC firmware version %2x.%02x",
+                       NEC_FW_MAJOR(le32_to_cpu(event->status)),
+                       NEC_FW_MINOR(le32_to_cpu(event->status)));
+}
+
 static void handle_cmd_completion(struct xhci_hcd *xhci,
                struct xhci_event_cmd *event)
 {
        int slot_id = TRB_TO_SLOT_ID(le32_to_cpu(event->flags));
        u64 cmd_dma;
        dma_addr_t cmd_dequeue_dma;
-       struct xhci_input_control_ctx *ctrl_ctx;
-       struct xhci_virt_device *virt_dev;
-       unsigned int ep_index;
-       struct xhci_ring *ep_ring;
-       unsigned int ep_state;
+       u32 cmd_comp_code;
+       union xhci_trb *cmd_trb;
+       u32 cmd_type;
 
        cmd_dma = le64_to_cpu(event->cmd_trb);
+       cmd_trb = xhci->cmd_ring->dequeue;
        cmd_dequeue_dma = xhci_trb_virt_to_dma(xhci->cmd_ring->deq_seg,
-                       xhci->cmd_ring->dequeue);
+                       cmd_trb);
        /* Is the command ring deq ptr out of sync with the deq seg ptr? */
        if (cmd_dequeue_dma == 0) {
                xhci->error_bitmask |= 1 << 4;
@@ -1412,19 +1528,17 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
                return;
        }
 
-       trace_xhci_cmd_completion(&xhci->cmd_ring->dequeue->generic,
-                                       (struct xhci_generic_trb *) event);
+       trace_xhci_cmd_completion(cmd_trb, (struct xhci_generic_trb *) event);
 
-       if ((GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_ABORT) ||
-               (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_CMD_STOP)) {
+       cmd_comp_code = GET_COMP_CODE(le32_to_cpu(event->status));
+       if (cmd_comp_code == COMP_CMD_ABORT || cmd_comp_code == COMP_CMD_STOP) {
                /* If the return value is 0, we think the trb pointed by
                 * command ring dequeue pointer is a good trb. The good
                 * trb means we don't want to cancel the trb, but it have
                 * been stopped by host. So we should handle it normally.
                 * Otherwise, driver should invoke inc_deq() and return.
                 */
-               if (handle_stopped_cmd_ring(xhci,
-                               GET_COMP_CODE(le32_to_cpu(event->status)))) {
+               if (handle_stopped_cmd_ring(xhci, cmd_comp_code)) {
                        inc_deq(xhci, xhci->cmd_ring);
                        return;
                }
@@ -1436,117 +1550,47 @@ static void handle_cmd_completion(struct xhci_hcd *xhci,
                        return;
        }
 
-       switch (le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3])
-               & TRB_TYPE_BITMASK) {
-       case TRB_TYPE(TRB_ENABLE_SLOT):
-               if (GET_COMP_CODE(le32_to_cpu(event->status)) == COMP_SUCCESS)
-                       xhci->slot_id = slot_id;
-               else
-                       xhci->slot_id = 0;
-               complete(&xhci->addr_dev);
+       cmd_type = TRB_FIELD_TO_TYPE(le32_to_cpu(cmd_trb->generic.field[3]));
+       switch (cmd_type) {
+       case TRB_ENABLE_SLOT:
+               xhci_handle_cmd_enable_slot(xhci, slot_id, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_DISABLE_SLOT):
-               if (xhci->devs[slot_id]) {
-                       if (xhci->quirks & XHCI_EP_LIMIT_QUIRK)
-                               /* Delete default control endpoint resources */
-                               xhci_free_device_endpoint_resources(xhci,
-                                               xhci->devs[slot_id], true);
-                       xhci_free_virt_device(xhci, slot_id);
-               }
+       case TRB_DISABLE_SLOT:
+               xhci_handle_cmd_disable_slot(xhci, slot_id);
                break;
-       case TRB_TYPE(TRB_CONFIG_EP):
-               virt_dev = xhci->devs[slot_id];
-               if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
-                       break;
-               /*
-                * Configure endpoint commands can come from the USB core
-                * configuration or alt setting changes, or because the HW
-                * needed an extra configure endpoint command after a reset
-                * endpoint command or streams were being configured.
-                * If the command was for a halted endpoint, the xHCI driver
-                * is not waiting on the configure endpoint command.
-                */
-               ctrl_ctx = xhci_get_input_control_ctx(xhci,
-                               virt_dev->in_ctx);
-               if (!ctrl_ctx) {
-                       xhci_warn(xhci, "Could not get input context, bad type.\n");
-                       break;
-               }
-               /* Input ctx add_flags are the endpoint index plus one */
-               ep_index = xhci_last_valid_endpoint(le32_to_cpu(ctrl_ctx->add_flags)) - 1;
-               /* A usb_set_interface() call directly after clearing a halted
-                * condition may race on this quirky hardware.  Not worth
-                * worrying about, since this is prototype hardware.  Not sure
-                * if this will work for streams, but streams support was
-                * untested on this prototype.
-                */
-               if (xhci->quirks & XHCI_RESET_EP_QUIRK &&
-                               ep_index != (unsigned int) -1 &&
-                   le32_to_cpu(ctrl_ctx->add_flags) - SLOT_FLAG ==
-                   le32_to_cpu(ctrl_ctx->drop_flags)) {
-                       ep_ring = xhci->devs[slot_id]->eps[ep_index].ring;
-                       ep_state = xhci->devs[slot_id]->eps[ep_index].ep_state;
-                       if (!(ep_state & EP_HALTED))
-                               goto bandwidth_change;
-                       xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
-                                       "Completed config ep cmd - "
-                                       "last ep index = %d, state = %d",
-                                       ep_index, ep_state);
-                       /* Clear internal halted state and restart ring(s) */
-                       xhci->devs[slot_id]->eps[ep_index].ep_state &=
-                               ~EP_HALTED;
-                       ring_doorbell_for_active_rings(xhci, slot_id, ep_index);
-                       break;
-               }
-bandwidth_change:
-               xhci_dbg_trace(xhci,  trace_xhci_dbg_context_change,
-                               "Completed config ep cmd");
-               xhci->devs[slot_id]->cmd_status =
-                       GET_COMP_CODE(le32_to_cpu(event->status));
-               complete(&xhci->devs[slot_id]->cmd_completion);
+       case TRB_CONFIG_EP:
+               xhci_handle_cmd_config_ep(xhci, slot_id, event, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_EVAL_CONTEXT):
-               virt_dev = xhci->devs[slot_id];
-               if (handle_cmd_in_cmd_wait_list(xhci, virt_dev, event))
-                       break;
-               xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
-               complete(&xhci->devs[slot_id]->cmd_completion);
+       case TRB_EVAL_CONTEXT:
+               xhci_handle_cmd_eval_ctx(xhci, slot_id, event, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_ADDR_DEV):
-               xhci->devs[slot_id]->cmd_status = GET_COMP_CODE(le32_to_cpu(event->status));
-               complete(&xhci->addr_dev);
+       case TRB_ADDR_DEV:
+               xhci_handle_cmd_addr_dev(xhci, slot_id, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_STOP_RING):
-               handle_stopped_endpoint(xhci, xhci->cmd_ring->dequeue, event);
+       case TRB_STOP_RING:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_stop_ep(xhci, slot_id, cmd_trb, event);
                break;
-       case TRB_TYPE(TRB_SET_DEQ):
-               handle_set_deq_completion(xhci, event, xhci->cmd_ring->dequeue);
+       case TRB_SET_DEQ:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_set_deq(xhci, slot_id, cmd_trb, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_CMD_NOOP):
+       case TRB_CMD_NOOP:
                break;
-       case TRB_TYPE(TRB_RESET_EP):
-               handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue);
+       case TRB_RESET_EP:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_reset_ep(xhci, slot_id, cmd_trb, cmd_comp_code);
                break;
-       case TRB_TYPE(TRB_RESET_DEV):
-               xhci_dbg(xhci, "Completed reset device command.\n");
-               slot_id = TRB_TO_SLOT_ID(
-                       le32_to_cpu(xhci->cmd_ring->dequeue->generic.field[3]));
-               virt_dev = xhci->devs[slot_id];
-               if (virt_dev)
-                       handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
-               else
-                       xhci_warn(xhci, "Reset device command completion "
-                                       "for disabled slot %u\n", slot_id);
+       case TRB_RESET_DEV:
+               WARN_ON(slot_id != TRB_TO_SLOT_ID(
+                               le32_to_cpu(cmd_trb->generic.field[3])));
+               xhci_handle_cmd_reset_dev(xhci, slot_id, event);
                break;
-       case TRB_TYPE(TRB_NEC_GET_FW):
-               if (!(xhci->quirks & XHCI_NEC_HOST)) {
-                       xhci->error_bitmask |= 1 << 6;
-                       break;
-               }
-               xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
-                       "NEC firmware version %2x.%02x",
-                        NEC_FW_MAJOR(le32_to_cpu(event->status)),
-                        NEC_FW_MINOR(le32_to_cpu(event->status)));
+       case TRB_NEC_GET_FW:
+               xhci_handle_cmd_nec_get_fw(xhci, event);
                break;
        default:
                /* Skip over unknown commands on the event ring */
index 6e0d886bcce52c19361d321cf68c0abacf5b054c..4265b48856f6a7b0160124964b3b7fc697ecd826 100644 (file)
@@ -3459,7 +3459,7 @@ int xhci_discover_or_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
        /* Wait for the Reset Device command to finish */
        timeleft = wait_for_completion_interruptible_timeout(
                        reset_device_cmd->completion,
-                       USB_CTRL_SET_TIMEOUT);
+                       XHCI_CMD_DEFAULT_TIMEOUT);
        if (timeleft <= 0) {
                xhci_warn(xhci, "%s while waiting for reset device command\n",
                                timeleft == 0 ? "Timeout" : "Signal");
@@ -3583,11 +3583,6 @@ void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
                del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
        }
 
-       if (udev->usb2_hw_lpm_enabled) {
-               xhci_set_usb2_hardware_lpm(hcd, udev, 0);
-               udev->usb2_hw_lpm_enabled = 0;
-       }
-
        spin_lock_irqsave(&xhci->lock, flags);
        /* Don't disable the slot if the host controller is dead. */
        state = xhci_readl(xhci, &xhci->op_regs->status);
@@ -3721,9 +3716,6 @@ disable_slot:
  * the device).
  * We should be protected by the usb_address0_mutex in khubd's hub_port_init, so
  * we should only issue and wait on one address command at the same time.
- *
- * We add one to the device address issued by the hardware because the USB core
- * uses address 1 for the root hubs (even though they're not really devices).
  */
 int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
@@ -3868,16 +3860,13 @@ int xhci_address_device(struct usb_hcd *hcd, struct usb_device *udev)
        slot_ctx = xhci_get_slot_ctx(xhci, virt_dev->out_ctx);
        trace_xhci_address_ctx(xhci, virt_dev->out_ctx,
                                slot_ctx->dev_info >> 27);
-       /* Use kernel assigned address for devices; store xHC assigned
-        * address locally. */
-       virt_dev->address = (le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK)
-               + 1;
        /* Zero the input context control for later use */
        ctrl_ctx->add_flags = 0;
        ctrl_ctx->drop_flags = 0;
 
        xhci_dbg_trace(xhci, trace_xhci_dbg_address,
-                       "Internal device address = %d", virt_dev->address);
+                      "Internal device address = %d",
+                      le32_to_cpu(slot_ctx->dev_state) & DEV_ADDR_MASK);
 
        return 0;
 }
@@ -4025,133 +4014,6 @@ static int xhci_calculate_usb2_hw_lpm_params(struct usb_device *udev)
        return PORT_BESLD(besld) | PORT_L1_TIMEOUT(l1) | PORT_HIRDM(hirdm);
 }
 
-static int xhci_usb2_software_lpm_test(struct usb_hcd *hcd,
-                                       struct usb_device *udev)
-{
-       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       struct dev_info *dev_info;
-       __le32 __iomem  **port_array;
-       __le32 __iomem  *addr, *pm_addr;
-       u32             temp, dev_id;
-       unsigned int    port_num;
-       unsigned long   flags;
-       int             hird;
-       int             ret;
-
-       if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
-                       !udev->lpm_capable)
-               return -EINVAL;
-
-       /* we only support lpm for non-hub device connected to root hub yet */
-       if (!udev->parent || udev->parent->parent ||
-                       udev->descriptor.bDeviceClass == USB_CLASS_HUB)
-               return -EINVAL;
-
-       spin_lock_irqsave(&xhci->lock, flags);
-
-       /* Look for devices in lpm_failed_devs list */
-       dev_id = le16_to_cpu(udev->descriptor.idVendor) << 16 |
-                       le16_to_cpu(udev->descriptor.idProduct);
-       list_for_each_entry(dev_info, &xhci->lpm_failed_devs, list) {
-               if (dev_info->dev_id == dev_id) {
-                       ret = -EINVAL;
-                       goto finish;
-               }
-       }
-
-       port_array = xhci->usb2_ports;
-       port_num = udev->portnum - 1;
-
-       if (port_num > HCS_MAX_PORTS(xhci->hcs_params1)) {
-               xhci_dbg(xhci, "invalid port number %d\n", udev->portnum);
-               ret = -EINVAL;
-               goto finish;
-       }
-
-       /*
-        * Test USB 2.0 software LPM.
-        * FIXME: some xHCI 1.0 hosts may implement a new register to set up
-        * hardware-controlled USB 2.0 LPM. See section 5.4.11 and 4.23.5.1.1.1
-        * in the June 2011 errata release.
-        */
-       xhci_dbg(xhci, "test port %d software LPM\n", port_num);
-       /*
-        * Set L1 Device Slot and HIRD/BESL.
-        * Check device's USB 2.0 extension descriptor to determine whether
-        * HIRD or BESL shoule be used. See USB2.0 LPM errata.
-        */
-       pm_addr = port_array[port_num] + PORTPMSC;
-       hird = xhci_calculate_hird_besl(xhci, udev);
-       temp = PORT_L1DS(udev->slot_id) | PORT_HIRD(hird);
-       xhci_writel(xhci, temp, pm_addr);
-
-       /* Set port link state to U2(L1) */
-       addr = port_array[port_num];
-       xhci_set_link_state(xhci, port_array, port_num, XDEV_U2);
-
-       /* wait for ACK */
-       spin_unlock_irqrestore(&xhci->lock, flags);
-       msleep(10);
-       spin_lock_irqsave(&xhci->lock, flags);
-
-       /* Check L1 Status */
-       ret = xhci_handshake(xhci, pm_addr,
-                       PORT_L1S_MASK, PORT_L1S_SUCCESS, 125);
-       if (ret != -ETIMEDOUT) {
-               /* enter L1 successfully */
-               temp = xhci_readl(xhci, addr);
-               xhci_dbg(xhci, "port %d entered L1 state, port status 0x%x\n",
-                               port_num, temp);
-               ret = 0;
-       } else {
-               temp = xhci_readl(xhci, pm_addr);
-               xhci_dbg(xhci, "port %d software lpm failed, L1 status %d\n",
-                               port_num, temp & PORT_L1S_MASK);
-               ret = -EINVAL;
-       }
-
-       /* Resume the port */
-       xhci_set_link_state(xhci, port_array, port_num, XDEV_U0);
-
-       spin_unlock_irqrestore(&xhci->lock, flags);
-       msleep(10);
-       spin_lock_irqsave(&xhci->lock, flags);
-
-       /* Clear PLC */
-       xhci_test_and_clear_bit(xhci, port_array, port_num, PORT_PLC);
-
-       /* Check PORTSC to make sure the device is in the right state */
-       if (!ret) {
-               temp = xhci_readl(xhci, addr);
-               xhci_dbg(xhci, "resumed port %d status 0x%x\n", port_num, temp);
-               if (!(temp & PORT_CONNECT) || !(temp & PORT_PE) ||
-                               (temp & PORT_PLS_MASK) != XDEV_U0) {
-                       xhci_dbg(xhci, "port L1 resume fail\n");
-                       ret = -EINVAL;
-               }
-       }
-
-       if (ret) {
-               /* Insert dev to lpm_failed_devs list */
-               xhci_warn(xhci, "device LPM test failed, may disconnect and "
-                               "re-enumerate\n");
-               dev_info = kzalloc(sizeof(struct dev_info), GFP_ATOMIC);
-               if (!dev_info) {
-                       ret = -ENOMEM;
-                       goto finish;
-               }
-               dev_info->dev_id = dev_id;
-               INIT_LIST_HEAD(&dev_info->list);
-               list_add(&dev_info->list, &xhci->lpm_failed_devs);
-       } else {
-               xhci_ring_device(xhci, udev->slot_id);
-       }
-
-finish:
-       spin_unlock_irqrestore(&xhci->lock, flags);
-       return ret;
-}
-
 int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
                        struct usb_device *udev, int enable)
 {
@@ -4228,7 +4090,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
                }
 
                pm_val &= ~PORT_HIRD_MASK;
-               pm_val |= PORT_HIRD(hird) | PORT_RWE;
+               pm_val |= PORT_HIRD(hird) | PORT_RWE | PORT_L1DS(udev->slot_id);
                xhci_writel(xhci, pm_val, pm_addr);
                pm_val = xhci_readl(xhci, pm_addr);
                pm_val |= PORT_HLE;
@@ -4236,7 +4098,7 @@ int xhci_set_usb2_hardware_lpm(struct usb_hcd *hcd,
                /* flush write */
                xhci_readl(xhci, pm_addr);
        } else {
-               pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK);
+               pm_val &= ~(PORT_HLE | PORT_RWE | PORT_HIRD_MASK | PORT_L1DS_MASK);
                xhci_writel(xhci, pm_val, pm_addr);
                /* flush write */
                xhci_readl(xhci, pm_addr);
@@ -4279,24 +4141,26 @@ static int xhci_check_usb2_port_capability(struct xhci_hcd *xhci, int port,
 int xhci_update_device(struct usb_hcd *hcd, struct usb_device *udev)
 {
        struct xhci_hcd *xhci = hcd_to_xhci(hcd);
-       int             ret;
        int             portnum = udev->portnum - 1;
 
-       ret = xhci_usb2_software_lpm_test(hcd, udev);
-       if (!ret) {
-               xhci_dbg(xhci, "software LPM test succeed\n");
-               if (xhci->hw_lpm_support == 1 &&
-                   xhci_check_usb2_port_capability(xhci, portnum, XHCI_HLC)) {
-                       udev->usb2_hw_lpm_capable = 1;
-                       udev->l1_params.timeout = XHCI_L1_TIMEOUT;
-                       udev->l1_params.besl = XHCI_DEFAULT_BESL;
-                       if (xhci_check_usb2_port_capability(xhci, portnum,
-                                                           XHCI_BLC))
-                               udev->usb2_hw_lpm_besl_capable = 1;
-                       ret = xhci_set_usb2_hardware_lpm(hcd, udev, 1);
-                       if (!ret)
-                               udev->usb2_hw_lpm_enabled = 1;
-               }
+       if (hcd->speed == HCD_USB3 || !xhci->sw_lpm_support ||
+                       !udev->lpm_capable)
+               return 0;
+
+       /* we only support lpm for non-hub device connected to root hub yet */
+       if (!udev->parent || udev->parent->parent ||
+                       udev->descriptor.bDeviceClass == USB_CLASS_HUB)
+               return 0;
+
+       if (xhci->hw_lpm_support == 1 &&
+                       xhci_check_usb2_port_capability(
+                               xhci, portnum, XHCI_HLC)) {
+               udev->usb2_hw_lpm_capable = 1;
+               udev->l1_params.timeout = XHCI_L1_TIMEOUT;
+               udev->l1_params.besl = XHCI_DEFAULT_BESL;
+               if (xhci_check_usb2_port_capability(xhci, portnum,
+                                       XHCI_BLC))
+                       udev->usb2_hw_lpm_besl_capable = 1;
        }
 
        return 0;
index 941d5f59e4dcc254770bac770ba024e36a677bad..03c74b7965f85d767436c8108348f6adf6f87126 100644 (file)
@@ -383,6 +383,7 @@ struct xhci_op_regs {
 #define        PORT_RWE                (1 << 3)
 #define        PORT_HIRD(p)            (((p) & 0xf) << 4)
 #define        PORT_HIRD_MASK          (0xf << 4)
+#define        PORT_L1DS_MASK          (0xff << 8)
 #define        PORT_L1DS(p)            (((p) & 0xff) << 8)
 #define        PORT_HLE                (1 << 16)
 
@@ -934,8 +935,6 @@ struct xhci_virt_device {
        /* Rings saved to ensure old alt settings can be re-instated */
        struct xhci_ring                **ring_cache;
        int                             num_rings_cached;
-       /* Store xHC assigned device address */
-       int                             address;
 #define        XHCI_MAX_RINGS_CACHED   31
        struct xhci_virt_ep             eps[31];
        struct completion               cmd_completion;
index aa28ac8c7607deeb49136340f0def4dd5f6fa05d..3e91d3e98ee871c99c88b37e328499853c853481 100644 (file)
@@ -120,7 +120,7 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
                        struct usb_host_endpoint        *e;
 
                        e = alt->endpoint + ep;
-                       switch (e->desc.bmAttributes) {
+                       switch (usb_endpoint_type(&e->desc)) {
                        case USB_ENDPOINT_XFER_BULK:
                                break;
                        case USB_ENDPOINT_XFER_ISOC:
index c258a97ef1b0050f41e02d0a8e08b0052af488e7..0440e2807280934a21a0f7e91f9cbec1a62a9190 100644 (file)
@@ -75,6 +75,7 @@ config USB_MUSB_TUSB6010
 config USB_MUSB_OMAP2PLUS
        tristate "OMAP2430 and onwards"
        depends on ARCH_OMAP2PLUS
+       select GENERIC_PHY
 
 config USB_MUSB_AM35X
        tristate "AM35x"
index cd70cc8861711015f5443e2b76e969209d9bdb3a..208785e36d5265988c807e0a5242a1579a284a77 100644 (file)
@@ -617,7 +617,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
                                /* case 3 << MUSB_DEVCTL_VBUS_SHIFT: */
                                default:
                                        s = "VALID"; break;
-                               }; s; }),
+                               } s; }),
                                VBUSERR_RETRY_COUNT - musb->vbuserr_retry,
                                musb->port1_status);
 
index 1c5bf75ee8ff8a45a3da1fca7cb1d31920f0eeff..29f7cd7c7964e9bf78607de0031396393e84bd44 100644 (file)
@@ -46,6 +46,7 @@
 #include <linux/usb.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/musb.h>
+#include <linux/phy/phy.h>
 
 struct musb;
 struct musb_hw_ep;
@@ -341,6 +342,7 @@ struct musb {
        u16                     int_tx;
 
        struct usb_phy          *xceiv;
+       struct phy              *phy;
 
        int nIrq;
        unsigned                irq_wake:1;
index bd4138d80a48f243c9d4cb32527196d0024eb3d1..1a5574c2d586c78f7de46e605cfbe0b359aa1ece 100644 (file)
@@ -443,7 +443,7 @@ static int get_musb_port_mode(struct device *dev)
        case USB_DR_MODE_OTG:
        default:
                return MUSB_PORT_MODE_DUAL_ROLE;
-       };
+       }
 }
 
 static int dsps_create_musb_pdev(struct dsps_glue *glue,
index 3671898a4535b3cf2de3c0e188f4947485d2760d..d2d3a173b31503b54f9071e9af48f4878567856f 100644 (file)
@@ -1121,7 +1121,7 @@ static int musb_gadget_enable(struct usb_ep *ep,
                        case USB_ENDPOINT_XFER_BULK:    s = "bulk"; break;
                        case USB_ENDPOINT_XFER_INT:     s = "int"; break;
                        default:                        s = "iso"; break;
-                       }; s; }),
+                       } s; }),
                        musb_ep->is_in ? "IN" : "OUT",
                        musb_ep->dma ? "dma, " : "",
                        musb_ep->packet_sz);
index 9a2b8c85f19af4ea05bca5c1e90fe08ac345f22b..6582a20bec05db6be2730f6ffee4f3677e6b2340 100644 (file)
@@ -253,7 +253,7 @@ musb_start_urb(struct musb *musb, int is_in, struct musb_qh *qh)
                        case USB_ENDPOINT_XFER_BULK:    s = "-bulk"; break;
                        case USB_ENDPOINT_XFER_ISOC:    s = "-iso"; break;
                        default:                        s = "-intr"; break;
-                       }; s; }),
+                       } s; }),
                        epnum, buf + offset, len);
 
        /* Configure endpoint */
index 59d2245db1c81ce228d7607aa1176e56bbfb9082..9eab645fed8b71856041ab925266f6ae4bc8303a 100644 (file)
@@ -38,6 +38,7 @@
 #include <linux/delay.h>
 #include <linux/usb/musb-omap.h>
 #include <linux/usb/omap_control_usb.h>
+#include <linux/of_platform.h>
 
 #include "musb_core.h"
 #include "omap2430.h"
@@ -348,11 +349,21 @@ static int omap2430_musb_init(struct musb *musb)
         * up through ULPI.  TWL4030-family PMICs include one,
         * which needs a driver, drivers aren't always needed.
         */
-       if (dev->parent->of_node)
+       if (dev->parent->of_node) {
+               musb->phy = devm_phy_get(dev->parent, "usb2-phy");
+
+               /* We can't totally remove musb->xceiv as of now because
+                * musb core uses xceiv.state and xceiv.otg. Once we have
+                * a separate state machine to handle otg, these can be moved
+                * out of xceiv and then we can start using the generic PHY
+                * framework
+                */
                musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent,
                    "usb-phy", 0);
-       else
+       } else {
                musb->xceiv = devm_usb_get_phy_dev(dev, 0);
+               musb->phy = devm_phy_get(dev, "usb");
+       }
 
        if (IS_ERR(musb->xceiv)) {
                status = PTR_ERR(musb->xceiv);
@@ -364,6 +375,10 @@ static int omap2430_musb_init(struct musb *musb)
                return -EPROBE_DEFER;
        }
 
+       if (IS_ERR(musb->phy)) {
+               pr_err("HS USB OTG: no PHY configured\n");
+               return PTR_ERR(musb->phy);
+       }
        musb->isr = omap2430_musb_interrupt;
 
        status = pm_runtime_get_sync(dev);
@@ -397,7 +412,7 @@ static int omap2430_musb_init(struct musb *musb)
        if (glue->status != OMAP_MUSB_UNKNOWN)
                omap_musb_set_mailbox(glue);
 
-       usb_phy_init(musb->xceiv);
+       phy_init(musb->phy);
 
        pm_runtime_put_noidle(musb->controller);
        return 0;
@@ -460,6 +475,7 @@ static int omap2430_musb_exit(struct musb *musb)
        del_timer_sync(&musb_idle_timer);
 
        omap2430_low_level_exit(musb);
+       phy_exit(musb->phy);
 
        return 0;
 }
@@ -509,8 +525,12 @@ static int omap2430_probe(struct platform_device *pdev)
        glue->dev                       = &pdev->dev;
        glue->musb                      = musb;
        glue->status                    = OMAP_MUSB_UNKNOWN;
+       glue->control_otghs = ERR_PTR(-ENODEV);
 
        if (np) {
+               struct device_node *control_node;
+               struct platform_device *control_pdev;
+
                pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
                if (!pdata) {
                        dev_err(&pdev->dev,
@@ -539,22 +559,20 @@ static int omap2430_probe(struct platform_device *pdev)
                of_property_read_u32(np, "ram-bits", (u32 *)&config->ram_bits);
                of_property_read_u32(np, "power", (u32 *)&pdata->power);
                config->multipoint = of_property_read_bool(np, "multipoint");
-               pdata->has_mailbox = of_property_read_bool(np,
-                   "ti,has-mailbox");
 
                pdata->board_data       = data;
                pdata->config           = config;
-       }
 
-       if (pdata->has_mailbox) {
-               glue->control_otghs = omap_get_control_dev();
-               if (IS_ERR(glue->control_otghs)) {
-                       dev_vdbg(&pdev->dev, "Failed to get control device\n");
-                       ret = PTR_ERR(glue->control_otghs);
-                       goto err2;
+               control_node = of_parse_phandle(np, "ctrl-module", 0);
+               if (control_node) {
+                       control_pdev = of_find_device_by_node(control_node);
+                       if (!control_pdev) {
+                               dev_err(&pdev->dev, "Failed to get control device\n");
+                               ret = -EINVAL;
+                               goto err2;
+                       }
+                       glue->control_otghs = &control_pdev->dev;
                }
-       } else {
-               glue->control_otghs = ERR_PTR(-ENODEV);
        }
        pdata->platform_ops             = &omap2430_ops;
 
@@ -638,7 +656,7 @@ static int omap2430_runtime_suspend(struct device *dev)
                                OTG_INTERFSEL);
 
                omap2430_low_level_exit(musb);
-               usb_phy_set_suspend(musb->xceiv, 1);
+               phy_power_off(musb->phy);
        }
 
        return 0;
@@ -653,8 +671,7 @@ static int omap2430_runtime_resume(struct device *dev)
                omap2430_low_level_init(musb);
                musb_writel(musb->mregs, OTG_INTERFSEL,
                                musb->context.otg_interfsel);
-
-               usb_phy_set_suspend(musb->xceiv, 0);
+               phy_power_on(musb->phy);
        }
 
        return 0;
index d5589f9c60a92e3c96757c8a6ba1fd6a171e01b1..64b8bef1919e7e61dfcfc3c7f7bc4caff1486d1e 100644 (file)
@@ -66,17 +66,6 @@ config OMAP_CONTROL_USB
          power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
          additional register to power on USB3 PHY.
 
-config OMAP_USB2
-       tristate "OMAP USB2 PHY Driver"
-       depends on ARCH_OMAP2PLUS
-       select OMAP_CONTROL_USB
-       select USB_PHY
-       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 OMAP_USB3
        tristate "OMAP USB3 PHY Driver"
        depends on ARCH_OMAP2PLUS || COMPILE_TEST
@@ -93,6 +82,7 @@ config AM335X_CONTROL_USB
 
 config AM335X_PHY_USB
        tristate "AM335x USB PHY Driver"
+       depends on ARM || COMPILE_TEST
        select USB_PHY
        select AM335X_CONTROL_USB
        select NOP_USB_XCEIV
@@ -123,16 +113,6 @@ config SAMSUNG_USB3PHY
          Enable this to support Samsung USB 3.0 (Super Speed) phy controller
          for samsung SoCs.
 
-config TWL4030_USB
-       tristate "TWL4030 USB Transceiver Driver"
-       depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
-       select USB_PHY
-       help
-         Enable this to support the USB OTG transceiver on TWL4030
-         family chips (including the TWL5030 and TPS659x0 devices).
-         This transceiver supports high and full speed devices plus,
-         in host mode, low speed.
-
 config TWL6030_USB
        tristate "TWL6030 USB Transceiver Driver"
        depends on TWL4030_CORE && OMAP_USB2 && USB_MUSB_OMAP2PLUS
index 2135e85f46eda29f104940467154a789db870683..9c3736109c2cb3ee35e0fa866b9b7cd3ee1df97b 100644 (file)
@@ -15,12 +15,10 @@ obj-$(CONFIG_NOP_USB_XCEIV)         += phy-generic.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_USB2)                        += phy-omap-usb2.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
-obj-$(CONFIG_TWL4030_USB)              += phy-twl4030-usb.o
 obj-$(CONFIG_TWL6030_USB)              += phy-twl6030-usb.o
 obj-$(CONFIG_USB_EHCI_TEGRA)           += phy-tegra-usb.o
 obj-$(CONFIG_USB_GPIO_VBUS)            += phy-gpio-vbus-usb.o
index a4dda8e1256257104cdfdd3d54707af57361ed8f..09c5ace1edd8a987f015508640f7f461508dd0a9 100644 (file)
 #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>
 
-static struct omap_control_usb *control_usb;
-
-/**
- * omap_get_control_dev - returns the device pointer for this control device
- *
- * This API should be called to get the device pointer for this control
- * module device. This device pointer should be used for called other
- * exported API's in this driver.
- *
- * To be used by PHY driver and glue driver.
- */
-struct device *omap_get_control_dev(void)
-{
-       if (!control_usb)
-               return ERR_PTR(-ENODEV);
-
-       return control_usb->dev;
-}
-EXPORT_SYMBOL_GPL(omap_get_control_dev);
-
 /**
- * omap_control_usb3_phy_power - power on/off the serializer using control
- *     module
+ * omap_control_usb_phy_power - power on/off the phy using control module reg
  * @dev: the control module device
- * @on: 0 to off and 1 to on based on powering on or off the PHY
- *
- * usb3 PHY driver should call this API to power on or off the PHY.
+ * @on: 0 or 1, based on powering on or off the PHY
  */
-void omap_control_usb3_phy_power(struct device *dev, bool on)
+void omap_control_usb_phy_power(struct device *dev, int on)
 {
        u32 val;
        unsigned long rate;
-       struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+       struct omap_control_usb *control_usb;
 
-       if (control_usb->type != OMAP_CTRL_DEV_TYPE2)
+       if (IS_ERR(dev) || !dev) {
+               pr_err("%s: invalid device\n", __func__);
                return;
+       }
 
-       rate = clk_get_rate(control_usb->sys_clk);
-       rate = rate/1000000;
-
-       val = readl(control_usb->phy_power);
-
-       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;
+       control_usb = dev_get_drvdata(dev);
+       if (!control_usb) {
+               dev_err(dev, "%s: invalid control usb device\n", __func__);
+               return;
        }
 
-       writel(val, control_usb->phy_power);
-}
-EXPORT_SYMBOL_GPL(omap_control_usb3_phy_power);
+       if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
+               return;
 
-/**
- * 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;
-       struct omap_control_usb *control_usb = dev_get_drvdata(dev);
+       val = readl(control_usb->power);
 
-       val = readl(control_usb->dev_conf);
+       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;
 
-       if (on)
-               val &= ~OMAP_CTRL_DEV_PHY_PD;
-       else
-               val |= OMAP_CTRL_DEV_PHY_PD;
+       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;
 
-       writel(val, control_usb->dev_conf);
+       case OMAP_CTRL_TYPE_DRA7USB2:
+               if (on)
+                       val &= ~OMAP_CTRL_USB2_PHY_PD;
+               else
+                       val |= OMAP_CTRL_USB2_PHY_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);
 
@@ -172,11 +162,19 @@ void omap_control_usb_set_mode(struct device *dev,
 {
        struct omap_control_usb *ctrl_usb;
 
-       if (IS_ERR(dev) || control_usb->type != OMAP_CTRL_DEV_TYPE1)
+       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);
@@ -193,12 +191,46 @@ void omap_control_usb_set_mode(struct device *dev,
 }
 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 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,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
+#endif
+
+
 static int omap_control_usb_probe(struct platform_device *pdev)
 {
        struct resource *res;
-       struct device_node *np = pdev->dev.of_node;
-       struct omap_control_usb_platform_data *pdata =
-                       dev_get_platdata(&pdev->dev);
+       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);
@@ -207,40 +239,27 @@ static int omap_control_usb_probe(struct platform_device *pdev)
                return -ENOMEM;
        }
 
-       if (np) {
-               of_property_read_u32(np, "ti,type", &control_usb->type);
-       } else if (pdata) {
-               control_usb->type = pdata->type;
-       } else {
-               dev_err(&pdev->dev, "no pdata present\n");
-               return -EINVAL;
-       }
-
-       control_usb->dev        = &pdev->dev;
-
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-               "control_dev_conf");
-       control_usb->dev_conf = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(control_usb->dev_conf))
-               return PTR_ERR(control_usb->dev_conf);
+       control_usb->dev = &pdev->dev;
+       control_usb->type = *(enum omap_control_usb_type *)of_id->data;
 
-       if (control_usb->type == OMAP_CTRL_DEV_TYPE1) {
+       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);
-       }
-
-       if (control_usb->type == OMAP_CTRL_DEV_TYPE2) {
+       } else {
                res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
-                       "phy_power_usb");
-               control_usb->phy_power = devm_ioremap_resource(
-                       &pdev->dev, res);
-               if (IS_ERR(control_usb->phy_power))
-                       return PTR_ERR(control_usb->phy_power);
+                               "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)) {
@@ -249,20 +268,11 @@ static int omap_control_usb_probe(struct platform_device *pdev)
                }
        }
 
-
        dev_set_drvdata(control_usb->dev, control_usb);
 
        return 0;
 }
 
-#ifdef CONFIG_OF
-static const struct of_device_id omap_control_usb_id_table[] = {
-       { .compatible = "ti,omap-control-usb" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
-#endif
-
 static struct platform_driver omap_control_usb_driver = {
        .probe          = omap_control_usb_probe,
        .driver         = {
index 4e8a0405f956c478491a1ed6f18d65a9afc07e9a..0c6ba29bddddc05b7b3c35e2067c026eec13bc2f 100644 (file)
@@ -26,6 +26,7 @@
 #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
@@ -100,7 +101,7 @@ static int omap_usb3_suspend(struct usb_phy *x, int suspend)
                        udelay(1);
                } while (--timeout);
 
-               omap_control_usb3_phy_power(phy->control_dev, 0);
+               omap_control_usb_phy_power(phy->control_dev, 0);
 
                phy->is_suspended       = 1;
        } else if (!suspend && phy->is_suspended) {
@@ -189,15 +190,21 @@ static int omap_usb3_init(struct usb_phy *x)
        if (ret)
                return ret;
 
-       omap_control_usb3_phy_power(phy->control_dev, 1);
+       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 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) {
@@ -239,13 +246,20 @@ static int omap_usb3_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       phy->control_dev = omap_get_control_dev();
-       if (IS_ERR(phy->control_dev)) {
-               dev_dbg(&pdev->dev, "Failed to get control device\n");
-               return -ENODEV;
+       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_usb3_phy_power(phy->control_dev, 0);
+       omap_control_usb_phy_power(phy->control_dev, 0);
        usb_add_phy_dev(&phy->phy);
 
        platform_set_drvdata(pdev, phy);
index 7c22a5390fc34dddf8e6f9987da559d32732a406..18bb8264b5a0565e91f714df9544fa8f27b9b189 100644 (file)
@@ -36,7 +36,7 @@ static int ulpi_viewport_wait(void __iomem *view, u32 mask)
                        return 0;
 
                udelay(1);
-       };
+       }
 
        return -ETIMEDOUT;
 }
index 781426230d695a3f34544ab4b0a589a500a0063e..6e1b69d0f5f57da980f439a3643567ada4aec3b6 100644 (file)
@@ -279,7 +279,7 @@ static void cyberjack_read_int_callback(struct urb *urb)
 
                old_rdtodo = priv->rdtodo;
 
-               if (old_rdtodo + size < old_rdtodo) {
+               if (old_rdtodo > SHRT_MAX - size) {
                        dev_dbg(dev, "To many bulk_in urbs to do.\n");
                        spin_unlock(&priv->lock);
                        goto resubmit;
index c45f9c0a1b3493f8f5566c87d6b8702f9fc8116c..f53298d320991d31ab7340dfb92c17db9ca49a06 100644 (file)
@@ -1966,8 +1966,16 @@ static int ftdi_process_packet(struct usb_serial_port *port,
                        port->icount.dsr++;
                if (diff_status & FTDI_RS0_RI)
                        port->icount.rng++;
-               if (diff_status & FTDI_RS0_RLSD)
+               if (diff_status & FTDI_RS0_RLSD) {
+                       struct tty_struct *tty;
+
                        port->icount.dcd++;
+                       tty = tty_port_tty_get(&port->port);
+                       if (tty)
+                               usb_serial_handle_dcd_change(port, tty,
+                                               status & FTDI_RS0_RLSD);
+                       tty_kref_put(tty);
+               }
 
                wake_up_interruptible(&port->port.delta_msr_wait);
                priv->prev_status = status;
index 1f31e6b4c2518f262a263bbefdddf5fda6ba9cb6..2b01ec8651c296e3f016bf2421040da6f1bbbc98 100644 (file)
@@ -7,7 +7,6 @@
  *     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/kernel.h>
@@ -37,7 +36,6 @@ MODULE_PARM_DESC(product, "User specified USB idProduct");
 
 static struct usb_device_id generic_device_ids[2]; /* Initially all zeroes. */
 
-/* All of the device info needed for the Generic Serial Converter */
 struct usb_serial_driver usb_serial_generic_device = {
        .driver = {
                .owner =        THIS_MODULE,
@@ -66,7 +64,6 @@ int usb_serial_generic_register(void)
        generic_device_ids[0].match_flags =
                USB_DEVICE_ID_MATCH_VENDOR | USB_DEVICE_ID_MATCH_PRODUCT;
 
-       /* register our generic driver with ourselves */
        retval = usb_serial_register_drivers(serial_drivers,
                        "usbserial_generic", generic_device_ids);
 #endif
@@ -76,7 +73,6 @@ int usb_serial_generic_register(void)
 void usb_serial_generic_deregister(void)
 {
 #ifdef CONFIG_USB_SERIAL_GENERIC
-       /* remove our generic driver */
        usb_serial_deregister_drivers(serial_drivers);
 #endif
 }
@@ -86,13 +82,11 @@ int usb_serial_generic_open(struct tty_struct *tty, struct usb_serial_port *port
        int result = 0;
        unsigned long flags;
 
-       /* clear the throttle flags */
        spin_lock_irqsave(&port->lock, flags);
        port->throttled = 0;
        port->throttle_req = 0;
        spin_unlock_irqrestore(&port->lock, flags);
 
-       /* if we have a bulk endpoint, start reading from it */
        if (port->bulk_in_size)
                result = usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
 
@@ -127,12 +121,16 @@ int usb_serial_generic_prepare_write_buffer(struct usb_serial_port *port,
 }
 
 /**
- * usb_serial_generic_write_start - kick off an URB write
- * @port:      Pointer to the &struct usb_serial_port data
+ * usb_serial_generic_write_start - start writing buffered data
+ * @port: usb-serial port
+ * @mem_flags: flags to use for memory allocations
+ *
+ * Serialised using USB_SERIAL_WRITE_BUSY flag.
  *
- * Returns zero on success, or a negative errno value
+ * Return: Zero on success or if busy, otherwise a negative errno value.
  */
-static int usb_serial_generic_write_start(struct usb_serial_port *port)
+int usb_serial_generic_write_start(struct usb_serial_port *port,
+                                                       gfp_t mem_flags)
 {
        struct urb *urb;
        int count, result;
@@ -163,7 +161,7 @@ retry:
        spin_unlock_irqrestore(&port->lock, flags);
 
        clear_bit(i, &port->write_urbs_free);
-       result = usb_submit_urb(urb, GFP_ATOMIC);
+       result = usb_submit_urb(urb, mem_flags);
        if (result) {
                dev_err_console(port, "%s - error submitting urb: %d\n",
                                                __func__, result);
@@ -175,34 +173,34 @@ retry:
                clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
                return result;
        }
-
-       /* Try sending off another urb, unless in irq context (in which case
-        * there will be no free urb). */
-       if (!in_irq())
+       /*
+        * Try sending off another urb, unless called from completion handler
+        * (in which case there will be no free urb or no data).
+        */
+       if (mem_flags != GFP_ATOMIC)
                goto retry;
 
        clear_bit_unlock(USB_SERIAL_WRITE_BUSY, &port->flags);
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(usb_serial_generic_write_start);
 
 /**
- * usb_serial_generic_write - generic write function for serial USB devices
- * @tty:       Pointer to &struct tty_struct for the device
- * @port:      Pointer to the &usb_serial_port structure for the device
- * @buf:       Pointer to the data to write
- * @count:     Number of bytes to write
+ * usb_serial_generic_write - generic write function
+ * @tty: tty for the port
+ * @port: usb-serial port
+ * @buf: data to write
+ * @count: number of bytes to write
  *
- * Returns the number of characters actually written, which may be anything
- * from zero to @count. If an error occurs, it returns the negative errno
- * value.
+ * Return: The number of characters buffered, which may be anything from
+ * zero to @count, or a negative errno value.
  */
 int usb_serial_generic_write(struct tty_struct *tty,
        struct usb_serial_port *port, const unsigned char *buf, int count)
 {
        int result;
 
-       /* only do something if we have a bulk out endpoint */
        if (!port->bulk_out_size)
                return -ENODEV;
 
@@ -210,7 +208,7 @@ int usb_serial_generic_write(struct tty_struct *tty,
                return 0;
 
        count = kfifo_in_locked(&port->write_fifo, buf, count, &port->lock);
-       result = usb_serial_generic_write_start(port);
+       result = usb_serial_generic_write_start(port, GFP_KERNEL);
        if (result)
                return result;
 
@@ -337,10 +335,11 @@ void usb_serial_generic_process_read_urb(struct urb *urb)
 
        if (!urb->actual_length)
                return;
-
-       /* The per character mucking around with sysrq path it too slow for
-          stuff like 3G modems, so shortcircuit it in the 99.9999999% of cases
-          where the USB serial is not a console anyway */
+       /*
+        * The per character mucking around with sysrq path it too slow for
+        * 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)
                tty_insert_flip_string(&port->port, ch, urb->actual_length);
        else {
@@ -413,7 +412,7 @@ void usb_serial_generic_write_bulk_callback(struct urb *urb)
                kfifo_reset_out(&port->write_fifo);
                spin_unlock_irqrestore(&port->lock, flags);
        } else {
-               usb_serial_generic_write_start(port);
+               usb_serial_generic_write_start(port, GFP_ATOMIC);
        }
 
        usb_serial_port_softint(port);
@@ -425,8 +424,6 @@ void usb_serial_generic_throttle(struct tty_struct *tty)
        struct usb_serial_port *port = tty->driver_data;
        unsigned long flags;
 
-       /* Set the throttle request flag. It will be picked up
-        * by usb_serial_generic_read_bulk_callback(). */
        spin_lock_irqsave(&port->lock, flags);
        port->throttle_req = 1;
        spin_unlock_irqrestore(&port->lock, flags);
@@ -438,7 +435,6 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
        struct usb_serial_port *port = tty->driver_data;
        int was_throttled;
 
-       /* Clear the throttle flags */
        spin_lock_irq(&port->lock);
        was_throttled = port->throttled;
        port->throttled = port->throttle_req = 0;
@@ -558,10 +554,10 @@ int usb_serial_handle_break(struct usb_serial_port *port)
 EXPORT_SYMBOL_GPL(usb_serial_handle_break);
 
 /**
- *     usb_serial_handle_dcd_change - handle a change of carrier detect state
- *     @port: usb_serial_port structure for the open port
- *     @tty: tty_struct structure for the port
- *     @status: new carrier detect status, nonzero if active
+ * usb_serial_handle_dcd_change - handle a change of carrier detect state
+ * @port: usb-serial port
+ * @tty: tty for the port
+ * @status: new carrier detect status, nonzero if active
  */
 void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
                                struct tty_struct *tty, unsigned int status)
@@ -570,6 +566,16 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
 
        dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status);
 
+       if (tty) {
+               struct tty_ldisc *ld = tty_ldisc_ref(tty);
+
+               if (ld) {
+                       if (ld->ops->dcd_change)
+                               ld->ops->dcd_change(tty, status);
+                       tty_ldisc_deref(ld);
+               }
+       }
+
        if (status)
                wake_up_interruptible(&port->open_wait);
        else if (tty && !C_CLOCAL(tty))
@@ -595,7 +601,7 @@ int usb_serial_generic_resume(struct usb_serial *serial)
                }
 
                if (port->bulk_out_size) {
-                       r = usb_serial_generic_write_start(port);
+                       r = usb_serial_generic_write_start(port, GFP_NOIO);
                        if (r < 0)
                                c++;
                }
index fdf953539c62b4943a5ef1c6f93eab52cd53e463..e5bdd987b9e8f7e260fc95c48b142742026999fa 100644 (file)
@@ -1532,7 +1532,11 @@ static int mos7840_tiocmget(struct tty_struct *tty)
                return -ENODEV;
 
        status = mos7840_get_uart_reg(port, MODEM_STATUS_REGISTER, &msr);
+       if (status != 1)
+               return -EIO;
        status = mos7840_get_uart_reg(port, MODEM_CONTROL_REGISTER, &mcr);
+       if (status != 1)
+               return -EIO;
        result = ((mcr & MCR_DTR) ? TIOCM_DTR : 0)
            | ((mcr & MCR_RTS) ? TIOCM_RTS : 0)
            | ((mcr & MCR_LOOPBACK) ? TIOCM_LOOP : 0)
index 7f78f300f8fbe02674aa7f1f0846d8399c3be0f8..f06ed82e63d139fa2d25efd1ef2ac7cf5545e995 100644 (file)
@@ -208,9 +208,9 @@ static int cbaf_check(struct cbaf *cbaf)
                                ar_name = "ASSOCIATE";
                                ar_assoc = 1;
                                break;
-                       };
+                       }
                        break;
-               };
+               }
 
                dev_dbg(dev, "Association request #%02u: 0x%04x/%04x "
                         "(%zu bytes): %s\n",
@@ -623,6 +623,8 @@ static int cbaf_probe(struct usb_interface *iface,
 
 error_create_group:
 error_check:
+       usb_put_intf(iface);
+       usb_put_dev(cbaf->usb_dev);
        kfree(cbaf->buffer);
 error_kmalloc_buffer:
        kfree(cbaf);
@@ -637,6 +639,7 @@ static void cbaf_disconnect(struct usb_interface *iface)
        sysfs_remove_group(&dev->kobj, &cbaf_dev_attr_group);
        usb_set_intfdata(iface, NULL);
        usb_put_intf(iface);
+       usb_put_dev(cbaf->usb_dev);
        kfree(cbaf->buffer);
        /* paranoia: clean up crypto keys */
        kzfree(cbaf);
index 33a12788f9ca063c8aae13ce28d9b978f27a372c..e538b72c4e3af2d09153614952e12101eac03520 100644 (file)
@@ -973,7 +973,7 @@ int wusb_usb_ncb(struct notifier_block *nb, unsigned long val,
        default:
                WARN_ON(1);
                result = NOTIFY_BAD;
-       };
+       }
        return result;
 }
 
index a09b65ebd9bb712d02060e760ff2c349bcdc2e77..6c09b0e4672b68529cd591e2b0bcc9da57524106 100644 (file)
@@ -44,11 +44,11 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
        /* Fill up Data Transfer EP pointers */
        wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
        wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
-       wa->xfer_result_size = usb_endpoint_maxp(wa->dti_epd);
-       wa->xfer_result = kmalloc(wa->xfer_result_size, GFP_KERNEL);
-       if (wa->xfer_result == NULL) {
+       wa->dti_buf_size = usb_endpoint_maxp(wa->dti_epd);
+       wa->dti_buf = kmalloc(wa->dti_buf_size, GFP_KERNEL);
+       if (wa->dti_buf == NULL) {
                result = -ENOMEM;
-               goto error_xfer_result_alloc;
+               goto error_dti_buf_alloc;
        }
        result = wa_nep_create(wa, iface);
        if (result < 0) {
@@ -59,8 +59,8 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
        return 0;
 
 error_nep_create:
-       kfree(wa->xfer_result);
-error_xfer_result_alloc:
+       kfree(wa->dti_buf);
+error_dti_buf_alloc:
        wa_rpipes_destroy(wa);
 error_rpipes_create:
        return result;
@@ -76,7 +76,7 @@ void __wa_destroy(struct wahc *wa)
                usb_kill_urb(wa->buf_in_urb);
                usb_put_urb(wa->buf_in_urb);
        }
-       kfree(wa->xfer_result);
+       kfree(wa->dti_buf);
        wa_nep_destroy(wa);
        wa_rpipes_destroy(wa);
 }
index cf250c21e946bcab938baafc6ca3f48ca2f1a2d3..41afaa6d01d28166e9e84435f715e6550132e445 100644 (file)
@@ -117,11 +117,17 @@ struct wa_rpipe {
        struct wahc *wa;
        spinlock_t seg_lock;
        struct list_head seg_list;
+       struct list_head list_node;
        atomic_t segs_available;
        u8 buffer[1];   /* For reads/writes on USB */
 };
 
 
+enum wa_dti_state {
+       WA_DTI_TRANSFER_RESULT_PENDING,
+       WA_DTI_ISOC_PACKET_STATUS_PENDING
+};
+
 /**
  * Instance of a HWA Host Controller
  *
@@ -178,14 +184,26 @@ struct wahc {
 
        u16 rpipes;
        unsigned long *rpipe_bm;        /* rpipe usage bitmap */
-       spinlock_t rpipe_bm_lock;       /* protect rpipe_bm */
+       struct list_head rpipe_delayed_list;    /* delayed RPIPES. */
+       spinlock_t rpipe_lock;  /* protect rpipe_bm and delayed list */
        struct mutex rpipe_mutex;       /* assigning resources to endpoints */
 
+       /*
+        * dti_state is used to track the state of the dti_urb.  When dti_state
+        * is WA_DTI_ISOC_PACKET_STATUS_PENDING, dti_isoc_xfer_in_progress and
+        * dti_isoc_xfer_seg identify which xfer the incoming isoc packet status
+        * refers to.
+        */
+       enum wa_dti_state dti_state;
+       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 */
        struct edc dti_edc;             /* DTI error density counter */
-       struct wa_xfer_result *xfer_result; /* real size = dti_ep maxpktsize */
-       size_t xfer_result_size;
+       void *dti_buf;
+       size_t dti_buf_size;
+
+       unsigned long dto_in_use;       /* protect dto endoint serialization. */
 
        s32 status;                     /* For reading status */
 
@@ -239,7 +257,8 @@ static inline void wa_nep_disarm(struct wahc *wa)
 /* RPipes */
 static inline void wa_rpipe_init(struct wahc *wa)
 {
-       spin_lock_init(&wa->rpipe_bm_lock);
+       INIT_LIST_HEAD(&wa->rpipe_delayed_list);
+       spin_lock_init(&wa->rpipe_lock);
        mutex_init(&wa->rpipe_mutex);
 }
 
@@ -247,6 +266,7 @@ static inline void wa_init(struct wahc *wa)
 {
        edc_init(&wa->nep_edc);
        atomic_set(&wa->notifs_queued, 0);
+       wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
        wa_rpipe_init(wa);
        edc_init(&wa->dti_edc);
        INIT_LIST_HEAD(&wa->xfer_list);
@@ -255,6 +275,7 @@ static inline void wa_init(struct wahc *wa)
        spin_lock_init(&wa->xfer_list_lock);
        INIT_WORK(&wa->xfer_enqueue_work, wa_urb_enqueue_run);
        INIT_WORK(&wa->xfer_error_work, wa_process_errored_transfers_run);
+       wa->dto_in_use = 0;
        atomic_set(&wa->xfer_id_count, 1);
 }
 
index fd4f1ce6256ac7a60fd2a9d5745e88b6f3c98d63..1ed068accb76d41d8b06fe95d9d9fa55d1fe5b19 100644 (file)
@@ -143,17 +143,18 @@ static void rpipe_init(struct wa_rpipe *rpipe)
        kref_init(&rpipe->refcnt);
        spin_lock_init(&rpipe->seg_lock);
        INIT_LIST_HEAD(&rpipe->seg_list);
+       INIT_LIST_HEAD(&rpipe->list_node);
 }
 
 static unsigned rpipe_get_idx(struct wahc *wa, unsigned rpipe_idx)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
        rpipe_idx = find_next_zero_bit(wa->rpipe_bm, wa->rpipes, rpipe_idx);
        if (rpipe_idx < wa->rpipes)
                set_bit(rpipe_idx, wa->rpipe_bm);
-       spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
 
        return rpipe_idx;
 }
@@ -162,9 +163,9 @@ static void rpipe_put_idx(struct wahc *wa, unsigned rpipe_idx)
 {
        unsigned long flags;
 
-       spin_lock_irqsave(&wa->rpipe_bm_lock, flags);
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
        clear_bit(rpipe_idx, wa->rpipe_bm);
-       spin_unlock_irqrestore(&wa->rpipe_bm_lock, flags);
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
 }
 
 void rpipe_destroy(struct kref *_rpipe)
@@ -361,8 +362,10 @@ static int rpipe_aim(struct wa_rpipe *rpipe, struct wahc *wa,
                        epcd->bMaxSequence, 32U), 2U);
        rpipe->descr.bMaxDataSequence = epcd_max_sequence - 1;
        rpipe->descr.bInterval = ep->desc.bInterval;
-       /* FIXME: bOverTheAirInterval */
-       rpipe->descr.bOverTheAirInterval = 0;   /* 0 if not isoc */
+       if (usb_endpoint_xfer_isoc(&ep->desc))
+               rpipe->descr.bOverTheAirInterval = epcd->bOverTheAirInterval;
+       else
+               rpipe->descr.bOverTheAirInterval = 0;   /* 0 if not isoc */
        /* FIXME: xmit power & preamble blah blah */
        rpipe->descr.bmAttribute = (ep->desc.bmAttributes &
                                        USB_ENDPOINT_XFERTYPE_MASK);
@@ -477,7 +480,7 @@ error:
  */
 int wa_rpipes_create(struct wahc *wa)
 {
-       wa->rpipes = wa->wa_descr->wNumRPipes;
+       wa->rpipes = le16_to_cpu(wa->wa_descr->wNumRPipes);
        wa->rpipe_bm = kzalloc(BITS_TO_LONGS(wa->rpipes)*sizeof(unsigned long),
                               GFP_KERNEL);
        if (wa->rpipe_bm == NULL)
index 6ad02f57c366706e85ab4e3d03fa2d6cdc006a79..f1e9a386beca6a4a92da3242796ec78afccd4e5a 100644 (file)
@@ -107,6 +107,7 @@ enum wa_seg_status {
 };
 
 static void wa_xfer_delayed_run(struct wa_rpipe *);
+static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting);
 
 /*
  * Life cycle governed by 'struct urb' (the refcount of the struct is
@@ -114,24 +115,24 @@ static void wa_xfer_delayed_run(struct wa_rpipe *);
  * struct).
  */
 struct wa_seg {
-       struct urb urb;
-       struct urb *dto_urb;            /* for data output? */
+       struct urb tr_urb;              /* transfer request urb. */
+       struct urb *isoc_pack_desc_urb; /* for isoc packet descriptor. */
+       struct urb *dto_urb;            /* for data output. */
        struct list_head list_node;     /* for rpipe->req_list */
        struct wa_xfer *xfer;           /* out xfer */
        u8 index;                       /* which segment we are */
        enum wa_seg_status status;
        ssize_t result;                 /* bytes xfered or error */
        struct wa_xfer_hdr xfer_hdr;
-       u8 xfer_extra[];                /* xtra space for xfer_hdr_ctl */
 };
 
 static inline void wa_seg_init(struct wa_seg *seg)
 {
-       usb_init_urb(&seg->urb);
+       usb_init_urb(&seg->tr_urb);
 
        /* set the remaining memory to 0. */
-       memset(((void *)seg) + sizeof(seg->urb), 0,
-               sizeof(*seg) - sizeof(seg->urb));
+       memset(((void *)seg) + sizeof(seg->tr_urb), 0,
+               sizeof(*seg) - sizeof(seg->tr_urb));
 }
 
 /*
@@ -169,7 +170,7 @@ static inline void wa_xfer_init(struct wa_xfer *xfer)
 /*
  * Destroy a transfer structure
  *
- * Note that freeing xfer->seg[cnt]->urb will free the containing
+ * Note that freeing xfer->seg[cnt]->tr_urb will free the containing
  * xfer->seg[cnt] memory that was allocated by __wa_xfer_setup_segs.
  */
 static void wa_xfer_destroy(struct kref *_xfer)
@@ -178,9 +179,17 @@ static void wa_xfer_destroy(struct kref *_xfer)
        if (xfer->seg) {
                unsigned cnt;
                for (cnt = 0; cnt < xfer->segs; cnt++) {
-                       usb_free_urb(xfer->seg[cnt]->dto_urb);
-                       usb_free_urb(&xfer->seg[cnt]->urb);
+                       struct wa_seg *seg = xfer->seg[cnt];
+                       if (seg) {
+                               usb_free_urb(seg->isoc_pack_desc_urb);
+                               if (seg->dto_urb) {
+                                       kfree(seg->dto_urb->sg);
+                                       usb_free_urb(seg->dto_urb);
+                               }
+                               usb_free_urb(&seg->tr_urb);
+                       }
                }
+               kfree(xfer->seg);
        }
        kfree(xfer);
 }
@@ -195,6 +204,59 @@ static void wa_xfer_put(struct wa_xfer *xfer)
        kref_put(&xfer->refcnt, wa_xfer_destroy);
 }
 
+/*
+ * Try to get exclusive access to the DTO endpoint resource.  Return true
+ * if successful.
+ */
+static inline int __wa_dto_try_get(struct wahc *wa)
+{
+       return (test_and_set_bit(0, &wa->dto_in_use) == 0);
+}
+
+/* Release the DTO endpoint resource. */
+static inline void __wa_dto_put(struct wahc *wa)
+{
+       clear_bit_unlock(0, &wa->dto_in_use);
+}
+
+/* Service RPIPEs that are waiting on the DTO resource. */
+static void wa_check_for_delayed_rpipes(struct wahc *wa)
+{
+       unsigned long flags;
+       int dto_waiting = 0;
+       struct wa_rpipe *rpipe;
+
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
+       while (!list_empty(&wa->rpipe_delayed_list) && !dto_waiting) {
+               rpipe = list_first_entry(&wa->rpipe_delayed_list,
+                               struct wa_rpipe, list_node);
+               __wa_xfer_delayed_run(rpipe, &dto_waiting);
+               /* remove this RPIPE from the list if it is not waiting. */
+               if (!dto_waiting) {
+                       pr_debug("%s: RPIPE %d serviced and removed from delayed list.\n",
+                               __func__,
+                               le16_to_cpu(rpipe->descr.wRPipeIndex));
+                       list_del_init(&rpipe->list_node);
+               }
+       }
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
+}
+
+/* add this RPIPE to the end of the delayed RPIPE list. */
+static void wa_add_delayed_rpipe(struct wahc *wa, struct wa_rpipe *rpipe)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&wa->rpipe_lock, flags);
+       /* add rpipe to the list if it is not already on it. */
+       if (list_empty(&rpipe->list_node)) {
+               pr_debug("%s: adding RPIPE %d to the delayed list.\n",
+                       __func__, le16_to_cpu(rpipe->descr.wRPipeIndex));
+               list_add_tail(&rpipe->list_node, &wa->rpipe_delayed_list);
+       }
+       spin_unlock_irqrestore(&wa->rpipe_lock, flags);
+}
+
 /*
  * xfer is referenced
  *
@@ -231,6 +293,31 @@ static void wa_xfer_completion(struct wa_xfer *xfer)
        wa_xfer_giveback(xfer);
 }
 
+/*
+ * Initialize a transfer's ID
+ *
+ * We need to use a sequential number; if we use the pointer or the
+ * hash of the pointer, it can repeat over sequential transfers and
+ * then it will confuse the HWA....wonder why in hell they put a 32
+ * bit handle in there then.
+ */
+static void wa_xfer_id_init(struct wa_xfer *xfer)
+{
+       xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
+}
+
+/* Return the xfer's ID. */
+static inline u32 wa_xfer_id(struct wa_xfer *xfer)
+{
+       return xfer->id;
+}
+
+/* Return the xfer's ID in transport format (little endian). */
+static inline __le32 wa_xfer_id_le32(struct wa_xfer *xfer)
+{
+       return cpu_to_le32(xfer->id);
+}
+
 /*
  * If transfer is done, wrap it up and return true
  *
@@ -253,33 +340,37 @@ static unsigned __wa_xfer_is_done(struct wa_xfer *xfer)
                switch (seg->status) {
                case WA_SEG_DONE:
                        if (found_short && seg->result > 0) {
-                               dev_dbg(dev, "xfer %p#%u: bad short segments (%zu)\n",
-                                       xfer, cnt, seg->result);
+                               dev_dbg(dev, "xfer %p ID %08X#%u: bad short segments (%zu)\n",
+                                       xfer, wa_xfer_id(xfer), cnt,
+                                       seg->result);
                                urb->status = -EINVAL;
                                goto out;
                        }
                        urb->actual_length += seg->result;
-                       if (seg->result < xfer->seg_size
+                       if (!(usb_pipeisoc(xfer->urb->pipe))
+                               && seg->result < xfer->seg_size
                            && cnt != xfer->segs-1)
                                found_short = 1;
-                       dev_dbg(dev, "xfer %p#%u: DONE short %d "
+                       dev_dbg(dev, "xfer %p ID %08X#%u: DONE short %d "
                                "result %zu urb->actual_length %d\n",
-                               xfer, seg->index, found_short, seg->result,
-                               urb->actual_length);
+                               xfer, wa_xfer_id(xfer), seg->index, found_short,
+                               seg->result, urb->actual_length);
                        break;
                case WA_SEG_ERROR:
                        xfer->result = seg->result;
-                       dev_dbg(dev, "xfer %p#%u: ERROR result %zu\n",
-                               xfer, seg->index, seg->result);
+                       dev_dbg(dev, "xfer %p ID %08X#%u: ERROR result %zu(0x%08zX)\n",
+                               xfer, wa_xfer_id(xfer), seg->index, seg->result,
+                               seg->result);
                        goto out;
                case WA_SEG_ABORTED:
-                       dev_dbg(dev, "xfer %p#%u ABORTED: result %d\n",
-                               xfer, seg->index, urb->status);
+                       dev_dbg(dev, "xfer %p ID %08X#%u ABORTED: result %d\n",
+                               xfer, wa_xfer_id(xfer), seg->index,
+                               urb->status);
                        xfer->result = urb->status;
                        goto out;
                default:
-                       dev_warn(dev, "xfer %p#%u: is_done bad state %d\n",
-                                xfer, cnt, seg->status);
+                       dev_warn(dev, "xfer %p ID %08X#%u: is_done bad state %d\n",
+                                xfer, wa_xfer_id(xfer), cnt, seg->status);
                        xfer->result = -EINVAL;
                        goto out;
                }
@@ -289,29 +380,6 @@ out:
        return result;
 }
 
-/*
- * Initialize a transfer's ID
- *
- * We need to use a sequential number; if we use the pointer or the
- * hash of the pointer, it can repeat over sequential transfers and
- * then it will confuse the HWA....wonder why in hell they put a 32
- * bit handle in there then.
- */
-static void wa_xfer_id_init(struct wa_xfer *xfer)
-{
-       xfer->id = atomic_add_return(1, &xfer->wa->xfer_id_count);
-}
-
-/*
- * Return the xfer's ID associated with xfer
- *
- * Need to generate a
- */
-static u32 wa_xfer_id(struct wa_xfer *xfer)
-{
-       return xfer->id;
-}
-
 /*
  * Search for a transfer list ID on the HCD's URB list
  *
@@ -356,15 +424,11 @@ static void __wa_xfer_abort_cb(struct urb *urb)
  *
  * The callback (see above) does nothing but freeing up the data by
  * putting the URB. Because the URB is allocated at the head of the
- * struct, the whole space we allocated is kfreed.
- *
- * We'll get an 'aborted transaction' xfer result on DTI, that'll
- * politely ignore because at this point the transaction has been
- * marked as aborted already.
+ * struct, the whole space we allocated is kfreed. *
  */
-static void __wa_xfer_abort(struct wa_xfer *xfer)
+static int __wa_xfer_abort(struct wa_xfer *xfer)
 {
-       int result;
+       int result = -ENOMEM;
        struct device *dev = &xfer->wa->usb_iface->dev;
        struct wa_xfer_abort_buffer *b;
        struct wa_rpipe *rpipe = xfer->ep->hcpriv;
@@ -375,7 +439,7 @@ static void __wa_xfer_abort(struct wa_xfer *xfer)
        b->cmd.bLength =  sizeof(b->cmd);
        b->cmd.bRequestType = WA_XFER_ABORT;
        b->cmd.wRPipe = rpipe->descr.wRPipeIndex;
-       b->cmd.dwTransferID = wa_xfer_id(xfer);
+       b->cmd.dwTransferID = wa_xfer_id_le32(xfer);
 
        usb_init_urb(&b->urb);
        usb_fill_bulk_urb(&b->urb, xfer->wa->usb_dev,
@@ -385,7 +449,7 @@ static void __wa_xfer_abort(struct wa_xfer *xfer)
        result = usb_submit_urb(&b->urb, GFP_ATOMIC);
        if (result < 0)
                goto error_submit;
-       return;                         /* callback frees! */
+       return result;                          /* callback frees! */
 
 
 error_submit:
@@ -394,7 +458,7 @@ error_submit:
                        xfer, result);
        kfree(b);
 error_kmalloc:
-       return;
+       return result;
 
 }
 
@@ -422,39 +486,53 @@ static ssize_t __wa_xfer_setup_sizes(struct wa_xfer *xfer,
                result = sizeof(struct wa_xfer_bi);
                break;
        case USB_ENDPOINT_XFER_ISOC:
-               dev_err(dev, "FIXME: ISOC not implemented\n");
-               result = -ENOSYS;
-               goto error;
+               if (usb_pipeout(urb->pipe)) {
+                       *pxfer_type = WA_XFER_TYPE_ISO;
+                       result = sizeof(struct wa_xfer_hwaiso);
+               } else {
+                       dev_err(dev, "FIXME: ISOC IN not implemented\n");
+                       result = -ENOSYS;
+                       goto error;
+               }
+               break;
        default:
                /* never happens */
                BUG();
                result = -EINVAL;       /* shut gcc up */
-       };
+       }
        xfer->is_inbound = urb->pipe & USB_DIR_IN ? 1 : 0;
        xfer->is_dma = urb->transfer_flags & URB_NO_TRANSFER_DMA_MAP ? 1 : 0;
-       xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
-               * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
-       /* Compute the segment size and make sure it is a multiple of
-        * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
-        * a check (FIXME) */
+
        maxpktsize = le16_to_cpu(rpipe->descr.wMaxPacketSize);
-       if (xfer->seg_size < maxpktsize) {
-               dev_err(dev, "HW BUG? seg_size %zu smaller than maxpktsize "
-                       "%zu\n", xfer->seg_size, maxpktsize);
-               result = -EINVAL;
-               goto error;
-       }
-       xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
-       xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length, xfer->seg_size);
-       if (xfer->segs >= WA_SEGS_MAX) {
-               dev_err(dev, "BUG? ops, number of segments %d bigger than %d\n",
-                       (int)(urb->transfer_buffer_length / xfer->seg_size),
-                       WA_SEGS_MAX);
-               result = -EINVAL;
-               goto error;
+       if ((rpipe->descr.bmAttribute & 0x3) == USB_ENDPOINT_XFER_ISOC) {
+               xfer->seg_size = maxpktsize;
+               xfer->segs = urb->number_of_packets;
+       } else {
+               xfer->seg_size = le16_to_cpu(rpipe->descr.wBlocks)
+                       * 1 << (xfer->wa->wa_descr->bRPipeBlockSize - 1);
+               /* Compute the segment size and make sure it is a multiple of
+                * the maxpktsize (WUSB1.0[8.3.3.1])...not really too much of
+                * a check (FIXME) */
+               if (xfer->seg_size < maxpktsize) {
+                       dev_err(dev,
+                               "HW BUG? seg_size %zu smaller than maxpktsize %zu\n",
+                               xfer->seg_size, maxpktsize);
+                       result = -EINVAL;
+                       goto error;
+               }
+               xfer->seg_size = (xfer->seg_size / maxpktsize) * maxpktsize;
+               xfer->segs = DIV_ROUND_UP(urb->transfer_buffer_length,
+                                               xfer->seg_size);
+               if (xfer->segs >= WA_SEGS_MAX) {
+                       dev_err(dev, "BUG? oops, number of segments %zu bigger than %d\n",
+                               (urb->transfer_buffer_length/xfer->seg_size),
+                               WA_SEGS_MAX);
+                       result = -EINVAL;
+                       goto error;
+               }
+               if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
+                       xfer->segs = 1;
        }
-       if (xfer->segs == 0 && *pxfer_type == WA_XFER_TYPE_CTL)
-               xfer->segs = 1;
 error:
        return result;
 }
@@ -471,7 +549,7 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
        xfer_hdr0->bLength = xfer_hdr_size;
        xfer_hdr0->bRequestType = xfer_type;
        xfer_hdr0->wRPipe = rpipe->descr.wRPipeIndex;
-       xfer_hdr0->dwTransferID = wa_xfer_id(xfer);
+       xfer_hdr0->dwTransferID = wa_xfer_id_le32(xfer);
        xfer_hdr0->bTransferSegment = 0;
        switch (xfer_type) {
        case WA_XFER_TYPE_CTL: {
@@ -484,8 +562,26 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
        }
        case WA_XFER_TYPE_BI:
                break;
-       case WA_XFER_TYPE_ISO:
-               printk(KERN_ERR "FIXME: ISOC not implemented\n");
+       case WA_XFER_TYPE_ISO: {
+               struct wa_xfer_hwaiso *xfer_iso =
+                       container_of(xfer_hdr0, struct wa_xfer_hwaiso, hdr);
+               struct wa_xfer_packet_info_hwaiso *packet_desc =
+                       ((void *)xfer_iso) + xfer_hdr_size;
+               struct usb_iso_packet_descriptor *iso_frame_desc =
+                       &(xfer->urb->iso_frame_desc[0]);
+               /* populate the isoc section of the transfer request. */
+               xfer_iso->dwNumOfPackets = cpu_to_le32(1);
+               /*
+                * populate isoc packet descriptor.  This assumes 1
+                * packet per segment.
+                */
+               packet_desc->wLength = cpu_to_le16(sizeof(*packet_desc) +
+                       sizeof(packet_desc->PacketLength[0]));
+               packet_desc->bPacketType = WA_XFER_ISO_PACKET_INFO;
+               packet_desc->PacketLength[0] =
+                       cpu_to_le16(iso_frame_desc->length);
+               break;
+       }
        default:
                BUG();
        };
@@ -494,12 +590,12 @@ static void __wa_xfer_setup_hdr0(struct wa_xfer *xfer,
 /*
  * Callback for the OUT data phase of the segment request
  *
- * Check wa_seg_cb(); most comments also apply here because this
+ * Check wa_seg_tr_cb(); most comments also apply here because this
  * function does almost the same thing and they work closely
  * together.
  *
  * If the seg request has failed but this DTO phase has succeeded,
- * wa_seg_cb() has already failed the segment and moved the
+ * wa_seg_tr_cb() has already failed the segment and moved the
  * status to WA_SEG_ERROR, so this will go through 'case 0' and
  * effectively do nothing.
  */
@@ -514,6 +610,10 @@ static void wa_seg_dto_cb(struct urb *urb)
        unsigned rpipe_ready = 0;
        u8 done = 0;
 
+       /* free the sg if it was used. */
+       kfree(urb->sg);
+       urb->sg = NULL;
+
        switch (urb->status) {
        case 0:
                spin_lock_irqsave(&xfer->lock, flags);
@@ -558,6 +658,72 @@ static void wa_seg_dto_cb(struct urb *urb)
        }
 }
 
+/*
+ * Callback for the isoc packet descriptor phase of the segment request
+ *
+ * Check wa_seg_tr_cb(); most comments also apply here because this
+ * function does almost the same thing and they work closely
+ * together.
+ *
+ * If the seg request has failed but this phase has succeeded,
+ * wa_seg_tr_cb() has already failed the segment and moved the
+ * status to WA_SEG_ERROR, so this will go through 'case 0' and
+ * effectively do nothing.
+ */
+static void wa_seg_iso_pack_desc_cb(struct urb *urb)
+{
+       struct wa_seg *seg = urb->context;
+       struct wa_xfer *xfer = seg->xfer;
+       struct wahc *wa;
+       struct device *dev;
+       struct wa_rpipe *rpipe;
+       unsigned long flags;
+       unsigned rpipe_ready = 0;
+       u8 done = 0;
+
+       switch (urb->status) {
+       case 0:
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               dev_dbg(dev, "iso xfer %p#%u: packet descriptor done\n",
+                       xfer, seg->index);
+               if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
+                       seg->status = WA_SEG_PENDING;
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               break;
+       case -ECONNRESET:       /* URB unlinked; no need to do anything */
+       case -ENOENT:           /* as it was done by the who unlinked us */
+               break;
+       default:                /* Other errors ... */
+               spin_lock_irqsave(&xfer->lock, flags);
+               wa = xfer->wa;
+               dev = &wa->usb_iface->dev;
+               rpipe = xfer->ep->hcpriv;
+               pr_err_ratelimited("iso xfer %p#%u: packet descriptor error %d\n",
+                               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);
+               }
+               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);
+               }
+               spin_unlock_irqrestore(&xfer->lock, flags);
+               if (done)
+                       wa_xfer_completion(xfer);
+               if (rpipe_ready)
+                       wa_xfer_delayed_run(rpipe);
+       }
+}
+
 /*
  * Callback for the segment request
  *
@@ -572,11 +738,11 @@ static void wa_seg_dto_cb(struct urb *urb)
  * We have to check before setting the status to WA_SEG_PENDING
  * because sometimes the xfer result callback arrives before this
  * callback (geeeeeeze), so it might happen that we are already in
- * another state. As well, we don't set it if the transfer is inbound,
+ * another state. As well, we don't set it if the transfer is not inbound,
  * as in that case, wa_seg_dto_cb will do it when the OUT data phase
  * finishes.
  */
-static void wa_seg_cb(struct urb *urb)
+static void wa_seg_tr_cb(struct urb *urb)
 {
        struct wa_seg *seg = urb->context;
        struct wa_xfer *xfer = seg->xfer;
@@ -592,8 +758,11 @@ static void wa_seg_cb(struct urb *urb)
                spin_lock_irqsave(&xfer->lock, flags);
                wa = xfer->wa;
                dev = &wa->usb_iface->dev;
-               dev_dbg(dev, "xfer %p#%u: request done\n", xfer, seg->index);
-               if (xfer->is_inbound && seg->status < WA_SEG_PENDING)
+               dev_dbg(dev, "xfer %p ID 0x%08X#%u: request done\n",
+                       xfer, wa_xfer_id(xfer), seg->index);
+               if (xfer->is_inbound &&
+                       seg->status < WA_SEG_PENDING &&
+                       !(usb_pipeisoc(xfer->urb->pipe)))
                        seg->status = WA_SEG_PENDING;
                spin_unlock_irqrestore(&xfer->lock, flags);
                break;
@@ -606,14 +775,16 @@ static void wa_seg_cb(struct urb *urb)
                dev = &wa->usb_iface->dev;
                rpipe = xfer->ep->hcpriv;
                if (printk_ratelimit())
-                       dev_err(dev, "xfer %p#%u: request error %d\n",
-                               xfer, seg->index, urb->status);
+                       dev_err(dev, "xfer %p ID 0x%08X#%u: request 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);
                }
+               usb_unlink_urb(seg->isoc_pack_desc_urb);
                usb_unlink_urb(seg->dto_urb);
                seg->status = WA_SEG_ERROR;
                seg->result = urb->status;
@@ -629,9 +800,11 @@ static void wa_seg_cb(struct urb *urb)
        }
 }
 
-/* allocate an SG list to store bytes_to_transfer bytes and copy the
+/*
+ * Allocate an SG list to store bytes_to_transfer bytes and copy the
  * subset of the in_sg that matches the buffer subset
- * we are about to transfer. */
+ * we are about to transfer.
+ */
 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)
@@ -709,6 +882,74 @@ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
        return out_sg;
 }
 
+/*
+ * Populate DMA buffer info for the isoc dto urb.
+ */
+static void __wa_populate_dto_urb_iso(struct wa_xfer *xfer,
+       struct wa_seg *seg,     int curr_iso_frame)
+{
+       /*
+        * dto urb buffer address and size pulled from
+        * iso_frame_desc.
+        */
+       seg->dto_urb->transfer_dma = xfer->urb->transfer_dma +
+               xfer->urb->iso_frame_desc[curr_iso_frame].offset;
+       seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+       seg->dto_urb->sg = NULL;
+       seg->dto_urb->num_sgs = 0;
+       seg->dto_urb->transfer_buffer_length =
+               xfer->urb->iso_frame_desc[curr_iso_frame].length;
+}
+
+/*
+ * Populate buffer ptr and size, DMA buffer or SG list for the dto urb.
+ */
+static int __wa_populate_dto_urb(struct wa_xfer *xfer,
+       struct wa_seg *seg, size_t buf_itr_offset, size_t buf_itr_size)
+{
+       int result = 0;
+
+       if (xfer->is_dma) {
+               seg->dto_urb->transfer_dma =
+                       xfer->urb->transfer_dma + buf_itr_offset;
+               seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+               seg->dto_urb->sg = NULL;
+               seg->dto_urb->num_sgs = 0;
+       } else {
+               /* do buffer or SG processing. */
+               seg->dto_urb->transfer_flags &=
+                       ~URB_NO_TRANSFER_DMA_MAP;
+               /* this should always be 0 before a resubmit. */
+               seg->dto_urb->num_mapped_sgs = 0;
+
+               if (xfer->urb->transfer_buffer) {
+                       seg->dto_urb->transfer_buffer =
+                               xfer->urb->transfer_buffer +
+                               buf_itr_offset;
+                       seg->dto_urb->sg = NULL;
+                       seg->dto_urb->num_sgs = 0;
+               } else {
+                       seg->dto_urb->transfer_buffer = NULL;
+
+                       /*
+                        * 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.
+                        */
+                       seg->dto_urb->sg = wa_xfer_create_subset_sg(
+                               xfer->urb->sg,
+                               buf_itr_offset, buf_itr_size,
+                               &(seg->dto_urb->num_sgs));
+                       if (!(seg->dto_urb->sg))
+                               result = -ENOMEM;
+               }
+       }
+       seg->dto_urb->transfer_buffer_length = buf_itr_size;
+
+       return result;
+}
+
 /*
  * Allocate the segs array and initialize each of them
  *
@@ -725,7 +966,7 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t 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;
+       size_t buf_itr, buf_size, buf_itr_size, iso_pkt_descr_size = 0;
 
        result = -ENOMEM;
        xfer->seg = kcalloc(xfer->segs, sizeof(xfer->seg[0]), GFP_ATOMIC);
@@ -733,6 +974,17 @@ 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;
+
+       if (usb_pipeisoc(xfer->urb->pipe)) {
+               /*
+                * This calculation assumes one isoc packet per xfer segment.
+                * It will need to be updated if this changes.
+                */
+               iso_pkt_descr_size = sizeof(struct wa_xfer_packet_info_hwaiso) +
+                       sizeof(__le16);
+               alloc_size += iso_pkt_descr_size;
+       }
+
        for (cnt = 0; cnt < xfer->segs; cnt++) {
                seg = xfer->seg[cnt] = kmalloc(alloc_size, GFP_ATOMIC);
                if (seg == NULL)
@@ -740,11 +992,11 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
                wa_seg_init(seg);
                seg->xfer = xfer;
                seg->index = cnt;
-               usb_fill_bulk_urb(&seg->urb, usb_dev,
+               usb_fill_bulk_urb(&seg->tr_urb, usb_dev,
                                  usb_sndbulkpipe(usb_dev,
                                                  dto_epd->bEndpointAddress),
                                  &seg->xfer_hdr, xfer_hdr_size,
-                                 wa_seg_cb, seg);
+                                 wa_seg_tr_cb, seg);
                buf_itr_size = min(buf_size, xfer->seg_size);
                if (xfer->is_inbound == 0 && buf_size > 0) {
                        /* outbound data. */
@@ -756,69 +1008,56 @@ static int __wa_xfer_setup_segs(struct wa_xfer *xfer, size_t xfer_hdr_size)
                                usb_sndbulkpipe(usb_dev,
                                                dto_epd->bEndpointAddress),
                                NULL, 0, wa_seg_dto_cb, seg);
-                       if (xfer->is_dma) {
-                               seg->dto_urb->transfer_dma =
-                                       xfer->urb->transfer_dma + buf_itr;
-                               seg->dto_urb->transfer_flags |=
-                                       URB_NO_TRANSFER_DMA_MAP;
-                               seg->dto_urb->transfer_buffer = NULL;
-                               seg->dto_urb->sg = NULL;
-                               seg->dto_urb->num_sgs = 0;
+
+                       if (usb_pipeisoc(xfer->urb->pipe)) {
+                               /* iso packet descriptor. */
+                               seg->isoc_pack_desc_urb =
+                                               usb_alloc_urb(0, GFP_ATOMIC);
+                               if (seg->isoc_pack_desc_urb == NULL)
+                                       goto error_iso_pack_desc_alloc;
+                               /*
+                                * The buffer for the isoc packet descriptor
+                                * after the transfer request header in the
+                                * segment object memory buffer.
+                                */
+                               usb_fill_bulk_urb(
+                                       seg->isoc_pack_desc_urb, usb_dev,
+                                       usb_sndbulkpipe(usb_dev,
+                                               dto_epd->bEndpointAddress),
+                                       (void *)(&seg->xfer_hdr) +
+                                               xfer_hdr_size,
+                                       iso_pkt_descr_size,
+                                       wa_seg_iso_pack_desc_cb, seg);
+
+                               /* fill in the xfer buffer information. */
+                               __wa_populate_dto_urb_iso(xfer, seg, cnt);
                        } else {
-                               /* do buffer or SG processing. */
-                               seg->dto_urb->transfer_flags &=
-                                       ~URB_NO_TRANSFER_DMA_MAP;
-                               /* this should always be 0 before a resubmit. */
-                               seg->dto_urb->num_mapped_sgs = 0;
-
-                               if (xfer->urb->transfer_buffer) {
-                                       seg->dto_urb->transfer_buffer =
-                                               xfer->urb->transfer_buffer +
-                                               buf_itr;
-                                       seg->dto_urb->sg = NULL;
-                                       seg->dto_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.
-                                       */
-                                       seg->dto_urb->sg =
-                                               wa_xfer_create_subset_sg(
-                                               xfer->urb->sg,
-                                               buf_itr, buf_itr_size,
-                                               &(seg->dto_urb->num_sgs));
-
-                                       if (!(seg->dto_urb->sg)) {
-                                               seg->dto_urb->num_sgs   = 0;
-                                               goto error_sg_alloc;
-                                       }
-
-                                       seg->dto_urb->transfer_buffer = NULL;
-                               }
+                               /* fill in the xfer buffer information. */
+                               result = __wa_populate_dto_urb(xfer, seg,
+                                                       buf_itr, buf_itr_size);
+                               if (result < 0)
+                                       goto error_seg_outbound_populate;
+
+                               buf_itr += buf_itr_size;
+                               buf_size -= buf_itr_size;
                        }
-                       seg->dto_urb->transfer_buffer_length = buf_itr_size;
                }
                seg->status = WA_SEG_READY;
-               buf_itr += buf_itr_size;
-               buf_size -= buf_itr_size;
        }
        return 0;
 
-error_sg_alloc:
+       /*
+        * Free the memory for the current segment which failed to init.
+        * Use the fact that cnt is left at were it failed.  The remaining
+        * segments will be cleaned up by wa_xfer_destroy.
+        */
+error_iso_pack_desc_alloc:
+error_seg_outbound_populate:
        usb_free_urb(xfer->seg[cnt]->dto_urb);
 error_dto_alloc:
        kfree(xfer->seg[cnt]);
-       cnt--;
+       xfer->seg[cnt] = NULL;
 error_seg_kmalloc:
-       /* use the fact that cnt is left at were it failed */
-       for (; cnt >= 0; cnt--) {
-               if (xfer->seg[cnt] && xfer->is_inbound == 0) {
-                       usb_free_urb(xfer->seg[cnt]->dto_urb);
-                       kfree(xfer->seg[cnt]->dto_urb->sg);
-               }
-               kfree(xfer->seg[cnt]);
-       }
 error_segs_kzalloc:
        return result;
 }
@@ -856,21 +1095,50 @@ static int __wa_xfer_setup(struct wa_xfer *xfer, struct urb *urb)
        wa_xfer_id_init(xfer);
        __wa_xfer_setup_hdr0(xfer, xfer_hdr0, xfer_type, xfer_hdr_size);
 
-       /* Fill remainig headers */
+       /* Fill remaining headers */
        xfer_hdr = xfer_hdr0;
-       transfer_size = urb->transfer_buffer_length;
-       xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ?
-               xfer->seg_size : transfer_size;
-       transfer_size -=  xfer->seg_size;
-       for (cnt = 1; cnt < xfer->segs; cnt++) {
-               xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
-               memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
-               xfer_hdr->bTransferSegment = cnt;
-               xfer_hdr->dwTransferLength = transfer_size > xfer->seg_size ?
-                       cpu_to_le32(xfer->seg_size)
-                       : cpu_to_le32(transfer_size);
-               xfer->seg[cnt]->status = WA_SEG_READY;
+       if (xfer_type == WA_XFER_TYPE_ISO) {
+               xfer_hdr0->dwTransferLength =
+                       cpu_to_le32(xfer->urb->iso_frame_desc[0].length);
+               for (cnt = 1; cnt < xfer->segs; cnt++) {
+                       struct usb_iso_packet_descriptor *iso_frame_desc =
+                               &(xfer->urb->iso_frame_desc[cnt]);
+                       struct wa_xfer_packet_info_hwaiso *packet_desc;
+
+                       xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
+                       packet_desc = ((void *)xfer_hdr) + xfer_hdr_size;
+                       /*
+                        * Copy values from the 0th header and isoc packet
+                        * descriptor.  Segment specific values are set below.
+                        */
+                       memcpy(xfer_hdr, xfer_hdr0,
+                               xfer_hdr_size + sizeof(*packet_desc));
+                       xfer_hdr->bTransferSegment = cnt;
+                       xfer_hdr->dwTransferLength =
+                               cpu_to_le32(iso_frame_desc->length);
+                       /* populate isoc packet descriptor length. */
+                       packet_desc->PacketLength[0] =
+                               cpu_to_le16(iso_frame_desc->length);
+
+                       xfer->seg[cnt]->status = WA_SEG_READY;
+               }
+       } else {
+               transfer_size = urb->transfer_buffer_length;
+               xfer_hdr0->dwTransferLength = transfer_size > xfer->seg_size ?
+                       cpu_to_le32(xfer->seg_size) :
+                       cpu_to_le32(transfer_size);
                transfer_size -=  xfer->seg_size;
+               for (cnt = 1; cnt < xfer->segs; cnt++) {
+                       xfer_hdr = &xfer->seg[cnt]->xfer_hdr;
+                       memcpy(xfer_hdr, xfer_hdr0, xfer_hdr_size);
+                       xfer_hdr->bTransferSegment = cnt;
+                       xfer_hdr->dwTransferLength =
+                               transfer_size > xfer->seg_size ?
+                                       cpu_to_le32(xfer->seg_size)
+                                       : cpu_to_le32(transfer_size);
+                       xfer->seg[cnt]->status = WA_SEG_READY;
+                       transfer_size -=  xfer->seg_size;
+               }
        }
        xfer_hdr->bTransferSegment |= 0x80;     /* this is the last segment */
        result = 0;
@@ -885,20 +1153,35 @@ error_setup_sizes:
  * rpipe->seg_lock is held!
  */
 static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
-                          struct wa_seg *seg)
+                          struct wa_seg *seg, int *dto_done)
 {
        int result;
-       result = usb_submit_urb(&seg->urb, GFP_ATOMIC);
+
+       /* default to done unless we encounter a multi-frame isoc segment. */
+       *dto_done = 1;
+
+       /* submit the transfer request. */
+       result = usb_submit_urb(&seg->tr_urb, GFP_ATOMIC);
        if (result < 0) {
-               printk(KERN_ERR "xfer %p#%u: REQ submit failed: %d\n",
-                      xfer, seg->index, result);
+               pr_err("%s: xfer %p#%u: REQ submit failed: %d\n",
+                      __func__, xfer, seg->index, result);
                goto error_seg_submit;
        }
+       /* submit the isoc packet descriptor if present. */
+       if (seg->isoc_pack_desc_urb) {
+               result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC);
+               if (result < 0) {
+                       pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n",
+                              __func__, xfer, seg->index, result);
+                       goto error_iso_pack_desc_submit;
+               }
+       }
+       /* submit the out data if this is an out request. */
        if (seg->dto_urb) {
                result = usb_submit_urb(seg->dto_urb, GFP_ATOMIC);
                if (result < 0) {
-                       printk(KERN_ERR "xfer %p#%u: DTO submit failed: %d\n",
-                              xfer, seg->index, result);
+                       pr_err("%s: xfer %p#%u: DTO submit failed: %d\n",
+                              __func__, xfer, seg->index, result);
                        goto error_dto_submit;
                }
        }
@@ -907,7 +1190,9 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
        return 0;
 
 error_dto_submit:
-       usb_unlink_urb(&seg->urb);
+       usb_unlink_urb(seg->isoc_pack_desc_urb);
+error_iso_pack_desc_submit:
+       usb_unlink_urb(&seg->tr_urb);
 error_seg_submit:
        seg->status = WA_SEG_ERROR;
        seg->result = result;
@@ -915,30 +1200,37 @@ error_seg_submit:
 }
 
 /*
- * Execute more queued request segments until the maximum concurrent allowed
+ * Execute more queued request segments until the maximum concurrent allowed.
+ * Return true if the DTO resource was acquired and released.
  *
  * The ugly unlock/lock sequence on the error path is needed as the
  * xfer->lock normally nests the seg_lock and not viceversa.
- *
  */
-static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+static int __wa_xfer_delayed_run(struct wa_rpipe *rpipe, int *dto_waiting)
 {
-       int result;
+       int result, dto_acquired = 0, dto_done = 0;
        struct device *dev = &rpipe->wa->usb_iface->dev;
        struct wa_seg *seg;
        struct wa_xfer *xfer;
        unsigned long flags;
 
+       *dto_waiting = 0;
+
        spin_lock_irqsave(&rpipe->seg_lock, flags);
        while (atomic_read(&rpipe->segs_available) > 0
-             && !list_empty(&rpipe->seg_list)) {
+             && !list_empty(&rpipe->seg_list)
+             && (dto_acquired = __wa_dto_try_get(rpipe->wa))) {
                seg = list_first_entry(&(rpipe->seg_list), struct wa_seg,
                                 list_node);
                list_del(&seg->list_node);
                xfer = seg->xfer;
-               result = __wa_seg_submit(rpipe, xfer, seg);
-               dev_dbg(dev, "xfer %p#%u submitted from delayed [%d segments available] %d\n",
-                       xfer, seg->index, atomic_read(&rpipe->segs_available), result);
+               result = __wa_seg_submit(rpipe, xfer, seg, &dto_done);
+               /* release the dto resource if this RPIPE is done with it. */
+               if (dto_done)
+                       __wa_dto_put(rpipe->wa);
+               dev_dbg(dev, "xfer %p ID %08X#%u submitted from delayed [%d segments available] %d\n",
+                       xfer, wa_xfer_id(xfer), seg->index,
+                       atomic_read(&rpipe->segs_available), result);
                if (unlikely(result < 0)) {
                        spin_unlock_irqrestore(&rpipe->seg_lock, flags);
                        spin_lock_irqsave(&xfer->lock, flags);
@@ -948,7 +1240,37 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
                        spin_lock_irqsave(&rpipe->seg_lock, flags);
                }
        }
+       /*
+        * Mark this RPIPE as waiting if dto was not acquired, there are
+        * delayed segs and no active transfers to wake us up later.
+        */
+       if (!dto_acquired && !list_empty(&rpipe->seg_list)
+               && (atomic_read(&rpipe->segs_available) ==
+                       le16_to_cpu(rpipe->descr.wRequests)))
+               *dto_waiting = 1;
+
        spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+
+       return dto_done;
+}
+
+static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
+{
+       int dto_waiting;
+       int dto_done = __wa_xfer_delayed_run(rpipe, &dto_waiting);
+
+       /*
+        * If this RPIPE is waiting on the DTO resource, add it to the tail of
+        * the waiting list.
+        * Otherwise, if the WA DTO resource was acquired and released by
+        *  __wa_xfer_delayed_run, another RPIPE may have attempted to acquire
+        * DTO and failed during that time.  Check the delayed list and process
+        * any waiters.  Start searching from the next RPIPE index.
+        */
+       if (dto_waiting)
+               wa_add_delayed_rpipe(rpipe->wa, rpipe);
+       else if (dto_done)
+               wa_check_for_delayed_rpipes(rpipe->wa);
 }
 
 /*
@@ -960,7 +1282,7 @@ static void wa_xfer_delayed_run(struct wa_rpipe *rpipe)
  */
 static int __wa_xfer_submit(struct wa_xfer *xfer)
 {
-       int result;
+       int result, dto_acquired = 0, dto_done = 0, dto_waiting = 0;
        struct wahc *wa = xfer->wa;
        struct device *dev = &wa->usb_iface->dev;
        unsigned cnt;
@@ -979,27 +1301,56 @@ static int __wa_xfer_submit(struct wa_xfer *xfer)
        result = 0;
        spin_lock_irqsave(&rpipe->seg_lock, flags);
        for (cnt = 0; cnt < xfer->segs; cnt++) {
+               int delay_seg = 1;
+
                available = atomic_read(&rpipe->segs_available);
                empty = list_empty(&rpipe->seg_list);
                seg = xfer->seg[cnt];
-               dev_dbg(dev, "xfer %p#%u: available %u empty %u (%s)\n",
-                       xfer, cnt, available, empty,
+               dev_dbg(dev, "xfer %p ID 0x%08X#%u: available %u empty %u (%s)\n",
+                       xfer, wa_xfer_id(xfer), cnt, available, empty,
                        available == 0 || !empty ? "delayed" : "submitted");
-               if (available == 0 || !empty) {
-                       dev_dbg(dev, "xfer %p#%u: delayed\n", xfer, cnt);
+               if (available && empty) {
+                       /*
+                        * Only attempt to acquire DTO if we have a segment
+                        * to send.
+                        */
+                       dto_acquired = __wa_dto_try_get(rpipe->wa);
+                       if (dto_acquired) {
+                               delay_seg = 0;
+                               result = __wa_seg_submit(rpipe, xfer, seg,
+                                                       &dto_done);
+                               if (dto_done)
+                                       __wa_dto_put(rpipe->wa);
+
+                               if (result < 0) {
+                                       __wa_xfer_abort(xfer);
+                                       goto error_seg_submit;
+                               }
+                       }
+               }
+
+               if (delay_seg) {
                        seg->status = WA_SEG_DELAYED;
                        list_add_tail(&seg->list_node, &rpipe->seg_list);
-               } else {
-                       result = __wa_seg_submit(rpipe, xfer, seg);
-                       if (result < 0) {
-                               __wa_xfer_abort(xfer);
-                               goto error_seg_submit;
-                       }
                }
                xfer->segs_submitted++;
        }
 error_seg_submit:
+       /*
+        * Mark this RPIPE as waiting if dto was not acquired, there are
+        * delayed segs and no active transfers to wake us up later.
+        */
+       if (!dto_acquired && !list_empty(&rpipe->seg_list)
+               && (atomic_read(&rpipe->segs_available) ==
+                       le16_to_cpu(rpipe->descr.wRequests)))
+               dto_waiting = 1;
        spin_unlock_irqrestore(&rpipe->seg_lock, flags);
+
+       if (dto_waiting)
+               wa_add_delayed_rpipe(rpipe->wa, rpipe);
+       else if (dto_done)
+               wa_check_for_delayed_rpipes(rpipe->wa);
+
        return result;
 }
 
@@ -1025,7 +1376,7 @@ error_seg_submit:
  * result never kicks in, the xfer will timeout from the USB code and
  * dequeue() will be called.
  */
-static void wa_urb_enqueue_b(struct wa_xfer *xfer)
+static int wa_urb_enqueue_b(struct wa_xfer *xfer)
 {
        int result;
        unsigned long flags;
@@ -1036,18 +1387,22 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
        unsigned done;
 
        result = rpipe_get_by_ep(wa, xfer->ep, urb, xfer->gfp);
-       if (result < 0)
+       if (result < 0) {
+               pr_err("%s: error_rpipe_get\n", __func__);
                goto error_rpipe_get;
+       }
        result = -ENODEV;
        /* FIXME: segmentation broken -- kills DWA */
        mutex_lock(&wusbhc->mutex);             /* get a WUSB dev */
        if (urb->dev == NULL) {
                mutex_unlock(&wusbhc->mutex);
+               pr_err("%s: error usb dev gone\n", __func__);
                goto error_dev_gone;
        }
        wusb_dev = __wusb_dev_get_by_usb_dev(wusbhc, urb->dev);
        if (wusb_dev == NULL) {
                mutex_unlock(&wusbhc->mutex);
+               pr_err("%s: error wusb dev gone\n", __func__);
                goto error_dev_gone;
        }
        mutex_unlock(&wusbhc->mutex);
@@ -1055,21 +1410,28 @@ static void wa_urb_enqueue_b(struct wa_xfer *xfer)
        spin_lock_irqsave(&xfer->lock, flags);
        xfer->wusb_dev = wusb_dev;
        result = urb->status;
-       if (urb->status != -EINPROGRESS)
+       if (urb->status != -EINPROGRESS) {
+               pr_err("%s: error_dequeued\n", __func__);
                goto error_dequeued;
+       }
 
        result = __wa_xfer_setup(xfer, urb);
-       if (result < 0)
+       if (result < 0) {
+               pr_err("%s: error_xfer_setup\n", __func__);
                goto error_xfer_setup;
+       }
        result = __wa_xfer_submit(xfer);
-       if (result < 0)
+       if (result < 0) {
+               pr_err("%s: error_xfer_submit\n", __func__);
                goto error_xfer_submit;
+       }
        spin_unlock_irqrestore(&xfer->lock, flags);
-       return;
+       return 0;
 
-       /* this is basically wa_xfer_completion() broken up wa_xfer_giveback()
-        * does a wa_xfer_put() that will call wa_xfer_destroy() and clean
-        * upundo setup().
+       /*
+        * this is basically wa_xfer_completion() broken up wa_xfer_giveback()
+        * does a wa_xfer_put() that will call wa_xfer_destroy() and undo
+        * setup().
         */
 error_xfer_setup:
 error_dequeued:
@@ -1081,8 +1443,7 @@ error_dev_gone:
        rpipe_put(xfer->ep->hcpriv);
 error_rpipe_get:
        xfer->result = result;
-       wa_xfer_giveback(xfer);
-       return;
+       return result;
 
 error_xfer_submit:
        done = __wa_xfer_is_done(xfer);
@@ -1090,6 +1451,8 @@ error_xfer_submit:
        spin_unlock_irqrestore(&xfer->lock, flags);
        if (done)
                wa_xfer_completion(xfer);
+       /* return success since the completion routine will run. */
+       return 0;
 }
 
 /*
@@ -1123,7 +1486,8 @@ void wa_urb_enqueue_run(struct work_struct *ws)
                list_del_init(&xfer->list_node);
 
                urb = xfer->urb;
-               wa_urb_enqueue_b(xfer);
+               if (wa_urb_enqueue_b(xfer) < 0)
+                       wa_xfer_giveback(xfer);
                usb_put_urb(urb);       /* taken when queuing */
        }
 }
@@ -1229,7 +1593,19 @@ int wa_urb_enqueue(struct wahc *wa, struct usb_host_endpoint *ep,
                spin_unlock_irqrestore(&wa->xfer_list_lock, my_flags);
                queue_work(wusbd, &wa->xfer_enqueue_work);
        } else {
-               wa_urb_enqueue_b(xfer);
+               result = wa_urb_enqueue_b(xfer);
+               if (result < 0) {
+                       /*
+                        * URB submit/enqueue failed.  Clean up, return an
+                        * error and do not run the callback.  This avoids
+                        * an infinite submit/complete loop.
+                        */
+                       dev_err(dev, "%s: URB enqueue failed: %d\n",
+                          __func__, result);
+                       wa_put(xfer->wa);
+                       wa_xfer_put(xfer);
+                       return result;
+               }
        }
        return 0;
 
@@ -1264,7 +1640,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
        struct wa_xfer *xfer;
        struct wa_seg *seg;
        struct wa_rpipe *rpipe;
-       unsigned cnt;
+       unsigned cnt, done = 0, xfer_abort_pending;
        unsigned rpipe_ready = 0;
 
        xfer = urb->hcpriv;
@@ -1278,6 +1654,7 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
                goto out;
        }
        spin_lock_irqsave(&xfer->lock, flags);
+       pr_debug("%s: DEQUEUE xfer id 0x%08X\n", __func__, wa_xfer_id(xfer));
        rpipe = xfer->ep->hcpriv;
        if (rpipe == NULL) {
                pr_debug("%s: xfer id 0x%08X has no RPIPE.  %s",
@@ -1293,9 +1670,11 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
        if (xfer->seg == NULL)          /* still hasn't reached */
                goto out_unlock;        /* setup(), enqueue_b() completes */
        /* Ok, the xfer is in flight already, it's been setup and submitted.*/
-       __wa_xfer_abort(xfer);
+       xfer_abort_pending = __wa_xfer_abort(xfer) >= 0;
        for (cnt = 0; cnt < xfer->segs; cnt++) {
                seg = xfer->seg[cnt];
+               pr_debug("%s: xfer id 0x%08X#%d status = %d\n",
+                       __func__, wa_xfer_id(xfer), cnt, seg->status);
                switch (seg->status) {
                case WA_SEG_NOTREADY:
                case WA_SEG_READY:
@@ -1304,42 +1683,50 @@ int wa_urb_dequeue(struct wahc *wa, struct urb *urb)
                        WARN_ON(1);
                        break;
                case WA_SEG_DELAYED:
+                       /*
+                        * delete from rpipe delayed list.  If no segments on
+                        * this xfer have been submitted, __wa_xfer_is_done will
+                        * trigger a giveback below.  Otherwise, the submitted
+                        * segments will be completed in the DTI interrupt.
+                        */
                        seg->status = WA_SEG_ABORTED;
                        spin_lock_irqsave(&rpipe->seg_lock, flags2);
                        list_del(&seg->list_node);
                        xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
                        spin_unlock_irqrestore(&rpipe->seg_lock, flags2);
                        break;
-               case WA_SEG_SUBMITTED:
-                       seg->status = WA_SEG_ABORTED;
-                       usb_unlink_urb(&seg->urb);
-                       if (xfer->is_inbound == 0)
-                               usb_unlink_urb(seg->dto_urb);
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
-               case WA_SEG_PENDING:
-                       seg->status = WA_SEG_ABORTED;
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
-               case WA_SEG_DTI_PENDING:
-                       usb_unlink_urb(wa->dti_urb);
-                       seg->status = WA_SEG_ABORTED;
-                       xfer->segs_done++;
-                       rpipe_ready = rpipe_avail_inc(rpipe);
-                       break;
                case WA_SEG_DONE:
                case WA_SEG_ERROR:
                case WA_SEG_ABORTED:
                        break;
+                       /*
+                        * In the states below, the HWA device already knows
+                        * about the transfer.  If an abort request was sent,
+                        * allow the HWA to process it and wait for the
+                        * results.  Otherwise, the DTI state and seg completed
+                        * counts can get out of sync.
+                        */
+               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
+                        * gotten the disconnect notification yet.
+                        */
+                       if (!xfer_abort_pending) {
+                               seg->status = WA_SEG_ABORTED;
+                               rpipe_ready = rpipe_avail_inc(rpipe);
+                               xfer->segs_done++;
+                       }
+                       break;
                }
        }
        xfer->result = urb->status;     /* -ENOENT or -ECONNRESET */
-       __wa_xfer_is_done(xfer);
+       done = __wa_xfer_is_done(xfer);
        spin_unlock_irqrestore(&xfer->lock, flags);
-       wa_xfer_completion(xfer);
+       if (done)
+               wa_xfer_completion(xfer);
        if (rpipe_ready)
                wa_xfer_delayed_run(rpipe);
        return 0;
@@ -1409,14 +1796,57 @@ static int wa_xfer_status_to_errno(u8 status)
        return errno;
 }
 
+/*
+ * If a last segment flag and/or a transfer result error is encountered,
+ * 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.
+ */
+static void wa_complete_remaining_xfer_segs(struct wa_xfer *xfer,
+               struct wa_seg *incoming_seg)
+{
+       int index;
+       struct wa_rpipe *rpipe = xfer->ep->hcpriv;
+
+       for (index = incoming_seg->index + 1; index < xfer->segs_submitted;
+               index++) {
+               struct wa_seg *current_seg = xfer->seg[index];
+
+               BUG_ON(current_seg == NULL);
+
+               switch (current_seg->status) {
+               case WA_SEG_SUBMITTED:
+               case WA_SEG_PENDING:
+               case WA_SEG_DTI_PENDING:
+                       rpipe_avail_inc(rpipe);
+               /*
+                * do not increment RPIPE avail for the WA_SEG_DELAYED case
+                * since it has not been submitted to the RPIPE.
+                */
+               case WA_SEG_DELAYED:
+                       xfer->segs_done++;
+                       current_seg->status = incoming_seg->status;
+                       break;
+               case WA_SEG_ABORTED:
+                       break;
+               default:
+                       WARN(1, "%s: xfer 0x%08X#%d. bad seg status = %d\n",
+                               __func__, wa_xfer_id(xfer), index,
+                               current_seg->status);
+                       break;
+               }
+       }
+}
+
 /*
  * Process a xfer result completion message
  *
- * inbound transfers: need to schedule a DTI read
+ * inbound transfers: need to schedule a buf_in_urb read
  *
  * FIXME: this function needs to be broken up in parts
  */
-static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
+static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer,
+               struct wa_xfer_result *xfer_result)
 {
        int result;
        struct device *dev = &wa->usb_iface->dev;
@@ -1424,8 +1854,7 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
        u8 seg_idx;
        struct wa_seg *seg;
        struct wa_rpipe *rpipe;
-       struct wa_xfer_result *xfer_result = wa->xfer_result;
-       u8 done = 0;
+       unsigned done = 0;
        u8 usb_status;
        unsigned rpipe_ready = 0;
 
@@ -1436,8 +1865,8 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
        seg = xfer->seg[seg_idx];
        rpipe = xfer->ep->hcpriv;
        usb_status = xfer_result->bTransferStatus;
-       dev_dbg(dev, "xfer %p#%u: bTransferStatus 0x%02x (seg status %u)\n",
-               xfer, seg_idx, usb_status, seg->status);
+       dev_dbg(dev, "xfer %p ID 0x%08X#%u: bTransferStatus 0x%02x (seg status %u)\n",
+               xfer, wa_xfer_id(xfer), seg_idx, usb_status, seg->status);
        if (seg->status == WA_SEG_ABORTED
            || seg->status == WA_SEG_ERROR)     /* already handled */
                goto segment_aborted;
@@ -1453,12 +1882,19 @@ static void wa_xfer_result_chew(struct wahc *wa, struct wa_xfer *xfer)
                seg->result = wa_xfer_status_to_errno(usb_status);
                dev_err(dev, "DTI: xfer %p#:%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;
                goto error_complete;
        }
        /* FIXME: we ignore warnings, tally them for stats */
        if (usb_status & 0x40)          /* Warning?... */
                usb_status = 0;         /* ... pass */
-       if (xfer->is_inbound) { /* IN data phase: read to buffer */
+       if (usb_pipeisoc(xfer->urb->pipe)) {
+               /* set up WA state to read the isoc packet status next. */
+               wa->dti_isoc_xfer_in_progress = wa_xfer_id(xfer);
+               wa->dti_isoc_xfer_seg = seg_idx;
+               wa->dti_state = WA_DTI_ISOC_PACKET_STATUS_PENDING;
+       } else if (xfer->is_inbound) {  /* IN data phase: read to buffer */
                seg->status = WA_SEG_DTI_PENDING;
                BUG_ON(wa->buf_in_urb->status == -EINPROGRESS);
                /* this should always be 0 before a resubmit. */
@@ -1535,12 +1971,14 @@ error_submit_buf_in:
                        xfer, seg_idx, result);
        seg->result = result;
        kfree(wa->buf_in_urb->sg);
+       wa->buf_in_urb->sg = NULL;
 error_sg_alloc:
        __wa_xfer_abort(xfer);
-error_complete:
        seg->status = WA_SEG_ERROR;
+error_complete:
        xfer->segs_done++;
        rpipe_ready = rpipe_avail_inc(rpipe);
+       wa_complete_remaining_xfer_segs(xfer, seg);
        done = __wa_xfer_is_done(xfer);
        /*
         * queue work item to clear STALL for control endpoints.
@@ -1552,10 +1990,8 @@ error_complete:
 
                dev_info(dev, "Control EP stall.  Queue delayed work.\n");
                spin_lock_irq(&wa->xfer_list_lock);
-               /* remove xfer from xfer_list. */
-               list_del(&xfer->list_node);
-               /* add xfer to xfer_errored_list. */
-               list_add_tail(&xfer->list_node, &wa->xfer_errored_list);
+               /* move xfer from xfer_list to xfer_errored_list. */
+               list_move_tail(&xfer->list_node, &wa->xfer_errored_list);
                spin_unlock_irq(&wa->xfer_list_lock);
                spin_unlock_irqrestore(&xfer->lock, flags);
                queue_work(wusbd, &wa->xfer_error_work);
@@ -1586,6 +2022,85 @@ segment_aborted:
        spin_unlock_irqrestore(&xfer->lock, flags);
 }
 
+/*
+ * Process a isochronous packet status message
+ *
+ * inbound transfers: need to schedule a buf_in_urb read
+ */
+static void wa_process_iso_packet_status(struct wahc *wa, struct urb *urb)
+{
+       struct device *dev = &wa->usb_iface->dev;
+       struct wa_xfer_packet_status_hwaiso *packet_status;
+       struct wa_xfer *xfer;
+       unsigned long flags;
+       struct wa_seg *seg;
+       struct wa_rpipe *rpipe;
+       unsigned done = 0;
+       unsigned rpipe_ready = 0;
+       const int expected_size = sizeof(*packet_status) +
+                               sizeof(packet_status->PacketStatus[0]);
+
+       /* We have a xfer result buffer; check it */
+       dev_dbg(dev, "DTI: isoc packet status %d bytes at %p\n",
+               urb->actual_length, urb->transfer_buffer);
+       if (urb->actual_length != expected_size) {
+               dev_err(dev, "DTI Error: isoc packet status--bad urb length (%d bytes vs %d needed)\n",
+                       urb->actual_length, expected_size);
+               goto error_parse_buffer;
+       }
+       packet_status = (struct wa_xfer_packet_status_hwaiso *)(wa->dti_buf);
+       if (le16_to_cpu(packet_status->wLength) != expected_size) {
+               dev_err(dev, "DTI Error: isoc packet status--bad length %u\n",
+                       le16_to_cpu(packet_status->wLength));
+               goto error_parse_buffer;
+       }
+       if (packet_status->bPacketType != WA_XFER_ISO_PACKET_STATUS) {
+               dev_err(dev, "DTI Error: isoc packet status--bad type 0x%02x\n",
+                       packet_status->bPacketType);
+               goto error_parse_buffer;
+       }
+       xfer = wa_xfer_get_by_id(wa, wa->dti_isoc_xfer_in_progress);
+       if (xfer == NULL) {
+               dev_err(dev, "DTI Error: isoc packet status--unknown xfer 0x%08x\n",
+                       wa->dti_isoc_xfer_in_progress);
+               goto error_parse_buffer;
+       }
+       spin_lock_irqsave(&xfer->lock, flags);
+       if (unlikely(wa->dti_isoc_xfer_seg >= xfer->segs))
+               goto error_bad_seg;
+       seg = xfer->seg[wa->dti_isoc_xfer_seg];
+       rpipe = xfer->ep->hcpriv;
+
+       /* set urb isoc packet status and length. */
+       xfer->urb->iso_frame_desc[seg->index].status =
+               wa_xfer_status_to_errno(
+               le16_to_cpu(packet_status->PacketStatus[0].PacketStatus));
+       xfer->urb->iso_frame_desc[seg->index].actual_length =
+               le16_to_cpu(packet_status->PacketStatus[0].PacketLength);
+
+       if (!xfer->is_inbound) {
+               /* OUT transfer, complete it -- */
+               seg->status = WA_SEG_DONE;
+               xfer->segs_done++;
+               rpipe_ready = rpipe_avail_inc(rpipe);
+               done = __wa_xfer_is_done(xfer);
+       }
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       wa->dti_state = WA_DTI_TRANSFER_RESULT_PENDING;
+       if (done)
+               wa_xfer_completion(xfer);
+       if (rpipe_ready)
+               wa_xfer_delayed_run(rpipe);
+       wa_xfer_put(xfer);
+       return;
+
+error_bad_seg:
+       spin_unlock_irqrestore(&xfer->lock, flags);
+       wa_xfer_put(xfer);
+error_parse_buffer:
+       return;
+}
+
 /*
  * Callback for the IN data phase
  *
@@ -1687,56 +2202,61 @@ static void wa_buf_in_cb(struct urb *urb)
  * We go back to OFF when we detect a ENOENT or ESHUTDOWN (or too many
  * errors) in the URBs.
  */
-static void wa_xfer_result_cb(struct urb *urb)
+static void wa_dti_cb(struct urb *urb)
 {
        int result;
        struct wahc *wa = urb->context;
        struct device *dev = &wa->usb_iface->dev;
-       struct wa_xfer_result *xfer_result;
        u32 xfer_id;
-       struct wa_xfer *xfer;
        u8 usb_status;
 
        BUG_ON(wa->dti_urb != urb);
        switch (wa->dti_urb->status) {
        case 0:
-               /* We have a xfer result buffer; check it */
-               dev_dbg(dev, "DTI: xfer result %d bytes at %p\n",
-                       urb->actual_length, urb->transfer_buffer);
-               if (wa->dti_urb->actual_length != sizeof(*xfer_result)) {
-                       dev_err(dev, "DTI Error: xfer result--bad size "
-                               "xfer result (%d bytes vs %zu needed)\n",
-                               urb->actual_length, sizeof(*xfer_result));
-                       break;
-               }
-               xfer_result = wa->xfer_result;
-               if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
-                       dev_err(dev, "DTI Error: xfer result--"
-                               "bad header length %u\n",
-                               xfer_result->hdr.bLength);
-                       break;
-               }
-               if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
-                       dev_err(dev, "DTI Error: xfer result--"
-                               "bad header type 0x%02x\n",
-                               xfer_result->hdr.bNotifyType);
-                       break;
-               }
-               usb_status = xfer_result->bTransferStatus & 0x3f;
-               if (usb_status == WA_XFER_STATUS_NOT_FOUND)
-                       /* taken care of already */
-                       break;
-               xfer_id = xfer_result->dwTransferID;
-               xfer = wa_xfer_get_by_id(wa, xfer_id);
-               if (xfer == NULL) {
-                       /* FIXME: transaction might have been cancelled */
-                       dev_err(dev, "DTI Error: xfer result--"
-                               "unknown xfer 0x%08x (status 0x%02x)\n",
-                               xfer_id, usb_status);
-                       break;
+               if (wa->dti_state == WA_DTI_TRANSFER_RESULT_PENDING) {
+                       struct wa_xfer_result *xfer_result;
+                       struct wa_xfer *xfer;
+
+                       /* We have a xfer result buffer; check it */
+                       dev_dbg(dev, "DTI: xfer result %d bytes at %p\n",
+                               urb->actual_length, urb->transfer_buffer);
+                       if (urb->actual_length != sizeof(*xfer_result)) {
+                               dev_err(dev, "DTI Error: xfer result--bad size xfer result (%d bytes vs %zu needed)\n",
+                                       urb->actual_length,
+                                       sizeof(*xfer_result));
+                               break;
+                       }
+                       xfer_result = (struct wa_xfer_result *)(wa->dti_buf);
+                       if (xfer_result->hdr.bLength != sizeof(*xfer_result)) {
+                               dev_err(dev, "DTI Error: xfer result--bad header length %u\n",
+                                       xfer_result->hdr.bLength);
+                               break;
+                       }
+                       if (xfer_result->hdr.bNotifyType != WA_XFER_RESULT) {
+                               dev_err(dev, "DTI Error: xfer result--bad header type 0x%02x\n",
+                                       xfer_result->hdr.bNotifyType);
+                               break;
+                       }
+                       usb_status = xfer_result->bTransferStatus & 0x3f;
+                       if (usb_status == WA_XFER_STATUS_NOT_FOUND)
+                               /* taken care of already */
+                               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. */
+                               dev_err(dev, "DTI Error: xfer result--unknown xfer 0x%08x (status 0x%02x)\n",
+                                       xfer_id, usb_status);
+                               break;
+                       }
+                       wa_xfer_result_chew(wa, xfer, xfer_result);
+                       wa_xfer_put(xfer);
+               } else if (wa->dti_state == WA_DTI_ISOC_PACKET_STATUS_PENDING) {
+                       wa_process_iso_packet_status(wa, urb);
+               } else {
+                       dev_err(dev, "DTI Error: unexpected EP state = %d\n",
+                               wa->dti_state);
                }
-               wa_xfer_result_chew(wa, xfer);
-               wa_xfer_put(xfer);
                break;
        case -ENOENT:           /* (we killed the URB)...so, no broadcast */
        case -ESHUTDOWN:        /* going away! */
@@ -1777,7 +2297,7 @@ out:
  * don't really set it up and start it until the first xfer complete
  * notification arrives, which is what we do here.
  *
- * Follow up in wa_xfer_result_cb(), as that's where the whole state
+ * 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
@@ -1813,8 +2333,8 @@ void wa_handle_notif_xfer(struct wahc *wa, struct wa_notif_hdr *notif_hdr)
        usb_fill_bulk_urb(
                wa->dti_urb, wa->usb_dev,
                usb_rcvbulkpipe(wa->usb_dev, 0x80 | notif_xfer->bEndpoint),
-               wa->xfer_result, wa->xfer_result_size,
-               wa_xfer_result_cb, wa);
+               wa->dti_buf, wa->dti_buf_size,
+               wa_dti_cb, wa);
 
        wa->buf_in_urb = usb_alloc_urb(0, GFP_KERNEL);
        if (wa->buf_in_urb == NULL) {
@@ -1836,6 +2356,7 @@ out:
 
 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;
index 1b035b2eb6b611a075e633b3cf020e2dbfcb5700..1129d0e9e6403dbb6c68a0478fda17bc2117b7b9 100644 (file)
@@ -16,6 +16,7 @@ if EXYNOS_VIDEO
 config EXYNOS_MIPI_DSI
        bool "EXYNOS MIPI DSI driver support."
        depends on ARCH_S5PV210 || ARCH_EXYNOS
+       select GENERIC_PHY
        help
          This enables support for MIPI-DSI device.
 
@@ -29,7 +30,7 @@ config EXYNOS_LCD_S6E8AX0
 
 config EXYNOS_DP
        bool "EXYNOS DP driver support"
-       depends on ARCH_EXYNOS
+       depends on OF && ARCH_EXYNOS
        default n
        help
          This enables support for DP device.
index 12bbede3b091e68ae0600b7fb55c765052d2f66d..5e1a7158005196572459dbf7bfbf561495210410 100644 (file)
@@ -19,8 +19,7 @@
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/of.h>
-
-#include <video/exynos_dp.h>
+#include <linux/phy/phy.h>
 
 #include "exynos_dp_core.h"
 
@@ -894,26 +893,17 @@ static void exynos_dp_hotplug(struct work_struct *work)
                dev_err(dp->dev, "unable to config video\n");
 }
 
-#ifdef CONFIG_OF
-static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
+static struct video_info *exynos_dp_dt_parse_pdata(struct device *dev)
 {
        struct device_node *dp_node = dev->of_node;
-       struct exynos_dp_platdata *pd;
        struct video_info *dp_video_config;
 
-       pd = devm_kzalloc(dev, sizeof(*pd), GFP_KERNEL);
-       if (!pd) {
-               dev_err(dev, "memory allocation for pdata failed\n");
-               return ERR_PTR(-ENOMEM);
-       }
        dp_video_config = devm_kzalloc(dev,
                                sizeof(*dp_video_config), GFP_KERNEL);
-
        if (!dp_video_config) {
                dev_err(dev, "memory allocation for video config failed\n");
                return ERR_PTR(-ENOMEM);
        }
-       pd->video_info = dp_video_config;
 
        dp_video_config->h_sync_polarity =
                of_property_read_bool(dp_node, "hsync-active-high");
@@ -960,7 +950,7 @@ static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
                return ERR_PTR(-EINVAL);
        }
 
-       return pd;
+       return dp_video_config;
 }
 
 static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
@@ -971,8 +961,11 @@ static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
 
        dp_phy_node = of_find_node_by_name(dp_phy_node, "dptx-phy");
        if (!dp_phy_node) {
-               dev_err(dp->dev, "could not find dptx-phy node\n");
-               return -ENODEV;
+               dp->phy = devm_phy_get(dp->dev, "dp");
+               if (IS_ERR(dp->phy))
+                       return PTR_ERR(dp->phy);
+               else
+                       return 0;
        }
 
        if (of_property_read_u32(dp_phy_node, "reg", &phy_base)) {
@@ -1003,48 +996,34 @@ err:
 
 static void exynos_dp_phy_init(struct exynos_dp_device *dp)
 {
-       u32 reg;
-
-       reg = __raw_readl(dp->phy_addr);
-       reg |= dp->enable_mask;
-       __raw_writel(reg, dp->phy_addr);
-}
-
-static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
-{
-       u32 reg;
-
-       reg = __raw_readl(dp->phy_addr);
-       reg &= ~(dp->enable_mask);
-       __raw_writel(reg, dp->phy_addr);
-}
-#else
-static struct exynos_dp_platdata *exynos_dp_dt_parse_pdata(struct device *dev)
-{
-       return NULL;
-}
-
-static int exynos_dp_dt_parse_phydata(struct exynos_dp_device *dp)
-{
-       return -EINVAL;
-}
-
-static void exynos_dp_phy_init(struct exynos_dp_device *dp)
-{
-       return;
+       if (dp->phy) {
+               phy_power_on(dp->phy);
+       } else if (dp->phy_addr) {
+               u32 reg;
+
+               reg = __raw_readl(dp->phy_addr);
+               reg |= dp->enable_mask;
+               __raw_writel(reg, dp->phy_addr);
+       }
 }
 
 static void exynos_dp_phy_exit(struct exynos_dp_device *dp)
 {
-       return;
+       if (dp->phy) {
+               phy_power_off(dp->phy);
+       } else if (dp->phy_addr) {
+               u32 reg;
+
+               reg = __raw_readl(dp->phy_addr);
+               reg &= ~(dp->enable_mask);
+               __raw_writel(reg, dp->phy_addr);
+       }
 }
-#endif /* CONFIG_OF */
 
 static int exynos_dp_probe(struct platform_device *pdev)
 {
        struct resource *res;
        struct exynos_dp_device *dp;
-       struct exynos_dp_platdata *pdata;
 
        int ret = 0;
 
@@ -1057,21 +1036,13 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
        dp->dev = &pdev->dev;
 
-       if (pdev->dev.of_node) {
-               pdata = exynos_dp_dt_parse_pdata(&pdev->dev);
-               if (IS_ERR(pdata))
-                       return PTR_ERR(pdata);
+       dp->video_info = exynos_dp_dt_parse_pdata(&pdev->dev);
+       if (IS_ERR(dp->video_info))
+               return PTR_ERR(dp->video_info);
 
-               ret = exynos_dp_dt_parse_phydata(dp);
-               if (ret)
-                       return ret;
-       } else {
-               pdata = pdev->dev.platform_data;
-               if (!pdata) {
-                       dev_err(&pdev->dev, "no platform data\n");
-                       return -EINVAL;
-               }
-       }
+       ret = exynos_dp_dt_parse_phydata(dp);
+       if (ret)
+               return ret;
 
        dp->clock = devm_clk_get(&pdev->dev, "dp");
        if (IS_ERR(dp->clock)) {
@@ -1095,15 +1066,7 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
        INIT_WORK(&dp->hotplug_work, exynos_dp_hotplug);
 
-       dp->video_info = pdata->video_info;
-
-       if (pdev->dev.of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_init(dp);
-       } else {
-               if (pdata->phy_init)
-                       pdata->phy_init();
-       }
+       exynos_dp_phy_init(dp);
 
        exynos_dp_init_dp(dp);
 
@@ -1121,18 +1084,11 @@ static int exynos_dp_probe(struct platform_device *pdev)
 
 static int exynos_dp_remove(struct platform_device *pdev)
 {
-       struct exynos_dp_platdata *pdata = pdev->dev.platform_data;
        struct exynos_dp_device *dp = platform_get_drvdata(pdev);
 
        flush_work(&dp->hotplug_work);
 
-       if (pdev->dev.of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_exit(dp);
-       } else {
-               if (pdata->phy_exit)
-                       pdata->phy_exit();
-       }
+       exynos_dp_phy_exit(dp);
 
        clk_disable_unprepare(dp->clock);
 
@@ -1143,20 +1099,13 @@ static int exynos_dp_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM_SLEEP
 static int exynos_dp_suspend(struct device *dev)
 {
-       struct exynos_dp_platdata *pdata = dev->platform_data;
        struct exynos_dp_device *dp = dev_get_drvdata(dev);
 
        disable_irq(dp->irq);
 
        flush_work(&dp->hotplug_work);
 
-       if (dev->of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_exit(dp);
-       } else {
-               if (pdata->phy_exit)
-                       pdata->phy_exit();
-       }
+       exynos_dp_phy_exit(dp);
 
        clk_disable_unprepare(dp->clock);
 
@@ -1165,16 +1114,9 @@ static int exynos_dp_suspend(struct device *dev)
 
 static int exynos_dp_resume(struct device *dev)
 {
-       struct exynos_dp_platdata *pdata = dev->platform_data;
        struct exynos_dp_device *dp = dev_get_drvdata(dev);
 
-       if (dev->of_node) {
-               if (dp->phy_addr)
-                       exynos_dp_phy_init(dp);
-       } else {
-               if (pdata->phy_init)
-                       pdata->phy_init();
-       }
+       exynos_dp_phy_init(dp);
 
        clk_prepare_enable(dp->clock);
 
@@ -1203,7 +1145,7 @@ static struct platform_driver exynos_dp_driver = {
                .name   = "exynos-dp",
                .owner  = THIS_MODULE,
                .pm     = &exynos_dp_pm_ops,
-               .of_match_table = of_match_ptr(exynos_dp_match),
+               .of_match_table = exynos_dp_match,
        },
 };
 
index 6c567bbf2fb8fcec22f4f7949916e5a515b7b573..607e36d0c147ada6fc2203a7dc657d2490c1a91d 100644 (file)
 #ifndef _EXYNOS_DP_CORE_H
 #define _EXYNOS_DP_CORE_H
 
+#define DP_TIMEOUT_LOOP_COUNT 100
+#define MAX_CR_LOOP 5
+#define MAX_EQ_LOOP 5
+
+enum link_rate_type {
+       LINK_RATE_1_62GBPS = 0x06,
+       LINK_RATE_2_70GBPS = 0x0a
+};
+
+enum link_lane_count_type {
+       LANE_COUNT1 = 1,
+       LANE_COUNT2 = 2,
+       LANE_COUNT4 = 4
+};
+
+enum link_training_state {
+       START,
+       CLOCK_RECOVERY,
+       EQUALIZER_TRAINING,
+       FINISHED,
+       FAILED
+};
+
+enum voltage_swing_level {
+       VOLTAGE_LEVEL_0,
+       VOLTAGE_LEVEL_1,
+       VOLTAGE_LEVEL_2,
+       VOLTAGE_LEVEL_3,
+};
+
+enum pre_emphasis_level {
+       PRE_EMPHASIS_LEVEL_0,
+       PRE_EMPHASIS_LEVEL_1,
+       PRE_EMPHASIS_LEVEL_2,
+       PRE_EMPHASIS_LEVEL_3,
+};
+
+enum pattern_set {
+       PRBS7,
+       D10_2,
+       TRAINING_PTN1,
+       TRAINING_PTN2,
+       DP_NONE
+};
+
+enum color_space {
+       COLOR_RGB,
+       COLOR_YCBCR422,
+       COLOR_YCBCR444
+};
+
+enum color_depth {
+       COLOR_6,
+       COLOR_8,
+       COLOR_10,
+       COLOR_12
+};
+
+enum color_coefficient {
+       COLOR_YCBCR601,
+       COLOR_YCBCR709
+};
+
+enum dynamic_range {
+       VESA,
+       CEA
+};
+
+enum pll_status {
+       PLL_UNLOCKED,
+       PLL_LOCKED
+};
+
+enum clock_recovery_m_value_type {
+       CALCULATED_M,
+       REGISTER_M
+};
+
+enum video_timing_recognition_type {
+       VIDEO_TIMING_FROM_CAPTURE,
+       VIDEO_TIMING_FROM_REGISTER
+};
+
+enum analog_power_block {
+       AUX_BLOCK,
+       CH0_BLOCK,
+       CH1_BLOCK,
+       CH2_BLOCK,
+       CH3_BLOCK,
+       ANALOG_TOTAL,
+       POWER_ALL
+};
+
 enum dp_irq_type {
        DP_IRQ_TYPE_HP_CABLE_IN,
        DP_IRQ_TYPE_HP_CABLE_OUT,
@@ -20,6 +113,22 @@ enum dp_irq_type {
        DP_IRQ_TYPE_UNKNOWN,
 };
 
+struct video_info {
+       char *name;
+
+       bool h_sync_polarity;
+       bool v_sync_polarity;
+       bool interlaced;
+
+       enum color_space color_space;
+       enum dynamic_range dynamic_range;
+       enum color_coefficient ycbcr_coeff;
+       enum color_depth color_depth;
+
+       enum link_rate_type link_rate;
+       enum link_lane_count_type lane_count;
+};
+
 struct link_train {
        int eq_loop;
        int cr_loop[4];
@@ -42,6 +151,7 @@ struct exynos_dp_device {
        struct video_info       *video_info;
        struct link_train       link_train;
        struct work_struct      hotplug_work;
+       struct phy              *phy;
 };
 
 /* exynos_dp_reg.c */
index 29d9d035c73a76e7313650fcbb6045b7dd0cfa38..b70da5052ff0287233b3668574df40ed5b7e8fb1 100644 (file)
@@ -14,8 +14,6 @@
 #include <linux/io.h>
 #include <linux/delay.h>
 
-#include <video/exynos_dp.h>
-
 #include "exynos_dp_core.h"
 #include "exynos_dp_reg.h"
 
index 32e540600f995b42937653832c3e4d8508012c50..00b3a52c1d68766d42cab3c65a1a9405d4a04617 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/interrupt.h>
 #include <linux/kthread.h>
 #include <linux/notifier.h>
+#include <linux/phy/phy.h>
 #include <linux/regulator/consumer.h>
 #include <linux/pm_runtime.h>
 #include <linux/err.h>
@@ -156,8 +157,7 @@ static int exynos_mipi_dsi_blank_mode(struct mipi_dsim_device *dsim, int power)
                exynos_mipi_regulator_enable(dsim);
 
                /* enable MIPI-DSI PHY. */
-               if (dsim->pd->phy_enable)
-                       dsim->pd->phy_enable(pdev, true);
+               phy_power_on(dsim->phy);
 
                clk_enable(dsim->clock);
 
@@ -373,6 +373,10 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
                return ret;
        }
 
+       dsim->phy = devm_phy_get(&pdev->dev, "dsim");
+       if (IS_ERR(dsim->phy))
+               return PTR_ERR(dsim->phy);
+
        dsim->clock = devm_clk_get(&pdev->dev, "dsim0");
        if (IS_ERR(dsim->clock)) {
                dev_err(&pdev->dev, "failed to get dsim clock source\n");
@@ -439,8 +443,7 @@ static int exynos_mipi_dsi_probe(struct platform_device *pdev)
        exynos_mipi_regulator_enable(dsim);
 
        /* enable MIPI-DSI PHY. */
-       if (dsim->pd->phy_enable)
-               dsim->pd->phy_enable(pdev, true);
+       phy_power_on(dsim->phy);
 
        exynos_mipi_update_cfg(dsim);
 
@@ -504,9 +507,8 @@ static int exynos_mipi_dsi_suspend(struct device *dev)
        if (client_drv && client_drv->suspend)
                client_drv->suspend(client_dev);
 
-       /* enable MIPI-DSI PHY. */
-       if (dsim->pd->phy_enable)
-               dsim->pd->phy_enable(pdev, false);
+       /* disable MIPI-DSI PHY. */
+       phy_power_off(dsim->phy);
 
        clk_disable(dsim->clock);
 
@@ -536,8 +538,7 @@ static int exynos_mipi_dsi_resume(struct device *dev)
        exynos_mipi_regulator_enable(dsim);
 
        /* enable MIPI-DSI PHY. */
-       if (dsim->pd->phy_enable)
-               dsim->pd->phy_enable(pdev, true);
+       phy_power_on(dsim->phy);
 
        clk_enable(dsim->clock);
 
index 81cbbdb96aae2bff8969fd68fefc086231de3c4b..673a3ce67f311df6567aa780dfdbf3d7adbe0cf9 100644 (file)
@@ -26,6 +26,7 @@
 #define __TWL_H_
 
 #include <linux/types.h>
+#include <linux/phy/phy.h>
 #include <linux/input/matrix_keypad.h>
 
 /*
@@ -615,6 +616,7 @@ enum twl4030_usb_mode {
 struct twl4030_usb_data {
        enum twl4030_usb_mode   usb_mode;
        unsigned long           features;
+       struct phy_init_data    *init_data;
 
        int             (*phy_init)(struct device *dev);
        int             (*phy_exit)(struct device *dev);
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
new file mode 100644 (file)
index 0000000..6d72269
--- /dev/null
@@ -0,0 +1,270 @@
+/*
+ * phy.h -- generic phy header file
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@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.
+ */
+
+#ifndef __DRIVERS_PHY_H
+#define __DRIVERS_PHY_H
+
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+
+struct phy;
+
+/**
+ * struct phy_ops - set of function pointers for performing phy operations
+ * @init: operation to be performed for initializing phy
+ * @exit: operation to be performed while exiting
+ * @power_on: powering on the phy
+ * @power_off: powering off the phy
+ * @owner: the module owner containing the ops
+ */
+struct phy_ops {
+       int     (*init)(struct phy *phy);
+       int     (*exit)(struct phy *phy);
+       int     (*power_on)(struct phy *phy);
+       int     (*power_off)(struct phy *phy);
+       struct module *owner;
+};
+
+/**
+ * struct phy - represents the phy device
+ * @dev: phy device
+ * @id: id of the phy device
+ * @ops: function pointers for performing phy operations
+ * @init_data: list of PHY consumers (non-dt only)
+ * @mutex: mutex to protect phy_ops
+ * @init_count: used to protect when the PHY is used by multiple consumers
+ * @power_count: used to protect when the PHY is used by multiple consumers
+ */
+struct phy {
+       struct device           dev;
+       int                     id;
+       const struct phy_ops    *ops;
+       struct phy_init_data    *init_data;
+       struct mutex            mutex;
+       int                     init_count;
+       int                     power_count;
+};
+
+/**
+ * struct phy_provider - represents the phy provider
+ * @dev: phy provider device
+ * @owner: the module owner having of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy pointer
+ * @list: to maintain a linked list of PHY providers
+ */
+struct phy_provider {
+       struct device           *dev;
+       struct module           *owner;
+       struct list_head        list;
+       struct phy * (*of_xlate)(struct device *dev,
+               struct of_phandle_args *args);
+};
+
+/**
+ * struct phy_consumer - represents the phy consumer
+ * @dev_name: the device name of the controller that will use this PHY device
+ * @port: name given to the consumer port
+ */
+struct phy_consumer {
+       const char *dev_name;
+       const char *port;
+};
+
+/**
+ * struct phy_init_data - contains the list of PHY consumers
+ * @num_consumers: number of consumers for this PHY device
+ * @consumers: list of PHY consumers
+ */
+struct phy_init_data {
+       unsigned int num_consumers;
+       struct phy_consumer *consumers;
+};
+
+#define PHY_CONSUMER(_dev_name, _port)                         \
+{                                                              \
+       .dev_name       = _dev_name,                            \
+       .port           = _port,                                \
+}
+
+#define        to_phy(dev)     (container_of((dev), struct phy, dev))
+
+#define        of_phy_provider_register(dev, xlate)    \
+       __of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+#define        devm_of_phy_provider_register(dev, xlate)       \
+       __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+static inline void phy_set_drvdata(struct phy *phy, void *data)
+{
+       dev_set_drvdata(&phy->dev, data);
+}
+
+static inline void *phy_get_drvdata(struct phy *phy)
+{
+       return dev_get_drvdata(&phy->dev);
+}
+
+#if IS_ENABLED(CONFIG_GENERIC_PHY)
+int phy_pm_runtime_get(struct phy *phy);
+int phy_pm_runtime_get_sync(struct phy *phy);
+int phy_pm_runtime_put(struct phy *phy);
+int phy_pm_runtime_put_sync(struct phy *phy);
+void phy_pm_runtime_allow(struct phy *phy);
+void phy_pm_runtime_forbid(struct phy *phy);
+int phy_init(struct phy *phy);
+int phy_exit(struct phy *phy);
+int phy_power_on(struct phy *phy);
+int phy_power_off(struct phy *phy);
+struct phy *phy_get(struct device *dev, const char *string);
+struct phy *devm_phy_get(struct device *dev, const char *string);
+void phy_put(struct phy *phy);
+void devm_phy_put(struct device *dev, struct phy *phy);
+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,
+       struct phy_init_data *init_data);
+struct phy *devm_phy_create(struct device *dev,
+       const struct phy_ops *ops, struct phy_init_data *init_data);
+void phy_destroy(struct phy *phy);
+void devm_phy_destroy(struct device *dev, struct phy *phy);
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args));
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args));
+void of_phy_provider_unregister(struct phy_provider *phy_provider);
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider);
+#else
+static inline int phy_pm_runtime_get(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_get_sync(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_put(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_put_sync(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline void phy_pm_runtime_allow(struct phy *phy)
+{
+       return;
+}
+
+static inline void phy_pm_runtime_forbid(struct phy *phy)
+{
+       return;
+}
+
+static inline int phy_init(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_exit(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_power_on(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline int phy_power_off(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline struct phy *phy_get(struct device *dev, const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_phy_get(struct device *dev, const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void phy_put(struct phy *phy)
+{
+}
+
+static inline void devm_phy_put(struct device *dev, struct phy *phy)
+{
+}
+
+static inline struct phy *of_phy_simple_xlate(struct device *dev,
+       struct of_phandle_args *args)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *phy_create(struct device *dev,
+       const struct phy_ops *ops, struct phy_init_data *init_data)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_phy_create(struct device *dev,
+       const struct phy_ops *ops, struct phy_init_data *init_data)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void phy_destroy(struct phy *phy)
+{
+}
+
+static inline void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+}
+
+static inline struct phy_provider *__of_phy_provider_register(
+       struct device *dev, struct module *owner, struct phy * (*of_xlate)(
+       struct device *dev, struct of_phandle_args *args))
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy_provider *__devm_of_phy_provider_register(struct device
+       *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+}
+
+static inline void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider)
+{
+}
+#endif
+
+#endif /* __DRIVERS_PHY_H */
index bf34e17cee7f6eb9c9068d811c37dae3b8c10fe0..c2fd9024717cb5aca213c1700126b69d6d350e24 100644 (file)
@@ -25,13 +25,4 @@ struct s5p_platform_mipi_csis {
        u8 hs_settle;
 };
 
-/**
- * s5p_csis_phy_enable - global MIPI-CSI receiver D-PHY control
- * @id:     MIPI-CSIS harware instance index (0...1)
- * @on:     true to enable D-PHY and deassert its reset
- *          false to disable D-PHY
- * @return: 0 on success, or negative error code on failure
- */
-int s5p_csis_phy_enable(int id, bool on);
-
 #endif /* __PLAT_SAMSUNG_MIPI_CSIS_H_ */
diff --git a/include/linux/platform_data/usb-ehci-s5p.h b/include/linux/platform_data/usb-ehci-s5p.h
deleted file mode 100644 (file)
index 5f28cae..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- * Author: Joonyoung Shim <jy0922.shim@samsung.com>
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef __PLAT_SAMSUNG_EHCI_H
-#define __PLAT_SAMSUNG_EHCI_H __FILE__
-
-struct s5p_ehci_platdata {
-       int (*phy_init)(struct platform_device *pdev, int type);
-       int (*phy_exit)(struct platform_device *pdev, int type);
-};
-
-extern void s5p_ehci_set_platdata(struct s5p_ehci_platdata *pd);
-
-#endif /* __PLAT_SAMSUNG_EHCI_H */
diff --git a/include/linux/platform_data/usb-ohci-exynos.h b/include/linux/platform_data/usb-ohci-exynos.h
deleted file mode 100644 (file)
index c256c59..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (C) 2011 Samsung Electronics Co.Ltd
- *             http://www.samsung.com/
- *
- * This program is free software; you can redistribute  it and/or modify it
- * under  the terms of  the GNU General  Public License as published by the
- * Free Software Foundation;  either version 2 of the  License, or (at your
- * option) any later version.
- */
-
-#ifndef __MACH_EXYNOS_OHCI_H
-#define __MACH_EXYNOS_OHCI_H
-
-struct exynos4_ohci_platdata {
-       int (*phy_init)(struct platform_device *pdev, int type);
-       int (*phy_exit)(struct platform_device *pdev, int type);
-};
-
-extern void exynos4_ohci_set_platdata(struct exynos4_ohci_platdata *pd);
-
-#endif /* __MACH_EXYNOS_OHCI_H */
index 001629cd1a97b831f2eb448013c114b3a76f0de7..7454865ad14834f50a05dc9baf7f6d78ea3de7b8 100644 (file)
@@ -475,7 +475,8 @@ struct usb3_lpm_parameters {
  * @lpm_capable: device supports LPM
  * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM
  * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM
- * @usb2_hw_lpm_enabled: USB2 hardware LPM enabled
+ * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled
+ * @usb2_hw_lpm_allowed: Userspace allows USB 2.0 LPM to be enabled
  * @usb3_lpm_enabled: USB3 hardware LPM enabled
  * @string_langid: language ID for strings
  * @product: iProduct string, if present (static)
@@ -548,6 +549,7 @@ struct usb_device {
        unsigned usb2_hw_lpm_capable:1;
        unsigned usb2_hw_lpm_besl_capable:1;
        unsigned usb2_hw_lpm_enabled:1;
+       unsigned usb2_hw_lpm_allowed:1;
        unsigned usb3_lpm_enabled:1;
        int string_langid;
 
@@ -702,7 +704,7 @@ extern int usb_alloc_streams(struct usb_interface *interface,
                unsigned int num_streams, gfp_t mem_flags);
 
 /* Reverts a group of bulk endpoints back to not using stream IDs. */
-extern void usb_free_streams(struct usb_interface *interface,
+extern int usb_free_streams(struct usb_interface *interface,
                struct usb_host_endpoint **eps, unsigned int num_eps,
                gfp_t mem_flags);
 
@@ -1209,11 +1211,13 @@ struct usb_anchor {
        struct list_head urb_list;
        wait_queue_head_t wait;
        spinlock_t lock;
+       atomic_t suspend_wakeups;
        unsigned int poisoned:1;
 };
 
 static inline void init_usb_anchor(struct usb_anchor *anchor)
 {
+       memset(anchor, 0, sizeof(*anchor));
        INIT_LIST_HEAD(&anchor->urb_list);
        init_waitqueue_head(&anchor->wait);
        spin_lock_init(&anchor->lock);
@@ -1574,6 +1578,8 @@ extern void usb_kill_anchored_urbs(struct usb_anchor *anchor);
 extern void usb_poison_anchored_urbs(struct usb_anchor *anchor);
 extern void usb_unpoison_anchored_urbs(struct usb_anchor *anchor);
 extern void usb_unlink_anchored_urbs(struct usb_anchor *anchor);
+extern void usb_anchor_suspend_wakeups(struct usb_anchor *anchor);
+extern void usb_anchor_resume_wakeups(struct usb_anchor *anchor);
 extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor);
 extern void usb_unanchor_urb(struct urb *urb);
 extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
index 75efc45eaa2ff361d4cb92fa4c325eb8d5d50461..b8aba196f7f12948cbf4d2a587371b081f150d1c 100644 (file)
@@ -73,6 +73,7 @@ struct giveback_urb_bh {
        spinlock_t lock;
        struct list_head  head;
        struct tasklet_struct bh;
+       struct usb_host_endpoint *completing_ep;
 };
 
 struct usb_hcd {
@@ -140,6 +141,7 @@ struct usb_hcd {
        unsigned                wireless:1;     /* Wireless 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 int            irq;            /* irq allocated */
        void __iomem            *regs;          /* device memory/io */
@@ -378,6 +380,12 @@ static inline int hcd_giveback_urb_in_bh(struct usb_hcd *hcd)
        return hcd->driver->flags & HCD_BH;
 }
 
+static inline bool hcd_periodic_completion_in_progress(struct usb_hcd *hcd,
+               struct usb_host_endpoint *ep)
+{
+       return hcd->high_prio_bh.completing_ep == ep;
+}
+
 extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
 extern int usb_hcd_check_unlink_urb(struct usb_hcd *hcd, struct urb *urb,
                int status);
@@ -428,6 +436,8 @@ extern int usb_hcd_pci_probe(struct pci_dev *dev,
 extern void usb_hcd_pci_remove(struct pci_dev *dev);
 extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 
+extern int usb_hcd_amd_remote_wakeup_quirk(struct pci_dev *dev);
+
 #ifdef CONFIG_PM
 extern const struct dev_pm_ops usb_hcd_pci_pm_ops;
 #endif
@@ -496,6 +506,7 @@ struct usb_tt {
        struct usb_device       *hub;   /* upstream highspeed hub */
        int                     multi;  /* true means one TT per port */
        unsigned                think_time;     /* think time in ns */
+       void                    *hcpriv;        /* HCD private data */
 
        /* for control/bulk error recovery (CLEAR_TT_BUFFER) */
        spinlock_t              lock;
@@ -554,9 +565,8 @@ extern void usb_ep0_reinit(struct usb_device *);
                 * of (7/6 * 8 * bytecount) = 9.33 * bytecount */
                /* bytecount = data payload byte count */
 
-#define NS_TO_US(ns)   ((ns + 500L) / 1000L)
-                       /* convert & round nanoseconds to microseconds */
-
+#define NS_TO_US(ns)   DIV_ROUND_UP(ns, 1000L)
+                       /* convert nanoseconds to microseconds, rounding up */
 
 /*
  * Full/low speed bandwidth allocation constants/support.
diff --git a/include/linux/usb/intel_mid_otg.h b/include/linux/usb/intel_mid_otg.h
deleted file mode 100644 (file)
index 756cf55..0000000
+++ /dev/null
@@ -1,180 +0,0 @@
-/*
- * Intel MID (Langwell/Penwell) USB OTG Transceiver driver
- * Copyright (C) 2008 - 2010, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
- */
-
-#ifndef __INTEL_MID_OTG_H
-#define __INTEL_MID_OTG_H
-
-#include <linux/pm.h>
-#include <linux/usb/otg.h>
-#include <linux/notifier.h>
-
-struct intel_mid_otg_xceiv;
-
-/* This is a common data structure for Intel MID platform to
- * save values of the OTG state machine */
-struct otg_hsm {
-       /* Input */
-       int a_bus_resume;
-       int a_bus_suspend;
-       int a_conn;
-       int a_sess_vld;
-       int a_srp_det;
-       int a_vbus_vld;
-       int b_bus_resume;
-       int b_bus_suspend;
-       int b_conn;
-       int b_se0_srp;
-       int b_ssend_srp;
-       int b_sess_end;
-       int b_sess_vld;
-       int id;
-/* id values */
-#define ID_B           0x05
-#define ID_A           0x04
-#define ID_ACA_C       0x03
-#define ID_ACA_B       0x02
-#define ID_ACA_A       0x01
-       int power_up;
-       int adp_change;
-       int test_device;
-
-       /* Internal variables */
-       int a_set_b_hnp_en;
-       int b_srp_done;
-       int b_hnp_enable;
-       int hnp_poll_enable;
-
-       /* Timeout indicator for timers */
-       int a_wait_vrise_tmout;
-       int a_wait_bcon_tmout;
-       int a_aidl_bdis_tmout;
-       int a_bidl_adis_tmout;
-       int a_bidl_adis_tmr;
-       int a_wait_vfall_tmout;
-       int b_ase0_brst_tmout;
-       int b_bus_suspend_tmout;
-       int b_srp_init_tmout;
-       int b_srp_fail_tmout;
-       int b_srp_fail_tmr;
-       int b_adp_sense_tmout;
-
-       /* Informative variables */
-       int a_bus_drop;
-       int a_bus_req;
-       int a_clr_err;
-       int b_bus_req;
-       int a_suspend_req;
-       int b_bus_suspend_vld;
-
-       /* Output */
-       int drv_vbus;
-       int loc_conn;
-       int loc_sof;
-
-       /* Others */
-       int vbus_srp_up;
-};
-
-/* must provide ULPI access function to read/write registers implemented in
- * ULPI address space */
-struct iotg_ulpi_access_ops {
-       int     (*read)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 *val);
-       int     (*write)(struct intel_mid_otg_xceiv *iotg, u8 reg, u8 val);
-};
-
-#define OTG_A_DEVICE   0x0
-#define OTG_B_DEVICE   0x1
-
-/*
- * the Intel MID (Langwell/Penwell) otg transceiver driver needs to interact
- * with device and host drivers to implement the USB OTG related feature. More
- * function members are added based on usb_phy data structure for this
- * purpose.
- */
-struct intel_mid_otg_xceiv {
-       struct usb_phy          otg;
-       struct otg_hsm          hsm;
-
-       /* base address */
-       void __iomem            *base;
-
-       /* ops to access ulpi */
-       struct iotg_ulpi_access_ops     ulpi_ops;
-
-       /* atomic notifier for interrupt context */
-       struct atomic_notifier_head     iotg_notifier;
-
-       /* start/stop USB Host function */
-       int     (*start_host)(struct intel_mid_otg_xceiv *iotg);
-       int     (*stop_host)(struct intel_mid_otg_xceiv *iotg);
-
-       /* start/stop USB Peripheral function */
-       int     (*start_peripheral)(struct intel_mid_otg_xceiv *iotg);
-       int     (*stop_peripheral)(struct intel_mid_otg_xceiv *iotg);
-
-       /* start/stop ADP sense/probe function */
-       int     (*set_adp_probe)(struct intel_mid_otg_xceiv *iotg,
-                                       bool enabled, int dev);
-       int     (*set_adp_sense)(struct intel_mid_otg_xceiv *iotg,
-                                       bool enabled);
-
-#ifdef CONFIG_PM
-       /* suspend/resume USB host function */
-       int     (*suspend_host)(struct intel_mid_otg_xceiv *iotg,
-                                       pm_message_t message);
-       int     (*resume_host)(struct intel_mid_otg_xceiv *iotg);
-
-       int     (*suspend_peripheral)(struct intel_mid_otg_xceiv *iotg,
-                                       pm_message_t message);
-       int     (*resume_peripheral)(struct intel_mid_otg_xceiv *iotg);
-#endif
-
-};
-static inline
-struct intel_mid_otg_xceiv *otg_to_mid_xceiv(struct usb_phy *otg)
-{
-       return container_of(otg, struct intel_mid_otg_xceiv, otg);
-}
-
-#define MID_OTG_NOTIFY_CONNECT         0x0001
-#define MID_OTG_NOTIFY_DISCONN         0x0002
-#define MID_OTG_NOTIFY_HSUSPEND                0x0003
-#define MID_OTG_NOTIFY_HRESUME         0x0004
-#define MID_OTG_NOTIFY_CSUSPEND                0x0005
-#define MID_OTG_NOTIFY_CRESUME         0x0006
-#define MID_OTG_NOTIFY_HOSTADD         0x0007
-#define MID_OTG_NOTIFY_HOSTREMOVE      0x0008
-#define MID_OTG_NOTIFY_CLIENTADD       0x0009
-#define MID_OTG_NOTIFY_CLIENTREMOVE    0x000a
-
-static inline int
-intel_mid_otg_register_notifier(struct intel_mid_otg_xceiv *iotg,
-                               struct notifier_block *nb)
-{
-       return atomic_notifier_chain_register(&iotg->iotg_notifier, nb);
-}
-
-static inline void
-intel_mid_otg_unregister_notifier(struct intel_mid_otg_xceiv *iotg,
-                               struct notifier_block *nb)
-{
-       atomic_notifier_chain_unregister(&iotg->iotg_notifier, nb);
-}
-
-#endif /* __INTEL_MID_OTG_H */
index 053c26841cc39bde245acac370e152189f256814..eb505250940af073cd1059f82c4e48d773df3acb 100644 (file)
@@ -99,8 +99,6 @@ struct musb_hdrc_platform_data {
        /* MUSB_HOST, MUSB_PERIPHERAL, or MUSB_OTG */
        u8              mode;
 
-       u8              has_mailbox:1;
-
        /* for clk_get() */
        const char      *clock;
 
index 27b5b8c931b0bd70d1e20d636b08beb6ebe3dc64..596b01918813aaeffbeb170001cb370ae9f5f776 100644 (file)
 #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 */
+};
+
 struct omap_control_usb {
        struct device *dev;
 
-       u32 __iomem *dev_conf;
        u32 __iomem *otghs_control;
-       u32 __iomem *phy_power;
+       u32 __iomem *power;
+       u32 __iomem *power_aux;
 
        struct clk *sys_clk;
 
-       u32 type;
-};
-
-struct omap_control_usb_platform_data {
-       u8 type;
+       enum omap_control_usb_type type;
 };
 
 enum omap_control_usb_mode {
@@ -42,10 +45,6 @@ enum omap_control_usb_mode {
        USB_MODE_DISCONNECT,
 };
 
-/* To differentiate ctrl module IP having either mailbox or USB3 PHY power */
-#define        OMAP_CTRL_DEV_TYPE1             0x1
-#define        OMAP_CTRL_DEV_TYPE2             0x2
-
 #define        OMAP_CTRL_DEV_PHY_PD            BIT(0)
 
 #define        OMAP_CTRL_DEV_AVALID            BIT(0)
@@ -63,26 +62,18 @@ enum omap_control_usb_mode {
 #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)
+
 #if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
-extern struct device *omap_get_control_dev(void);
 extern void omap_control_usb_phy_power(struct device *dev, int on);
-extern void omap_control_usb3_phy_power(struct device *dev, bool on);
 extern void omap_control_usb_set_mode(struct device *dev,
        enum omap_control_usb_mode mode);
 #else
-static inline struct device *omap_get_control_dev(void)
-{
-       return ERR_PTR(-ENODEV);
-}
 
 static inline void omap_control_usb_phy_power(struct device *dev, int on)
 {
 }
 
-static inline void omap_control_usb3_phy_power(struct device *dev, int on)
-{
-}
-
 static inline void omap_control_usb_set_mode(struct device *dev,
        enum omap_control_usb_mode mode)
 {
index d528b804515081f9d5388dc565822241cfd6adc9..704a1ab8240ca124f29c5ce361c871090d28ea5b 100644 (file)
@@ -320,6 +320,8 @@ extern struct usb_serial_port *usb_serial_port_get_by_minor(unsigned int minor);
 extern void usb_serial_put(struct usb_serial *serial);
 extern int usb_serial_generic_open(struct tty_struct *tty,
        struct usb_serial_port *port);
+extern int usb_serial_generic_write_start(struct usb_serial_port *port,
+                                                       gfp_t mem_flags);
 extern int usb_serial_generic_write(struct tty_struct *tty,
        struct usb_serial_port *port, const unsigned char *buf, int count);
 extern void usb_serial_generic_close(struct usb_serial_port *port);
index 4ff744e2b678a27f051649b86f1c8ee4eb8210d1..c1257130769b5465a04d7d162207613f378acfde 100644 (file)
@@ -142,7 +142,7 @@ enum wa_notif_type {
 struct wa_notif_hdr {
        u8 bLength;
        u8 bNotifyType;                 /* enum wa_notif_type */
-} __attribute__((packed));
+} __packed;
 
 /**
  * HWA DN Received notification [(WUSB] section 8.5.4.2)
@@ -158,7 +158,7 @@ struct hwa_notif_dn {
        u8 bSourceDeviceAddr;           /* from errata 2005/07 */
        u8 bmAttributes;
        struct wusb_dn_hdr dndata[];
-} __attribute__((packed));
+} __packed;
 
 /* [WUSB] section 8.3.3 */
 enum wa_xfer_type {
@@ -167,6 +167,8 @@ enum wa_xfer_type {
        WA_XFER_TYPE_ISO = 0x82,
        WA_XFER_RESULT = 0x83,
        WA_XFER_ABORT = 0x84,
+       WA_XFER_ISO_PACKET_INFO = 0xA0,
+       WA_XFER_ISO_PACKET_STATUS = 0xA1,
 };
 
 /* [WUSB] section 8.3.3 */
@@ -177,28 +179,47 @@ struct wa_xfer_hdr {
        __le32 dwTransferID;            /* Host-assigned ID */
        __le32 dwTransferLength;        /* Length of data to xfer */
        u8 bTransferSegment;
-} __attribute__((packed));
+} __packed;
 
 struct wa_xfer_ctl {
        struct wa_xfer_hdr hdr;
        u8 bmAttribute;
        __le16 wReserved;
        struct usb_ctrlrequest baSetupData;
-} __attribute__((packed));
+} __packed;
 
 struct wa_xfer_bi {
        struct wa_xfer_hdr hdr;
        u8 bReserved;
        __le16 wReserved;
-} __attribute__((packed));
+} __packed;
 
+/* [WUSB] section 8.5.5 */
 struct wa_xfer_hwaiso {
        struct wa_xfer_hdr hdr;
        u8 bReserved;
        __le16 wPresentationTime;
        __le32 dwNumOfPackets;
-       /* FIXME: u8 pktdata[]? */
-} __attribute__((packed));
+} __packed;
+
+struct wa_xfer_packet_info_hwaiso {
+       __le16 wLength;
+       u8 bPacketType;
+       u8 bReserved;
+       __le16 PacketLength[0];
+} __packed;
+
+struct wa_xfer_packet_status_len_hwaiso {
+       __le16 PacketLength;
+       __le16 PacketStatus;
+} __packed;
+
+struct wa_xfer_packet_status_hwaiso {
+       __le16 wLength;
+       u8 bPacketType;
+       u8 bReserved;
+       struct wa_xfer_packet_status_len_hwaiso PacketStatus[0];
+} __packed;
 
 /* [WUSB] section 8.3.3.5 */
 struct wa_xfer_abort {
@@ -206,7 +227,7 @@ struct wa_xfer_abort {
        u8 bRequestType;
        __le16 wRPipe;                  /* RPipe index */
        __le32 dwTransferID;            /* Host-assigned ID */
-} __attribute__((packed));
+} __packed;
 
 /**
  * WA Transfer Complete notification ([WUSB] section 8.3.3.3)
@@ -216,7 +237,7 @@ struct wa_notif_xfer {
        struct wa_notif_hdr hdr;
        u8 bEndpoint;
        u8 Reserved;
-} __attribute__((packed));
+} __packed;
 
 /** Transfer result basic codes [WUSB] table 8-15 */
 enum {
@@ -243,7 +264,7 @@ struct wa_xfer_result {
        u8     bTransferSegment;
        u8     bTransferStatus;
        __le32 dwNumOfPackets;
-} __attribute__((packed));
+} __packed;
 
 /**
  * Wire Adapter Class Descriptor ([WUSB] section 8.5.2.7).
@@ -258,16 +279,16 @@ struct wa_xfer_result {
 struct usb_wa_descriptor {
        u8      bLength;
        u8      bDescriptorType;
-       u16     bcdWAVersion;
+       __le16  bcdWAVersion;
        u8      bNumPorts;              /* don't use!! */
        u8      bmAttributes;           /* Reserved == 0 */
-       u16     wNumRPipes;
-       u16     wRPipeMaxBlock;
+       __le16  wNumRPipes;
+       __le16  wRPipeMaxBlock;
        u8      bRPipeBlockSize;
        u8      bPwrOn2PwrGood;
        u8      bNumMMCIEs;
        u8      DeviceRemovable;        /* FIXME: in DWA this is up to 16 bytes */
-} __attribute__((packed));
+} __packed;
 
 /**
  * HWA Device Information Buffer (WUSB1.0[T8.54])
@@ -277,6 +298,6 @@ struct hwa_dev_info {
        u8      bDeviceAddress;
        __le16  wPHYRates;
        u8      bmDeviceAttribute;
-} __attribute__((packed));
+} __packed;
 
 #endif /* #ifndef __LINUX_USB_WUSB_WA_H */
diff --git a/include/video/exynos_dp.h b/include/video/exynos_dp.h
deleted file mode 100644 (file)
index bd8cabd..0000000
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Samsung SoC DP device support
- *
- * Copyright (C) 2012 Samsung Electronics Co., Ltd.
- * Author: Jingoo Han <jg1.han@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 _EXYNOS_DP_H
-#define _EXYNOS_DP_H
-
-#define DP_TIMEOUT_LOOP_COUNT 100
-#define MAX_CR_LOOP 5
-#define MAX_EQ_LOOP 5
-
-enum link_rate_type {
-       LINK_RATE_1_62GBPS = 0x06,
-       LINK_RATE_2_70GBPS = 0x0a
-};
-
-enum link_lane_count_type {
-       LANE_COUNT1 = 1,
-       LANE_COUNT2 = 2,
-       LANE_COUNT4 = 4
-};
-
-enum link_training_state {
-       START,
-       CLOCK_RECOVERY,
-       EQUALIZER_TRAINING,
-       FINISHED,
-       FAILED
-};
-
-enum voltage_swing_level {
-       VOLTAGE_LEVEL_0,
-       VOLTAGE_LEVEL_1,
-       VOLTAGE_LEVEL_2,
-       VOLTAGE_LEVEL_3,
-};
-
-enum pre_emphasis_level {
-       PRE_EMPHASIS_LEVEL_0,
-       PRE_EMPHASIS_LEVEL_1,
-       PRE_EMPHASIS_LEVEL_2,
-       PRE_EMPHASIS_LEVEL_3,
-};
-
-enum pattern_set {
-       PRBS7,
-       D10_2,
-       TRAINING_PTN1,
-       TRAINING_PTN2,
-       DP_NONE
-};
-
-enum color_space {
-       COLOR_RGB,
-       COLOR_YCBCR422,
-       COLOR_YCBCR444
-};
-
-enum color_depth {
-       COLOR_6,
-       COLOR_8,
-       COLOR_10,
-       COLOR_12
-};
-
-enum color_coefficient {
-       COLOR_YCBCR601,
-       COLOR_YCBCR709
-};
-
-enum dynamic_range {
-       VESA,
-       CEA
-};
-
-enum pll_status {
-       PLL_UNLOCKED,
-       PLL_LOCKED
-};
-
-enum clock_recovery_m_value_type {
-       CALCULATED_M,
-       REGISTER_M
-};
-
-enum video_timing_recognition_type {
-       VIDEO_TIMING_FROM_CAPTURE,
-       VIDEO_TIMING_FROM_REGISTER
-};
-
-enum analog_power_block {
-       AUX_BLOCK,
-       CH0_BLOCK,
-       CH1_BLOCK,
-       CH2_BLOCK,
-       CH3_BLOCK,
-       ANALOG_TOTAL,
-       POWER_ALL
-};
-
-struct video_info {
-       char *name;
-
-       bool h_sync_polarity;
-       bool v_sync_polarity;
-       bool interlaced;
-
-       enum color_space color_space;
-       enum dynamic_range dynamic_range;
-       enum color_coefficient ycbcr_coeff;
-       enum color_depth color_depth;
-
-       enum link_rate_type link_rate;
-       enum link_lane_count_type lane_count;
-};
-
-struct exynos_dp_platdata {
-       struct video_info *video_info;
-
-       void (*phy_init)(void);
-       void (*phy_exit)(void);
-};
-
-#endif /* _EXYNOS_DP_H */
index 89dc88a171af812f1ee01f6b5b64af4e61fc7ca1..6a578f8a1b3e2126c3940d6f4dc2b93db54837bc 100644 (file)
@@ -216,6 +216,7 @@ struct mipi_dsim_config {
  *     automatically.
  * @e_clk_src: select byte clock source.
  * @pd: pointer to MIPI-DSI driver platform data.
+ * @phy: pointer to the MIPI-DSI PHY
  */
 struct mipi_dsim_device {
        struct device                   *dev;
@@ -236,6 +237,7 @@ struct mipi_dsim_device {
        bool                            suspended;
 
        struct mipi_dsim_platform_data  *pd;
+       struct phy                      *phy;
 };
 
 /*
@@ -248,7 +250,6 @@ struct mipi_dsim_device {
  * @enabled: indicate whether mipi controller got enabled or not.
  * @lcd_panel_info: pointer for lcd panel specific structure.
  *     this structure specifies width, height, timing and polarity and so on.
- * @phy_enable: pointer to a callback controlling D-PHY enable/reset
  */
 struct mipi_dsim_platform_data {
        char                            lcd_panel_name[PANEL_NAME_SIZE];
@@ -256,8 +257,6 @@ struct mipi_dsim_platform_data {
        struct mipi_dsim_config         *dsim_config;
        unsigned int                    enabled;
        void                            *lcd_panel_info;
-
-       int (*phy_enable)(struct platform_device *pdev, bool on);
 };
 
 /*