Merge tag 'usb-serial-5.19-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 20 May 2022 19:14:44 +0000 (21:14 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 20 May 2022 19:14:44 +0000 (21:14 +0200)
Johan writes:

USB-serial updates for 5.19-rc1

Here are the USB-serial updates for 5.19-rc1, including:

 - a workaround for pl2303 devices with unexpected bcdUSB
 - a new modem device id

Included is also a printk clean up.

All but the modem-id commit have been in linux-next with no reported
issues.

* tag 'usb-serial-5.19-rc1' of https://git.kernel.org/pub/scm/linux/kernel/git/johan/usb-serial:
  USB: serial: option: add Quectel BG95 modem
  USB: serial: pl2303: fix type detection for odd device
  USB: serial: ftdi_sio: clean up printk format specifier

189 files changed:
Documentation/ABI/testing/configfs-usb-gadget-uvc
Documentation/ABI/testing/sysfs-bus-thunderbolt
Documentation/devicetree/bindings/usb/am33xx-usb.txt
Documentation/devicetree/bindings/usb/da8xx-usb.txt
Documentation/devicetree/bindings/usb/dwc2.yaml
Documentation/devicetree/bindings/usb/dwc3-xilinx.yaml
Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/generic-ehci.yaml
Documentation/devicetree/bindings/usb/generic-ohci.yaml
Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml
Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
Documentation/devicetree/bindings/usb/renesas,usbhs.yaml
Documentation/devicetree/bindings/usb/samsung,exynos-usb2.yaml
Documentation/devicetree/bindings/usb/ti,am62-usb.yaml [new file with mode: 0644]
Documentation/usb/gadget-testing.rst
arch/arm/boot/dts/qcom-ipq4019.dtsi
arch/arm/boot/dts/qcom-sdx55.dtsi
arch/arm64/boot/dts/qcom/apq8096-db820c.dts
arch/arm64/boot/dts/qcom/ipq6018.dtsi
arch/arm64/boot/dts/qcom/ipq8074.dtsi
arch/arm64/boot/dts/qcom/msm8953.dtsi
arch/arm64/boot/dts/qcom/msm8994.dtsi
arch/arm64/boot/dts/qcom/msm8996-xiaomi-common.dtsi
arch/arm64/boot/dts/qcom/msm8996.dtsi
arch/arm64/boot/dts/qcom/msm8998.dtsi
arch/arm64/boot/dts/qcom/qcs404-evb.dtsi
arch/arm64/boot/dts/qcom/qcs404.dtsi
arch/arm64/boot/dts/qcom/sc7180.dtsi
arch/arm64/boot/dts/qcom/sc7280-idp.dts
arch/arm64/boot/dts/qcom/sc7280.dtsi
arch/arm64/boot/dts/qcom/sdm630.dtsi
arch/arm64/boot/dts/qcom/sdm845.dtsi
arch/arm64/boot/dts/qcom/sm6125.dtsi
arch/arm64/boot/dts/qcom/sm6350.dtsi
arch/arm64/boot/dts/qcom/sm8150.dtsi
arch/arm64/boot/dts/qcom/sm8250.dtsi
arch/arm64/boot/dts/qcom/sm8350.dtsi
arch/arm64/boot/dts/qcom/sm8450.dtsi
arch/mips/boot/dts/ingenic/jz4780.dtsi
arch/mips/boot/dts/ingenic/x1000.dtsi
arch/mips/boot/dts/ingenic/x1830.dtsi
drivers/base/property.c
drivers/hid/usbhid/hid-core.c
drivers/hid/usbhid/usbkbd.c
drivers/hid/usbhid/usbmouse.c
drivers/input/misc/ati_remote2.c
drivers/input/misc/cm109.c
drivers/input/misc/powermate.c
drivers/input/misc/yealink.c
drivers/input/tablet/acecad.c
drivers/input/tablet/pegasus_notetaker.c
drivers/media/rc/ati_remote.c
drivers/media/rc/mceusb.c
drivers/media/rc/streamzap.c
drivers/media/rc/xbox_remote.c
drivers/media/usb/tm6000/tm6000-dvb.c
drivers/media/usb/tm6000/tm6000-input.c
drivers/media/usb/tm6000/tm6000-video.c
drivers/misc/mei/hdcp/mei_hdcp.c
drivers/misc/mei/pxp/mei_pxp.c
drivers/net/usb/cdc_ncm.c
drivers/net/usb/lan78xx.c
drivers/net/usb/rndis_host.c
drivers/net/usb/usbnet.c
drivers/net/wireless/mediatek/mt76/usb.c
drivers/net/wireless/ralink/rt2x00/rt2x00usb.c
drivers/thunderbolt/ctl.c
drivers/thunderbolt/nhi.c
drivers/thunderbolt/path.c
drivers/thunderbolt/switch.c
drivers/thunderbolt/tb.c
drivers/thunderbolt/tb.h
drivers/thunderbolt/tb_msgs.h
drivers/thunderbolt/tb_regs.h
drivers/thunderbolt/test.c
drivers/thunderbolt/tunnel.c
drivers/thunderbolt/tunnel.h
drivers/thunderbolt/usb4_port.c
drivers/thunderbolt/xdomain.c
drivers/usb/atm/usbatm.c
drivers/usb/c67x00/c67x00-drv.c
drivers/usb/c67x00/c67x00-sched.c
drivers/usb/cdns3/cdns3-gadget.c
drivers/usb/cdns3/cdns3-gadget.h
drivers/usb/core/devices.c
drivers/usb/core/driver.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hcd.c
drivers/usb/core/hub.c
drivers/usb/core/usb-acpi.c
drivers/usb/dwc2/core.c
drivers/usb/dwc2/core.h
drivers/usb/dwc2/gadget.c
drivers/usb/dwc2/params.c
drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/Makefile
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/drd.c
drivers/usb/dwc3/dwc3-am62.c [new file with mode: 0644]
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/dwc3-xilinx.c
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h
drivers/usb/dwc3/host.c
drivers/usb/gadget/composite.c
drivers/usb/gadget/configfs.c
drivers/usb/gadget/function/f_acm.c
drivers/usb/gadget/function/f_uvc.c
drivers/usb/gadget/function/u_audio.c
drivers/usb/gadget/function/u_uvc.h
drivers/usb/gadget/function/uvc.h
drivers/usb/gadget/function/uvc_configfs.c
drivers/usb/gadget/function/uvc_configfs.h
drivers/usb/gadget/function/uvc_queue.c
drivers/usb/gadget/function/uvc_queue.h
drivers/usb/gadget/function/uvc_video.c
drivers/usb/gadget/legacy/dbgp.c
drivers/usb/gadget/legacy/inode.c
drivers/usb/gadget/legacy/raw_gadget.c
drivers/usb/gadget/udc/core.c
drivers/usb/gadget/udc/net2272.c
drivers/usb/gadget/udc/net2280.c
drivers/usb/gadget/udc/omap_udc.c
drivers/usb/gadget/udc/pxa27x_udc.h
drivers/usb/gadget/udc/s3c-hsudc.c
drivers/usb/gadget/udc/tegra-xudc.c
drivers/usb/gadget/udc/udc-xilinx.c
drivers/usb/host/ehci-omap.c
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci-q.c
drivers/usb/host/ehci-xilinx-of.c
drivers/usb/host/fhci-hcd.c
drivers/usb/host/fotg210-hcd.c
drivers/usb/host/isp116x-hcd.c
drivers/usb/host/isp1362-hcd.c
drivers/usb/host/max3421-hcd.c
drivers/usb/host/ohci-hcd.c
drivers/usb/host/ohci-omap.c
drivers/usb/host/ohci-platform.c
drivers/usb/host/ohci-ppc-of.c
drivers/usb/host/oxu210hp-hcd.c
drivers/usb/host/r8a66597-hcd.c
drivers/usb/host/sl811-hcd.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/isp1760/isp1760-core.c
drivers/usb/isp1760/isp1760-hcd.c
drivers/usb/misc/ftdi-elan.c
drivers/usb/misc/lvstest.c
drivers/usb/musb/mediatek.c
drivers/usb/musb/omap2430.c
drivers/usb/storage/alauda.c
drivers/usb/storage/isd200.c
drivers/usb/storage/karma.c
drivers/usb/storage/onetouch.c
drivers/usb/storage/shuttle_usbat.c
drivers/usb/storage/transport.c
drivers/usb/typec/bus.c
drivers/usb/typec/mux.c
drivers/usb/typec/mux.h
drivers/usb/typec/mux/Kconfig
drivers/usb/typec/mux/Makefile
drivers/usb/typec/mux/fsa4480.c [new file with mode: 0644]
drivers/usb/typec/mux/intel_pmc_mux.c
drivers/usb/typec/mux/pi3usb30532.c
drivers/usb/typec/tipd/core.c
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h
drivers/usb/usbip/stub_dev.c
drivers/usb/usbip/stub_rx.c
include/linux/property.h
include/linux/thunderbolt.h
include/linux/usb.h
include/linux/usb/gadget.h
include/linux/usb/hcd.h
include/linux/usb/typec_mux.h
sound/usb/line6/pcm.c
sound/usb/midi.c
sound/usb/usx2y/usb_stream.c
sound/usb/usx2y/usbusx2yaudio.c
sound/usb/usx2y/usx2yhwdeppcm.c
tools/usb/testusb.c

index 889ed45..611b23e 100644 (file)
@@ -7,6 +7,7 @@ Description:    UVC function directory
                streaming_maxburst      0..15 (ss only)
                streaming_maxpacket     1..1023 (fs), 1..3072 (hs/ss)
                streaming_interval      1..16
+               function_name           string [32]
                ===================     =============================
 
 What:          /config/usb-gadget/gadget/functions/uvc.name/control
index b7e87f6..f7570c2 100644 (file)
@@ -293,6 +293,16 @@ Contact:   thunderbolt-software@lists.01.org
 Description:   This contains XDomain service specific settings as
                bitmask. Format: %x
 
+What:          /sys/bus/thunderbolt/devices/usb4_portX/connector
+Date:          April 2022
+Contact:       Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Description:
+               Symlink to the USB Type-C connector. This link is only
+               created when USB Type-C Connector Class is enabled,
+               and only if the system firmware is capable of
+               describing the connection between a port and its
+               connector.
+
 What:          /sys/bus/thunderbolt/devices/usb4_portX/link
 Date:          Sep 2021
 KernelVersion: v5.14
index 7a198a3..654ffc6 100644 (file)
@@ -61,8 +61,9 @@ DMA
   endpoint number (0 â€¦ 14 for endpoints 1 â€¦ 15 on instance 0 and 15 â€¦ 29
   for endpoints 1 â€¦ 15 on instance 1). The second number is 0 for RX and
   1 for TX transfers.
-- #dma-channels: should be set to 30 representing the 15 endpoints for
+- dma-channels: should be set to 30 representing the 15 endpoints for
   each USB instance.
+- #dma-channels: deprecated
 
 Example:
 ~~~~~~~~
@@ -193,7 +194,7 @@ usb: usb@47400000 {
                interrupts = <17>;
                interrupt-names = "glue";
                #dma-cells = <2>;
-               #dma-channels = <30>;
-               #dma-requests = <256>;
+               dma-channels = <30>;
+               dma-requests = <256>;
        };
 };
index 9ce2255..fb2027a 100644 (file)
@@ -36,7 +36,8 @@ DMA
 - #dma-cells: should be set to 2. The first number represents the
   channel number (0 â€¦ 3 for endpoints 1 â€¦ 4).
   The second number is 0 for RX and 1 for TX transfers.
-- #dma-channels: should be set to 4 representing the 4 endpoints.
+- dma-channels: should be set to 4 representing the 4 endpoints.
+- #dma-channels: deprecated
 
 Example:
        usb_phy: usb-phy {
@@ -74,7 +75,7 @@ Example:
                        reg-names = "controller", "scheduler", "queuemgr";
                        interrupts = <58>;
                        #dma-cells = <2>;
-                       #dma-channels = <4>;
+                       dma-channels = <4>;
                };
 
        };
index 4cebce6..c6e8c0b 100644 (file)
@@ -17,6 +17,13 @@ properties:
     oneOf:
       - const: brcm,bcm2835-usb
       - const: hisilicon,hi6220-usb
+      - const: ingenic,jz4775-otg
+      - const: ingenic,jz4780-otg
+      - const: ingenic,x1000-otg
+      - const: ingenic,x1600-otg
+      - const: ingenic,x1700-otg
+      - const: ingenic,x1830-otg
+      - const: ingenic,x2000-otg
       - items:
           - const: rockchip,rk3066-usb
           - const: snps,dwc2
index f77c16e..098b731 100644 (file)
@@ -71,6 +71,10 @@ properties:
         - usb2-phy
         - usb3-phy
 
+  reset-gpios:
+    description: GPIO used for the reset ulpi-phy
+    maxItems: 1
+
 # Required child node:
 
 patternProperties:
diff --git a/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml b/Documentation/devicetree/bindings/usb/fcs,fsa4480.yaml
new file mode 100644 (file)
index 0000000..9473f26
--- /dev/null
@@ -0,0 +1,72 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/usb/fcs,fsa4480.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: ON Semiconductor Analog Audio Switch
+
+maintainers:
+  - Bjorn Andersson <bjorn.andersson@linaro.org>
+
+properties:
+  compatible:
+    enum:
+      - fcs,fsa4480
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  vcc-supply:
+    description: power supply (2.7V-5.5V)
+
+  mode-switch:
+    description: Flag the port as possible handle of altmode switching
+    type: boolean
+
+  orientation-switch:
+    description: Flag the port as possible handler of orientation switching
+    type: boolean
+
+  port:
+    $ref: /schemas/graph.yaml#/properties/port
+    description:
+      A port node to link the FSA4480 to a TypeC controller for the purpose of
+      handling altmode muxing and orientation switching.
+
+required:
+  - compatible
+  - reg
+  - port
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c13 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        fsa4480@42 {
+          compatible = "fcs,fsa4480";
+          reg = <0x42>;
+
+          interrupts-extended = <&tlmm 2 IRQ_TYPE_LEVEL_LOW>;
+
+          vcc-supply = <&vreg_bob>;
+
+          mode-switch;
+          orientation-switch;
+
+          port {
+            fsa4480_ept: endpoint {
+              remote-endpoint = <&typec_controller>;
+            };
+          };
+        };
+    };
+...
index 8913497..0b4524b 100644 (file)
@@ -55,6 +55,7 @@ properties:
               - brcm,bcm7420-ehci
               - brcm,bcm7425-ehci
               - brcm,bcm7435-ehci
+              - hpe,gxp-ehci
               - ibm,476gtr-ehci
               - nxp,lpc1850-ehci
               - qca,ar7100-ehci
index acbf94f..e2ac846 100644 (file)
@@ -42,6 +42,7 @@ properties:
               - brcm,bcm7420-ohci
               - brcm,bcm7425-ohci
               - brcm,bcm7435-ohci
+              - hpe,gxp-ohci
               - ibm,476gtr-ohci
               - ingenic,jz4740-ohci
               - snps,hsdk-v1.0-ohci
index df766f8..37b02a8 100644 (file)
@@ -25,6 +25,7 @@ properties:
           - mediatek,mt8173-mtu3
           - mediatek,mt8183-mtu3
           - mediatek,mt8192-mtu3
+          - mediatek,mt8195-mtu3
       - const: mediatek,mtu3
 
   reg:
index ce252db..e336fe2 100644 (file)
@@ -16,16 +16,21 @@ properties:
           - qcom,ipq4019-dwc3
           - qcom,ipq6018-dwc3
           - qcom,ipq8064-dwc3
+          - qcom,ipq8074-dwc3
           - qcom,msm8953-dwc3
+          - qcom,msm8994-dwc3
           - qcom,msm8996-dwc3
           - qcom,msm8998-dwc3
+          - qcom,qcs404-dwc3
           - qcom,sc7180-dwc3
           - qcom,sc7280-dwc3
           - qcom,sdm660-dwc3
           - qcom,sdm845-dwc3
           - qcom,sdx55-dwc3
+          - qcom,sdx65-dwc3
           - qcom,sm4250-dwc3
           - qcom,sm6115-dwc3
+          - qcom,sm6125-dwc3
           - qcom,sm6350-dwc3
           - qcom,sm8150-dwc3
           - qcom,sm8250-dwc3
@@ -50,26 +55,22 @@ properties:
     maxItems: 1
 
   clocks:
-    description:
-      A list of phandle and clock-specifier pairs for the clocks
-      listed in clock-names.
-    items:
-      - description: System Config NOC clock.
-      - description: Master/Core clock, has to be >= 125 MHz
-          for SS operation and >= 60MHz for HS operation.
-      - description: System bus AXI clock.
-      - description: Mock utmi clock needed for ITP/SOF generation
-          in host mode. Its frequency should be 19.2MHz.
-      - description: Sleep clock, used for wakeup when
-          USB3 core goes into low power mode (U3).
+    description: |
+      Several clocks are used, depending on the variant. Typical ones are::
+       - cfg_noc:: System Config NOC clock.
+       - core:: Master/Core clock, has to be >= 125 MHz for SS operation and >=
+                60MHz for HS operation.
+       - iface:: System bus AXI clock.
+       - sleep:: Sleep clock, used for wakeup when USB3 core goes into low
+                 power mode (U3).
+       - mock_utmi:: Mock utmi clock needed for ITP/SOF generation in host
+                     mode. Its frequency should be 19.2MHz.
+    minItems: 1
+    maxItems: 6
 
   clock-names:
-    items:
-      - const: cfg_noc
-      - const: core
-      - const: iface
-      - const: mock_utmi
-      - const: sleep
+    minItems: 1
+    maxItems: 6
 
   assigned-clocks:
     items:
@@ -132,6 +133,185 @@ required:
   - interrupts
   - interrupt-names
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,ipq4019-dwc3
+    then:
+      properties:
+        clocks:
+          maxItems: 3
+        clock-names:
+          items:
+            - const: core
+            - const: sleep
+            - const: mock_utmi
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,ipq8064-dwc3
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Master/Core clock, has to be >= 125 MHz
+                for SS operation and >= 60MHz for HS operation.
+        clock-names:
+          items:
+            - const: core
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,msm8953-dwc3
+              - qcom,msm8996-dwc3
+              - qcom,msm8998-dwc3
+              - qcom,sc7180-dwc3
+              - qcom,sc7280-dwc3
+              - qcom,sdm845-dwc3
+              - qcom,sdx55-dwc3
+              - qcom,sm6350-dwc3
+    then:
+      properties:
+        clocks:
+          maxItems: 5
+        clock-names:
+          items:
+            - const: cfg_noc
+            - const: core
+            - const: iface
+            - const: sleep
+            - const: mock_utmi
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,ipq6018-dwc3
+    then:
+      properties:
+        clocks:
+          minItems: 3
+          maxItems: 4
+        clock-names:
+          oneOf:
+            - items:
+                - const: core
+                - const: sleep
+                - const: mock_utmi
+            - items:
+                - const: cfg_noc
+                - const: core
+                - const: sleep
+                - const: mock_utmi
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,ipq8074-dwc3
+    then:
+      properties:
+        clocks:
+          maxItems: 4
+        clock-names:
+          items:
+            - const: cfg_noc
+            - const: core
+            - const: sleep
+            - const: mock_utmi
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,msm8994-dwc3
+              - qcom,qcs404-dwc3
+    then:
+      properties:
+        clocks:
+          maxItems: 4
+        clock-names:
+          items:
+            - const: core
+            - const: iface
+            - const: sleep
+            - const: mock_utmi
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sdm660-dwc3
+    then:
+      properties:
+        clocks:
+          minItems: 6
+        clock-names:
+          items:
+            - const: cfg_noc
+            - const: core
+            - const: iface
+            - const: sleep
+            - const: mock_utmi
+            - const: bus
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sm6125-dwc3
+              - qcom,sm8150-dwc3
+              - qcom,sm8250-dwc3
+              - qcom,sm8450-dwc3
+    then:
+      properties:
+        clocks:
+          minItems: 6
+        clock-names:
+          items:
+            - const: cfg_noc
+            - const: core
+            - const: iface
+            - const: sleep
+            - const: mock_utmi
+            - const: xo
+
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sm8350-dwc3
+    then:
+      properties:
+        clocks:
+          minItems: 5
+          maxItems: 6
+        clock-names:
+          minItems: 5
+          items:
+            - const: cfg_noc
+            - const: core
+            - const: iface
+            - const: sleep
+            - const: mock_utmi
+            - const: xo
+
+
 additionalProperties: false
 
 examples:
@@ -153,10 +333,13 @@ examples:
             clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                      <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                      <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                     <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
-                     <&gcc GCC_USB30_PRIM_SLEEP_CLK>;
-            clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                      "sleep";
+                     <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                     <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+            clock-names = "cfg_noc",
+                          "core",
+                          "iface",
+                          "sleep",
+                          "mock_utmi";
 
             assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                           <&gcc GCC_USB30_PRIM_MASTER_CLK>;
index 0bb841b..bad55df 100644 (file)
@@ -19,6 +19,7 @@ properties:
       - items:
           - enum:
               - renesas,usbhs-r7s9210   # RZ/A2
+              - renesas,usbhs-r9a07g043 # RZ/G2UL
               - renesas,usbhs-r9a07g044 # RZ/G2{L,LC}
               - renesas,usbhs-r9a07g054 # RZ/V2L
           - const: renesas,rza2-usbhs
@@ -118,6 +119,7 @@ allOf:
         compatible:
           contains:
             enum:
+              - renesas,usbhs-r9a07g043
               - renesas,usbhs-r9a07g044
               - renesas,usbhs-r9a07g054
     then:
@@ -128,6 +130,8 @@ allOf:
             - description: U2P_INT_DMA[0]
             - description: U2P_INT_DMA[1]
             - description: U2P_INT_DMAERR
+      required:
+        - resets
     else:
       properties:
         interrupts:
index 9c92def..caa572d 100644 (file)
@@ -15,9 +15,6 @@ properties:
       - samsung,exynos4210-ehci
       - samsung,exynos4210-ohci
 
-  '#address-cells':
-    const: 1
-
   clocks:
     maxItems: 1
 
@@ -46,15 +43,6 @@ properties:
       Only for controller in EHCI mode, if present, specifies the GPIO that
       needs to be pulled up for the bus to be powered.
 
-  '#size-cells':
-    const: 0
-
-patternProperties:
-  "^.*@[0-9a-f]{1,2}$":
-    description: The hard wired USB devices
-    type: object
-    $ref: /usb/usb-device.yaml
-
 required:
   - compatible
   - clocks
@@ -65,6 +53,7 @@ required:
   - reg
 
 allOf:
+  - $ref: usb-hcd.yaml#
   - if:
       properties:
         compatible:
@@ -74,7 +63,7 @@ allOf:
       properties:
         samsung,vbus-gpio: false
 
-additionalProperties: false
+unevaluatedProperties: false
 
 examples:
   - |
diff --git a/Documentation/devicetree/bindings/usb/ti,am62-usb.yaml b/Documentation/devicetree/bindings/usb/ti,am62-usb.yaml
new file mode 100644 (file)
index 0000000..d25fc70
--- /dev/null
@@ -0,0 +1,103 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/ti,am62-usb.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI's AM62 wrapper module for the Synopsys USBSS-DRD controller
+
+maintainers:
+  - Aswath Govindraju <a-govindraju@ti.com>
+
+properties:
+  compatible:
+    const: ti,am62-usb
+
+  reg:
+    maxItems: 1
+
+  ranges: true
+
+  power-domains:
+    description:
+      PM domain provider node and an args specifier containing
+      the USB ISO device id value. See,
+      Documentation/devicetree/bindings/soc/ti/sci-pm-domain.yaml
+    maxItems: 1
+
+  clocks:
+    description: Clock phandle to usb2_refclk
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: ref
+
+  ti,vbus-divider:
+    description:
+      Should be present if USB VBUS line is connected to the
+      VBUS pin of the SoC via a 1/3 voltage divider.
+    type: boolean
+
+  ti,syscon-phy-pll-refclk:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    items:
+      - items:
+          - description: Phandle to the SYSCON entry
+          - description: USB phy control register offset within SYSCON
+    description:
+      Specifier for conveying frequency of ref clock input, for the
+      operation of USB2PHY.
+
+  '#address-cells':
+    const: 2
+
+  '#size-cells':
+    const: 2
+
+patternProperties:
+  "^usb@[0-9a-f]+$":
+    $ref: snps,dwc3.yaml#
+    description: Required child node
+
+required:
+  - compatible
+  - reg
+  - power-domains
+  - clocks
+  - clock-names
+  - ti,syscon-phy-pll-refclk
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/soc/ti,sci_pm_domain.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/gpio/gpio.h>
+
+    bus {
+      #address-cells = <2>;
+      #size-cells = <2>;
+
+      usbss1: usb@f910000 {
+        compatible = "ti,am62-usb";
+        reg = <0x00 0x0f910000 0x00 0x800>;
+        clocks = <&k3_clks 162 3>;
+        clock-names = "ref";
+        ti,syscon-phy-pll-refclk = <&wkup_conf 0x4018>;
+        power-domains = <&k3_pds 179 TI_SCI_PD_EXCLUSIVE>;
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        usb@31100000 {
+          compatible = "snps,dwc3";
+          reg =<0x00 0x31100000 0x00 0x50000>;
+          interrupts = <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>, /* irq.0 */
+                       <GIC_SPI 226 IRQ_TYPE_LEVEL_HIGH>; /* irq.0 */
+          interrupt-names = "host", "peripheral";
+          maximum-speed = "high-speed";
+          dr_mode = "otg";
+        };
+      };
+    };
index c6d034a..1c37159 100644 (file)
@@ -787,6 +787,7 @@ The uvc function provides these attributes in its function directory:
        streaming_maxpacket maximum packet size this endpoint is capable of
                            sending or receiving when this configuration is
                            selected
+       function_name       name of the interface
        =================== ================================================
 
 There are also "control" and "streaming" subdirectories, each of which contain
index a9d0566..c0cd2d3 100644 (file)
                        clocks = <&gcc GCC_USB3_MASTER_CLK>,
                                 <&gcc GCC_USB3_SLEEP_CLK>,
                                 <&gcc GCC_USB3_MOCK_UTMI_CLK>;
-                       clock-names = "master", "sleep", "mock_utmi";
+                       clock-names = "core", "sleep", "mock_utmi";
                        ranges;
                        status = "disabled";
 
index d455795..913dfc7 100644 (file)
                        clocks = <&gcc GCC_USB30_SLV_AHB_CLK>,
                                 <&gcc GCC_USB30_MASTER_CLK>,
                                 <&gcc GCC_USB30_MSTR_AXI_CLK>,
-                                <&gcc GCC_USB30_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_SLEEP_CLK>,
+                                <&gcc GCC_USB30_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_MASTER_CLK>;
index f623db8..7e9af10 100644 (file)
 &usb2 {
        status = "okay";
        extcon = <&usb2_id>;
+};
 
-       dwc3@7600000 {
-               extcon = <&usb2_id>;
-               dr_mode = "otg";
-               maximum-speed = "high-speed";
-       };
+&usb2_dwc3 {
+       extcon = <&usb2_id>;
+       dr_mode = "otg";
+       maximum-speed = "high-speed";
 };
 
 &usb3 {
        status = "okay";
        extcon = <&usb3_id>;
+};
 
-       dwc3@6a00000 {
-               extcon = <&usb3_id>;
-               dr_mode = "otg";
-       };
+&usb3_dwc3 {
+       extcon = <&usb3_id>;
+       dr_mode = "otg";
 };
 
 &usb3phy {
index aac5657..b3f8637 100644 (file)
                        status = "disabled";
                };
 
-               usb2: usb2@7000000 {
+               usb2: usb@70f8800 {
                        compatible = "qcom,ipq6018-dwc3", "qcom,dwc3";
                        reg = <0x0 0x070F8800 0x0 0x400>;
                        #address-cells = <2>;
                        clocks = <&gcc GCC_USB1_MASTER_CLK>,
                                 <&gcc GCC_USB1_SLEEP_CLK>,
                                 <&gcc GCC_USB1_MOCK_UTMI_CLK>;
-                       clock-names = "master",
+                       clock-names = "core",
                                      "sleep",
                                      "mock_utmi";
 
                        status = "disabled";
                };
 
-               usb3: usb3@8A00000 {
+               usb3: usb@8af8800 {
                        compatible = "qcom,ipq6018-dwc3", "qcom,dwc3";
                        reg = <0x0 0x8AF8800 0x0 0x400>;
                        #address-cells = <2>;
                                <&gcc GCC_USB0_MASTER_CLK>,
                                <&gcc GCC_USB0_SLEEP_CLK>,
                                <&gcc GCC_USB0_MOCK_UTMI_CLK>;
-                       clock-names = "sys_noc_axi",
-                               "master",
+                       clock-names = "cfg_noc",
+                               "core",
                                "sleep",
                                "mock_utmi";
 
                        resets = <&gcc GCC_USB0_BCR>;
                        status = "disabled";
 
-                       dwc_0: usb@8A00000 {
+                       dwc_0: usb@8a00000 {
                                compatible = "snps,dwc3";
                                reg = <0x0 0x8A00000 0x0 0xcd00>;
                                interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
index d80b1ce..1a41dd7 100644 (file)
                };
 
                usb_0: usb@8af8800 {
-                       compatible = "qcom,dwc3";
+                       compatible = "qcom,ipq8074-dwc3", "qcom,dwc3";
                        reg = <0x08af8800 0x400>;
                        #address-cells = <1>;
                        #size-cells = <1>;
                                <&gcc GCC_USB0_MASTER_CLK>,
                                <&gcc GCC_USB0_SLEEP_CLK>,
                                <&gcc GCC_USB0_MOCK_UTMI_CLK>;
-                       clock-names = "sys_noc_axi",
-                               "master",
+                       clock-names = "cfg_noc",
+                               "core",
                                "sleep",
                                "mock_utmi";
 
                        resets = <&gcc GCC_USB0_BCR>;
                        status = "disabled";
 
-                       dwc_0: dwc3@8a00000 {
+                       dwc_0: usb@8a00000 {
                                compatible = "snps,dwc3";
                                reg = <0x8a00000 0xcd00>;
                                interrupts = <GIC_SPI 140 IRQ_TYPE_LEVEL_HIGH>;
                };
 
                usb_1: usb@8cf8800 {
-                       compatible = "qcom,dwc3";
+                       compatible = "qcom,ipq8074-dwc3", "qcom,dwc3";
                        reg = <0x08cf8800 0x400>;
                        #address-cells = <1>;
                        #size-cells = <1>;
                                <&gcc GCC_USB1_MASTER_CLK>,
                                <&gcc GCC_USB1_SLEEP_CLK>,
                                <&gcc GCC_USB1_MOCK_UTMI_CLK>;
-                       clock-names = "sys_noc_axi",
-                               "master",
+                       clock-names = "cfg_noc",
+                               "core",
                                "sleep",
                                "mock_utmi";
 
                        resets = <&gcc GCC_USB1_BCR>;
                        status = "disabled";
 
-                       dwc_1: dwc3@8c00000 {
+                       dwc_1: usb@8c00000 {
                                compatible = "snps,dwc3";
                                reg = <0x8c00000 0xcd00>;
                                interrupts = <GIC_SPI 99 IRQ_TYPE_LEVEL_HIGH>;
index 431228f..e4e6477 100644 (file)
                        clocks = <&gcc GCC_USB_PHY_CFG_AHB_CLK>,
                                 <&gcc GCC_USB30_MASTER_CLK>,
                                 <&gcc GCC_PCNOC_USB3_AXI_CLK>,
-                                <&gcc GCC_USB30_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface",
-                                     "mock_utmi", "sleep";
+                                <&gcc GCC_USB30_SLEEP_CLK>,
+                                <&gcc GCC_USB30_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_MASTER_CLK>;
index 8c1dc51..6d5652d 100644 (file)
                };
 
                usb3: usb@f92f8800 {
-                       compatible = "qcom,msm8996-dwc3", "qcom,dwc3";
+                       compatible = "qcom,msm8994-dwc3", "qcom,dwc3";
                        reg = <0xf92f8800 0x400>;
                        #address-cells = <1>;
                        #size-cells = <1>;
                                 <&gcc GCC_SYS_NOC_USB3_AXI_CLK>,
                                 <&gcc GCC_USB30_SLEEP_CLK>,
                                 <&gcc GCC_USB30_MOCK_UTMI_CLK>;
-                       clock-names = "core", "iface", "sleep", "mock_utmi", "ref", "xo";
+                       clock-names = "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_MASTER_CLK>;
index 7a9fcbe..51f8826 100644 (file)
        extcon = <&typec>;
 
        qcom,select-utmi-as-pipe-clk;
+};
 
-       dwc3@6a00000 {
-               extcon = <&typec>;
+&usb3_dwc3 {
+       extcon = <&typec>;
 
-               /* usb3-phy is not used on this device */
-               phys = <&hsusb_phy1>;
-               phy-names = "usb2-phy";
+       /* usb3-phy is not used on this device */
+       phys = <&hsusb_phy1>;
+       phy-names = "usb2-phy";
 
-               maximum-speed = "high-speed";
-               snps,is-utmi-l1-suspend;
-               snps,usb2-gadget-lpm-disable;
-               snps,hird-threshold = /bits/ 8 <0>;
-       };
+       maximum-speed = "high-speed";
+       snps,is-utmi-l1-suspend;
+       snps,usb2-gadget-lpm-disable;
+       snps,hird-threshold = /bits/ 8 <0>;
 };
 
 &hsusb_phy1 {
index b9a48cf..aa5eec5 100644 (file)
                        interrupt-names = "hs_phy_irq", "ss_phy_irq";
 
                        clocks = <&gcc GCC_SYS_NOC_USB3_AXI_CLK>,
-                               <&gcc GCC_USB30_MASTER_CLK>,
-                               <&gcc GCC_AGGRE2_USB3_AXI_CLK>,
-                               <&gcc GCC_USB30_MOCK_UTMI_CLK>,
-                               <&gcc GCC_USB30_SLEEP_CLK>,
-                               <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
+                                <&gcc GCC_USB30_MASTER_CLK>,
+                                <&gcc GCC_AGGRE2_USB3_AXI_CLK>,
+                                <&gcc GCC_USB30_SLEEP_CLK>,
+                                <&gcc GCC_USB30_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_MASTER_CLK>;
                        power-domains = <&gcc USB30_GDSC>;
                        status = "disabled";
 
-                       usb3_dwc3: dwc3@6a00000 {
+                       usb3_dwc3: usb@6a00000 {
                                compatible = "snps,dwc3";
                                reg = <0x06a00000 0xcc00>;
                                interrupts = <0 131 IRQ_TYPE_LEVEL_HIGH>;
                                <&gcc GCC_USB20_MOCK_UTMI_CLK>,
                                <&gcc GCC_USB20_SLEEP_CLK>,
                                <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB20_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB20_MASTER_CLK>;
                        qcom,select-utmi-as-pipe-clk;
                        status = "disabled";
 
-                       dwc3@7600000 {
+                       usb2_dwc3: usb@7600000 {
                                compatible = "snps,dwc3";
                                reg = <0x07600000 0xcc00>;
                                interrupts = <0 138 IRQ_TYPE_LEVEL_HIGH>;
index 2fda21e..192713c 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_AXI_CLK>,
                                 <&gcc GCC_USB30_MASTER_CLK>,
                                 <&gcc GCC_AGGRE1_USB3_AXI_CLK>,
-                                <&gcc GCC_USB30_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_SLEEP_CLK>,
+                                <&gcc GCC_USB30_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_MASTER_CLK>;
 
                        resets = <&gcc GCC_USB_30_BCR>;
 
-                       usb3_dwc3: dwc3@a800000 {
+                       usb3_dwc3: usb@a800000 {
                                compatible = "snps,dwc3";
                                reg = <0x0a800000 0xcd00>;
                                interrupts = <GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>;
index a80c578..2f3104a 100644 (file)
 &usb3 {
        status = "okay";
 
-       dwc3@7580000 {
-               dr_mode = "host";
-       };
+};
+
+&usb3_dwc3 {
+       dr_mode = "host";
 };
 
 &usb2_phy_prim {
index 3f06f7c..4ed6269 100644 (file)
                };
 
                usb3: usb@7678800 {
-                       compatible = "qcom,dwc3";
+                       compatible = "qcom,qcs404-dwc3", "qcom,dwc3";
                        reg = <0x07678800 0x400>;
                        #address-cells = <1>;
                        #size-cells = <1>;
                        assigned-clock-rates = <19200000>, <200000000>;
                        status = "disabled";
 
-                       dwc3@7580000 {
+                       usb3_dwc3: usb@7580000 {
                                compatible = "snps,dwc3";
                                reg = <0x07580000 0xcd00>;
                                interrupts = <GIC_SPI 26 IRQ_TYPE_LEVEL_HIGH>;
                };
 
                usb2: usb@79b8800 {
-                       compatible = "qcom,dwc3";
+                       compatible = "qcom,qcs404-dwc3", "qcom,dwc3";
                        reg = <0x079b8800 0x400>;
                        #address-cells = <1>;
                        #size-cells = <1>;
                        assigned-clock-rates = <19200000>, <133333333>;
                        status = "disabled";
 
-                       dwc3@78c0000 {
+                       usb@78c0000 {
                                compatible = "snps,dwc3";
                                reg = <0x078c0000 0xcc00>;
                                interrupts = <GIC_SPI 44 IRQ_TYPE_LEVEL_HIGH>;
index e1c46b8..0cedca6 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
                                        <&gem_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_USB3 0>;
                        interconnect-names = "usb-ddr", "apps-usb";
 
-                       usb_1_dwc3: dwc3@a600000 {
+                       usb_1_dwc3: usb@a600000 {
                                compatible = "snps,dwc3";
                                reg = <0 0x0a600000 0 0xe000>;
                                interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
index a7be133..6d3ff80 100644 (file)
@@ -90,7 +90,7 @@
 };
 
 &usb_2_dwc3 {
-       dr_mode = "host";
+       dr_mode = "otg";
 };
 
 &usb_2_hsphy {
index f0b64be..5620926 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>,
                                 <&gcc GCC_USB30_SEC_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
-                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_SEC_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface","mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_SEC_SLEEP_CLK>,
+                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_SEC_MASTER_CLK>;
                                phys = <&usb_2_hsphy>;
                                phy-names = "usb2-phy";
                                maximum-speed = "high-speed";
+                               usb-role-switch;
+                               port {
+                                       usb2_role_switch: endpoint {
+                                               remote-endpoint = <&eud_ep>;
+                                       };
+                               };
                        };
                };
 
                        interrupts = <GIC_SPI 582 IRQ_TYPE_LEVEL_HIGH>;
                };
 
+               eud: eud@88e0000 {
+                       compatible = "qcom,sc7280-eud","qcom,eud";
+                       reg = <0 0x88e0000 0 0x2000>,
+                             <0 0x88e2000 0 0x1000>;
+                       interrupts-extended = <&pdc 11 IRQ_TYPE_LEVEL_HIGH>;
+                       ports {
+                               port@0 {
+                                       eud_ep: endpoint {
+                                               remote-endpoint = <&usb2_role_switch>;
+                                       };
+                               };
+                               port@1 {
+                                       eud_con: endpoint {
+                                               remote-endpoint = <&con_eud>;
+                                       };
+                               };
+                       };
+               };
+
+               eud_typec: connector {
+                       compatible = "usb-c-connector";
+                       ports {
+                               port@0 {
+                                       con_eud: endpoint {
+                                               remote-endpoint = <&eud_con>;
+                                       };
+                               };
+                       };
+               };
+
                nsp_noc: interconnect@a0c0000 {
                        reg = <0 0x0a0c0000 0 0x10000>;
                        compatible = "qcom,sc7280-nsp-noc";
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
                        assigned-clock-rates = <19200000>, <200000000>;
 
                        interrupts-extended = <&intc GIC_SPI 131 IRQ_TYPE_LEVEL_HIGH>,
-                                             <&pdc 14 IRQ_TYPE_EDGE_BOTH>,
+                                             <&pdc 17 IRQ_TYPE_EDGE_BOTH>,
                                              <&pdc 15 IRQ_TYPE_EDGE_BOTH>,
-                                             <&pdc 17 IRQ_TYPE_LEVEL_HIGH>;
-                       interrupt-names = "hs_phy_irq", "dp_hs_phy_irq",
-                                         "dm_hs_phy_irq", "ss_phy_irq";
+                                             <&pdc 14 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "hs_phy_irq",
+                                         "ss_phy_irq",
+                                         "dm_hs_phy_irq",
+                                         "dp_hs_phy_irq";
 
                        power-domains = <&gcc GCC_USB30_PRIM_GDSC>;
 
index 2402935..6841ef6 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_AXI_CLK>,
                                 <&gcc GCC_USB30_MASTER_CLK>,
                                 <&gcc GCC_AGGRE2_USB3_AXI_CLK>,
-                                <&rpmcc RPM_SMD_AGGR2_NOC_CLK>,
+                                <&gcc GCC_USB30_SLEEP_CLK>,
                                 <&gcc GCC_USB30_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "bus",
-                                     "mock_utmi", "sleep";
+                                <&rpmcc RPM_SMD_AGGR2_NOC_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "bus";
 
                        assigned-clocks = <&gcc GCC_USB30_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_MASTER_CLK>,
index b31bf62..5f318ea 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
                                        <&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_USB3_0 0>;
                        interconnect-names = "usb-ddr", "apps-usb";
 
-                       usb_1_dwc3: dwc3@a600000 {
+                       usb_1_dwc3: usb@a600000 {
                                compatible = "snps,dwc3";
                                reg = <0 0x0a600000 0 0xcd00>;
                                interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
                        clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>,
                                 <&gcc GCC_USB30_SEC_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
-                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_SEC_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_SEC_SLEEP_CLK>,
+                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_SEC_MASTER_CLK>;
                                        <&gladiator_noc MASTER_APPSS_PROC 0 &config_noc SLAVE_USB3_1 0>;
                        interconnect-names = "usb-ddr", "apps-usb";
 
-                       usb_2_dwc3: dwc3@a800000 {
+                       usb_2_dwc3: usb@a800000 {
                                compatible = "snps,dwc3";
                                reg = <0 0x0a800000 0 0xcd00>;
                                interrupts = <GIC_SPI 138 IRQ_TYPE_LEVEL_HIGH>;
index e81b2a7..135e6e0 100644 (file)
                };
 
                usb3: usb@4ef8800 {
-                       compatible = "qcom,msm8996-dwc3", "qcom,dwc3";
+                       compatible = "qcom,sm6125-dwc3", "qcom,dwc3";
                        reg = <0x04ef8800 0x400>;
                        #address-cells = <1>;
                        #size-cells = <1>;
                        ranges;
 
-                       clocks = <&gcc GCC_USB30_PRIM_MASTER_CLK>,
+                       clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
+                                <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_SYS_NOC_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB3_PRIM_CLKREF_CLK>,
                                 <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
+                                <&gcc GCC_USB3_PRIM_CLKREF_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "xo";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
index d7c9edf..bbd2b4c 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        interrupts-extended = <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
                                              <&pdc 17 IRQ_TYPE_LEVEL_HIGH>,
index 15f3bf2..1f49ddf 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB3_SEC_CLKREF_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep", "xo";
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "xo";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
 
                        resets = <&gcc GCC_USB30_PRIM_BCR>;
 
-                       usb_1_dwc3: dwc3@a600000 {
+                       usb_1_dwc3: usb@a600000 {
                                compatible = "snps,dwc3";
                                reg = <0 0x0a600000 0 0xcd00>;
                                interrupts = <GIC_SPI 133 IRQ_TYPE_LEVEL_HIGH>;
                        clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>,
                                 <&gcc GCC_USB30_SEC_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
-                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB30_SEC_SLEEP_CLK>,
+                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB3_SEC_CLKREF_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep", "xo";
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "xo";
 
                        assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_SEC_MASTER_CLK>;
index af8f226..72189f3 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB3_SEC_CLKREF_EN>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep", "xo";
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "xo";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
                        clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>,
                                 <&gcc GCC_USB30_SEC_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
-                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB30_SEC_SLEEP_CLK>,
+                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB3_SEC_CLKREF_EN>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep", "xo";
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "xo";
 
                        assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_SEC_MASTER_CLK>;
index 20f850b..918e7b5 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
-                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep";
+                                <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>;
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
                        clocks = <&gcc GCC_CFG_NOC_USB3_SEC_AXI_CLK>,
                                 <&gcc GCC_USB30_SEC_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_SEC_AXI_CLK>,
-                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB30_SEC_SLEEP_CLK>,
+                                <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB3_SEC_CLKREF_EN>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep", "xo";
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "xo";
 
                        assigned-clocks = <&gcc GCC_USB30_SEC_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_SEC_MASTER_CLK>;
index 934e29b..f9f7f17 100644 (file)
                        clocks = <&gcc GCC_CFG_NOC_USB3_PRIM_AXI_CLK>,
                                 <&gcc GCC_USB30_PRIM_MASTER_CLK>,
                                 <&gcc GCC_AGGRE_USB3_PRIM_AXI_CLK>,
-                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB30_PRIM_SLEEP_CLK>,
+                                <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                 <&gcc GCC_USB3_0_CLKREF_EN>;
-                       clock-names = "cfg_noc", "core", "iface", "mock_utmi",
-                                     "sleep", "xo";
+                       clock-names = "cfg_noc",
+                                     "core",
+                                     "iface",
+                                     "sleep",
+                                     "mock_utmi",
+                                     "xo";
 
                        assigned-clocks = <&gcc GCC_USB30_PRIM_MOCK_UTMI_CLK>,
                                          <&gcc GCC_USB30_PRIM_MASTER_CLK>;
                        assigned-clock-rates = <19200000>, <200000000>;
 
                        interrupts-extended = <&intc GIC_SPI 130 IRQ_TYPE_LEVEL_HIGH>,
-                                             <&pdc 14 IRQ_TYPE_EDGE_BOTH>,
+                                             <&pdc 17 IRQ_TYPE_LEVEL_HIGH>,
                                              <&pdc 15 IRQ_TYPE_EDGE_BOTH>,
-                                             <&pdc 17 IRQ_TYPE_LEVEL_HIGH>;
-                       interrupt-names = "hs_phy_irq", "dp_hs_phy_irq",
-                                         "dm_hs_phy_irq", "ss_phy_irq";
+                                             <&pdc 14 IRQ_TYPE_EDGE_BOTH>;
+                       interrupt-names = "hs_phy_irq",
+                                         "ss_phy_irq",
+                                         "dm_hs_phy_irq",
+                                         "dp_hs_phy_irq";
 
                        power-domains = <&gcc USB30_PRIM_GDSC>;
 
index b998301..c182a65 100644 (file)
        };
 
        otg: usb@13500000 {
-               compatible = "ingenic,jz4780-otg", "snps,dwc2";
+               compatible = "ingenic,jz4780-otg";
                reg = <0x13500000 0x40000>;
 
                interrupt-parent = <&intc>;
index 8bd27ed..343818a 100644 (file)
        };
 
        otg: usb@13500000 {
-               compatible = "ingenic,x1000-otg", "snps,dwc2";
+               compatible = "ingenic,x1000-otg";
                reg = <0x13500000 0x40000>;
 
                interrupt-parent = <&intc>;
index 2595df8..6aff19f 100644 (file)
        };
 
        otg: usb@13500000 {
-               compatible = "ingenic,x1830-otg", "snps,dwc2";
+               compatible = "ingenic,x1830-otg";
                reg = <0x13500000 0x40000>;
 
                interrupt-parent = <&intc>;
index c0e94cc..4765976 100644 (file)
@@ -1193,15 +1193,23 @@ const void *device_get_match_data(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(device_get_match_data);
 
-static void *
-fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
-                         void *data, devcon_match_fn_t match)
+static unsigned int fwnode_graph_devcon_matches(struct fwnode_handle *fwnode,
+                                               const char *con_id, void *data,
+                                               devcon_match_fn_t match,
+                                               void **matches,
+                                               unsigned int matches_len)
 {
        struct fwnode_handle *node;
        struct fwnode_handle *ep;
+       unsigned int count = 0;
        void *ret;
 
        fwnode_graph_for_each_endpoint(fwnode, ep) {
+               if (matches && count >= matches_len) {
+                       fwnode_handle_put(ep);
+                       break;
+               }
+
                node = fwnode_graph_get_remote_port_parent(ep);
                if (!fwnode_device_is_available(node)) {
                        fwnode_handle_put(node);
@@ -1211,33 +1219,43 @@ fwnode_graph_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
                ret = match(node, con_id, data);
                fwnode_handle_put(node);
                if (ret) {
-                       fwnode_handle_put(ep);
-                       return ret;
+                       if (matches)
+                               matches[count] = ret;
+                       count++;
                }
        }
-       return NULL;
+       return count;
 }
 
-static void *
-fwnode_devcon_match(struct fwnode_handle *fwnode, const char *con_id,
-                   void *data, devcon_match_fn_t match)
+static unsigned int fwnode_devcon_matches(struct fwnode_handle *fwnode,
+                                         const char *con_id, void *data,
+                                         devcon_match_fn_t match,
+                                         void **matches,
+                                         unsigned int matches_len)
 {
        struct fwnode_handle *node;
+       unsigned int count = 0;
+       unsigned int i;
        void *ret;
-       int i;
 
        for (i = 0; ; i++) {
+               if (matches && count >= matches_len)
+                       break;
+
                node = fwnode_find_reference(fwnode, con_id, i);
                if (IS_ERR(node))
                        break;
 
                ret = match(node, NULL, data);
                fwnode_handle_put(node);
-               if (ret)
-                       return ret;
+               if (ret) {
+                       if (matches)
+                               matches[count] = ret;
+                       count++;
+               }
        }
 
-       return NULL;
+       return count;
 }
 
 /**
@@ -1255,15 +1273,61 @@ void *fwnode_connection_find_match(struct fwnode_handle *fwnode,
                                   const char *con_id, void *data,
                                   devcon_match_fn_t match)
 {
+       unsigned int count;
        void *ret;
 
        if (!fwnode || !match)
                return NULL;
 
-       ret = fwnode_graph_devcon_match(fwnode, con_id, data, match);
-       if (ret)
+       count = fwnode_graph_devcon_matches(fwnode, con_id, data, match, &ret, 1);
+       if (count)
                return ret;
 
-       return fwnode_devcon_match(fwnode, con_id, data, match);
+       count = fwnode_devcon_matches(fwnode, con_id, data, match, &ret, 1);
+       return count ? ret : NULL;
 }
 EXPORT_SYMBOL_GPL(fwnode_connection_find_match);
+
+/**
+ * fwnode_connection_find_matches - Find connections from a device node
+ * @fwnode: Device node with the connection
+ * @con_id: Identifier for the connection
+ * @data: Data for the match function
+ * @match: Function to check and convert the connection description
+ * @matches: (Optional) array of pointers to fill with matches
+ * @matches_len: Length of @matches
+ *
+ * Find up to @matches_len connections with unique identifier @con_id between
+ * @fwnode and other device nodes. @match will be used to convert the
+ * connection description to data the caller is expecting to be returned
+ * through the @matches array.
+ * If @matches is NULL @matches_len is ignored and the total number of resolved
+ * matches is returned.
+ *
+ * Return: Number of matches resolved, or negative errno.
+ */
+int fwnode_connection_find_matches(struct fwnode_handle *fwnode,
+                                  const char *con_id, void *data,
+                                  devcon_match_fn_t match,
+                                  void **matches, unsigned int matches_len)
+{
+       unsigned int count_graph;
+       unsigned int count_ref;
+
+       if (!fwnode || !match)
+               return -EINVAL;
+
+       count_graph = fwnode_graph_devcon_matches(fwnode, con_id, data, match,
+                                                 matches, matches_len);
+
+       if (matches) {
+               matches += count_graph;
+               matches_len -= count_graph;
+       }
+
+       count_ref = fwnode_devcon_matches(fwnode, con_id, data, match,
+                                         matches, matches_len);
+
+       return count_graph + count_ref;
+}
+EXPORT_SYMBOL_GPL(fwnode_connection_find_matches);
index 54752c8..4490e2f 100644 (file)
@@ -387,7 +387,7 @@ static int hid_submit_ctrl(struct hid_device *hid)
 
                usbhid->urbctrl->pipe = usb_rcvctrlpipe(hid_to_usb_dev(hid), 0);
                maxpacket = usb_maxpacket(hid_to_usb_dev(hid),
-                                         usbhid->urbctrl->pipe, 0);
+                                         usbhid->urbctrl->pipe);
                len += (len == 0);      /* Don't allow 0-length reports */
                len = round_up(len, maxpacket);
                if (len > usbhid->bufsize)
index df02002..b4b007c 100644 (file)
@@ -279,7 +279,7 @@ static int usb_kbd_probe(struct usb_interface *iface,
                return -ENODEV;
 
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(dev, pipe);
 
        kbd = kzalloc(sizeof(struct usb_kbd), GFP_KERNEL);
        input_dev = input_allocate_device();
index c893320..fb1d7d1 100644 (file)
@@ -123,7 +123,7 @@ static int usb_mouse_probe(struct usb_interface *intf, const struct usb_device_i
                return -ENODEV;
 
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(dev, pipe);
 
        mouse = kzalloc(sizeof(struct usb_mouse), GFP_KERNEL);
        input_dev = input_allocate_device();
index 8a36d78..946bf75 100644 (file)
@@ -639,7 +639,7 @@ static int ati_remote2_urb_init(struct ati_remote2 *ar2)
                        return -ENOMEM;
 
                pipe = usb_rcvintpipe(udev, ar2->ep[i]->bEndpointAddress);
-               maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+               maxp = usb_maxpacket(udev, pipe);
                maxp = maxp > 4 ? 4 : maxp;
 
                usb_fill_int_urb(ar2->urb[i], udev, pipe, ar2->buf[i], maxp,
index f515fae..728325a 100644 (file)
@@ -745,7 +745,7 @@ static int cm109_usb_probe(struct usb_interface *intf,
 
        /* get a handle to the interrupt data pipe */
        pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
-       ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+       ret = usb_maxpacket(udev, pipe);
        if (ret != USB_PKT_LEN)
                dev_err(&intf->dev, "invalid payload size %d, expected %d\n",
                        ret, USB_PKT_LEN);
index c4e0e18..c1c733a 100644 (file)
@@ -374,7 +374,7 @@ static int powermate_probe(struct usb_interface *intf, const struct usb_device_i
 
        /* get a handle to the interrupt data pipe */
        pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(udev, pipe);
 
        if (maxp < POWERMATE_PAYLOAD_SIZE_MIN || maxp > POWERMATE_PAYLOAD_SIZE_MAX) {
                printk(KERN_WARNING "powermate: Expected payload of %d--%d bytes, found %d bytes!\n",
index 8ab01c7..6942078 100644 (file)
@@ -905,7 +905,7 @@ static int usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 
        /* get a handle to the interrupt data pipe */
        pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
-       ret = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+       ret = usb_maxpacket(udev, pipe);
        if (ret != USB_PKT_LEN)
                dev_err(&intf->dev, "invalid payload size %d, expected %zd\n",
                        ret, USB_PKT_LEN);
index a38d1fe..56c7e47 100644 (file)
@@ -130,7 +130,7 @@ static int usb_acecad_probe(struct usb_interface *intf, const struct usb_device_
                return -ENODEV;
 
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(dev, pipe);
 
        acecad = kzalloc(sizeof(struct usb_acecad), GFP_KERNEL);
        input_dev = input_allocate_device();
index 749edbd..c608ac5 100644 (file)
@@ -296,7 +296,7 @@ static int pegasus_probe(struct usb_interface *intf,
        pegasus->intf = intf;
 
        pipe = usb_rcvintpipe(dev, endpoint->bEndpointAddress);
-       pegasus->data_len = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+       pegasus->data_len = usb_maxpacket(dev, pipe);
 
        pegasus->data = usb_alloc_coherent(dev, pegasus->data_len, GFP_KERNEL,
                                           &pegasus->data_dma);
index c12dda7..3155e87 100644 (file)
@@ -773,7 +773,7 @@ static int ati_remote_initialize(struct ati_remote *ati_remote)
 
        /* Set up irq_urb */
        pipe = usb_rcvintpipe(udev, ati_remote->endpoint_in->bEndpointAddress);
-       maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(udev, pipe);
        maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
 
        usb_fill_int_urb(ati_remote->irq_urb, udev, pipe, ati_remote->inbuf,
@@ -784,7 +784,7 @@ static int ati_remote_initialize(struct ati_remote *ati_remote)
 
        /* Set up out_urb */
        pipe = usb_sndintpipe(udev, ati_remote->endpoint_out->bEndpointAddress);
-       maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(udev, pipe);
        maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
 
        usb_fill_int_urb(ati_remote->out_urb, udev, pipe, ati_remote->outbuf,
index 2dc810f..0834d5f 100644 (file)
@@ -1728,7 +1728,7 @@ static int mceusb_dev_probe(struct usb_interface *intf,
                pipe = usb_rcvintpipe(dev, ep_in->bEndpointAddress);
        else
                pipe = usb_rcvbulkpipe(dev, ep_in->bEndpointAddress);
-       maxp = usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(dev, pipe);
 
        ir = kzalloc(sizeof(struct mceusb_dev), GFP_KERNEL);
        if (!ir)
index 16ba85d..deb8533 100644 (file)
@@ -307,7 +307,7 @@ static int streamzap_probe(struct usb_interface *intf,
        }
 
        pipe = usb_rcvintpipe(usbdev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(usbdev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(usbdev, pipe);
 
        if (maxp == 0) {
                dev_err(&intf->dev, "%s: endpoint Max Packet Size is 0!?!\n",
index 98d0b43..7424b20 100644 (file)
@@ -171,7 +171,7 @@ static int xbox_remote_initialize(struct xbox_remote *xbox_remote,
 
        /* Set up irq_urb */
        pipe = usb_rcvintpipe(udev, endpoint_in->bEndpointAddress);
-       maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(udev, pipe);
        maxp = (maxp > DATA_BUFSIZE) ? DATA_BUFSIZE : maxp;
 
        usb_fill_int_urb(xbox_remote->irq_urb, udev, pipe, xbox_remote->inbuf,
index 8c2725e..ee04973 100644 (file)
@@ -120,7 +120,7 @@ static int tm6000_start_stream(struct tm6000_core *dev)
        pipe = usb_rcvbulkpipe(dev->udev, dev->bulk_in.endp->desc.bEndpointAddress
                                                          & USB_ENDPOINT_NUMBER_MASK);
 
-       size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+       size = usb_maxpacket(dev->udev, pipe);
        size = size * 15; /* 512 x 8 or 12 or 15 */
 
        dvb->bulk_urb->transfer_buffer = kzalloc(size, GFP_KERNEL);
index 84602ed..5136e9e 100644 (file)
@@ -340,7 +340,7 @@ static int __tm6000_ir_int_start(struct rc_dev *rc)
                dev->int_in.endp->desc.bEndpointAddress
                & USB_ENDPOINT_NUMBER_MASK);
 
-       size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+       size = usb_maxpacket(dev->udev, pipe);
        dprintk(1, "IR max size: %d\n", size);
 
        ir->int_urb->transfer_buffer = kzalloc(size, GFP_ATOMIC);
index e293f6f..d855a19 100644 (file)
@@ -570,7 +570,7 @@ static int tm6000_prepare_isoc(struct tm6000_core *dev)
                               dev->isoc_in.endp->desc.bEndpointAddress &
                               USB_ENDPOINT_NUMBER_MASK);
 
-       size = usb_maxpacket(dev->udev, pipe, usb_pipeout(pipe));
+       size = usb_maxpacket(dev->udev, pipe);
 
        if (size > dev->isoc_in.maxsize)
                size = dev->isoc_in.maxsize;
index ec2a4fc..e889a8b 100644 (file)
@@ -784,7 +784,7 @@ static int mei_hdcp_component_match(struct device *dev, int subcomponent,
 {
        struct device *base = data;
 
-       if (strcmp(dev->driver->name, "i915") ||
+       if (!dev->driver || strcmp(dev->driver->name, "i915") ||
            subcomponent != I915_COMPONENT_HDCP)
                return 0;
 
index f7380d3..5c39457 100644 (file)
@@ -131,7 +131,7 @@ static int mei_pxp_component_match(struct device *dev, int subcomponent,
 {
        struct device *base = data;
 
-       if (strcmp(dev->driver->name, "i915") ||
+       if (!dev->driver || strcmp(dev->driver->name, "i915") ||
            subcomponent != I915_COMPONENT_PXP)
                return 0;
 
index 15f91d6..63ce9d8 100644 (file)
@@ -441,7 +441,7 @@ static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx)
         * .bind which is called before usbnet sets up dev->maxpacket
         */
        if (val != le32_to_cpu(ctx->ncm_parm.dwNtbOutMaxSize) &&
-           val % usb_maxpacket(dev->udev, dev->out, 1) == 0)
+           val % usb_maxpacket(dev->udev, dev->out) == 0)
                val++;
 
        /* we might need to flush any pending tx buffers if running */
@@ -465,7 +465,7 @@ static void cdc_ncm_update_rxtx_max(struct usbnet *dev, u32 new_rx, u32 new_tx)
        usbnet_update_max_qlen(dev);
 
        /* never pad more than 3 full USB packets per transfer */
-       ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out, 1),
+       ctx->min_tx_pkt = clamp_t(u16, ctx->tx_max - 3 * usb_maxpacket(dev->udev, dev->out),
                                  CDC_NCM_MIN_TX_PKT, ctx->tx_max);
 }
 
index 415f166..a743393 100644 (file)
@@ -4423,7 +4423,7 @@ static int lan78xx_probe(struct usb_interface *intf,
                goto out4;
 
        period = ep_intr->desc.bInterval;
-       maxp = usb_maxpacket(dev->udev, dev->pipe_intr, 0);
+       maxp = usb_maxpacket(dev->udev, dev->pipe_intr);
        buf = kmalloc(maxp, GFP_KERNEL);
        if (!buf) {
                ret = -ENOMEM;
@@ -4441,7 +4441,7 @@ static int lan78xx_probe(struct usb_interface *intf,
                dev->urb_intr->transfer_flags |= URB_FREE_BUFFER;
        }
 
-       dev->maxpacket = usb_maxpacket(dev->udev, dev->pipe_out, 1);
+       dev->maxpacket = usb_maxpacket(dev->udev, dev->pipe_out);
 
        /* Reject broken descriptors. */
        if (dev->maxpacket == 0) {
index 247f58c..de0b00b 100644 (file)
@@ -333,7 +333,7 @@ generic_rndis_bind(struct usbnet *dev, struct usb_interface *intf, int flags)
        net->hard_header_len += sizeof (struct rndis_data_hdr);
        dev->hard_mtu = net->mtu + net->hard_header_len;
 
-       dev->maxpacket = usb_maxpacket(dev->udev, dev->out, 1);
+       dev->maxpacket = usb_maxpacket(dev->udev, dev->out);
        if (dev->maxpacket == 0) {
                netif_dbg(dev, probe, dev->net,
                          "dev->maxpacket can't be 0\n");
index 9a6450f..d5bf573 100644 (file)
@@ -229,7 +229,7 @@ static int init_status (struct usbnet *dev, struct usb_interface *intf)
        pipe = usb_rcvintpipe (dev->udev,
                        dev->status->desc.bEndpointAddress
                                & USB_ENDPOINT_NUMBER_MASK);
-       maxp = usb_maxpacket (dev->udev, pipe, 0);
+       maxp = usb_maxpacket(dev->udev, pipe);
 
        /* avoid 1 msec chatter:  min 8 msec poll rate */
        period = max ((int) dev->status->desc.bInterval,
@@ -1789,7 +1789,7 @@ usbnet_probe (struct usb_interface *udev, const struct usb_device_id *prod)
 
        if (!dev->rx_urb_size)
                dev->rx_urb_size = dev->hard_mtu;
-       dev->maxpacket = usb_maxpacket (dev->udev, dev->out, 1);
+       dev->maxpacket = usb_maxpacket(dev->udev, dev->out);
        if (dev->maxpacket == 0) {
                /* that is a broken device */
                status = -ENODEV;
index a85e192..1bb92ca 100644 (file)
@@ -1068,7 +1068,7 @@ int __mt76u_init(struct mt76_dev *dev, struct usb_interface *intf,
 
        INIT_WORK(&usb->stat_work, mt76u_tx_status_data);
 
-       usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0), 1);
+       usb->data_len = usb_maxpacket(udev, usb_sndctrlpipe(udev, 0));
        if (usb->data_len < 32)
                usb->data_len = 32;
 
index 74c3d8c..0827bc8 100644 (file)
@@ -586,10 +586,10 @@ static void rt2x00usb_assign_endpoint(struct data_queue *queue,
 
        if (queue->qid == QID_RX) {
                pipe = usb_rcvbulkpipe(usb_dev, queue->usb_endpoint);
-               queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 0);
+               queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe);
        } else {
                pipe = usb_sndbulkpipe(usb_dev, queue->usb_endpoint);
-               queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe, 1);
+               queue->usb_maxpacket = usb_maxpacket(usb_dev, pipe);
        }
 
        if (!queue->usb_maxpacket)
index 4986edf..e92c658 100644 (file)
@@ -158,21 +158,20 @@ static bool tb_cfg_request_is_active(struct tb_cfg_request *req)
 static struct tb_cfg_request *
 tb_cfg_request_find(struct tb_ctl *ctl, struct ctl_pkg *pkg)
 {
-       struct tb_cfg_request *req;
-       bool found = false;
+       struct tb_cfg_request *req = NULL, *iter;
 
        mutex_lock(&pkg->ctl->request_queue_lock);
-       list_for_each_entry(req, &pkg->ctl->request_queue, list) {
-               tb_cfg_request_get(req);
-               if (req->match(req, pkg)) {
-                       found = true;
+       list_for_each_entry(iter, &pkg->ctl->request_queue, list) {
+               tb_cfg_request_get(iter);
+               if (iter->match(iter, pkg)) {
+                       req = iter;
                        break;
                }
-               tb_cfg_request_put(req);
+               tb_cfg_request_put(iter);
        }
        mutex_unlock(&pkg->ctl->request_queue_lock);
 
-       return found ? req : NULL;
+       return req;
 }
 
 /* utility functions */
index 4a58218..6221ca4 100644 (file)
@@ -1207,7 +1207,7 @@ static int nhi_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 
        nhi->pdev = pdev;
        nhi->ops = (const struct tb_nhi_ops *)id->driver_data;
-       /* cannot fail - table is allocated bin pcim_iomap_regions */
+       /* cannot fail - table is allocated in pcim_iomap_regions */
        nhi->iobase = pcim_iomap_table(pdev)[0];
        nhi->hop_count = ioread32(nhi->iobase + REG_HOP_COUNT) & 0x3ff;
        dev_dbg(&pdev->dev, "total paths: %d\n", nhi->hop_count);
index 299712a..ee03fd7 100644 (file)
@@ -166,6 +166,9 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
                return NULL;
        }
 
+       tb_dbg(path->tb, "discovering %s path starting from %llx:%u\n",
+              path->name, tb_route(src->sw), src->port);
+
        p = src;
        h = src_hopid;
 
@@ -198,10 +201,13 @@ struct tb_path *tb_path_discover(struct tb_port *src, int src_hopid,
                path->hops[i].out_port = out_port;
                path->hops[i].next_hop_index = next_hop;
 
+               tb_dump_hop(&path->hops[i], &hop);
+
                h = next_hop;
                p = out_port->remote;
        }
 
+       tb_dbg(path->tb, "path discovery complete\n");
        return path;
 
 err:
index ac87e8b..561e1d7 100644 (file)
@@ -693,8 +693,14 @@ static int __tb_port_enable(struct tb_port *port, bool enable)
        else
                phy |= LANE_ADP_CS_1_LD;
 
-       return tb_port_write(port, &phy, TB_CFG_PORT,
-                            port->cap_phy + LANE_ADP_CS_1, 1);
+
+       ret = tb_port_write(port, &phy, TB_CFG_PORT,
+                           port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       tb_port_dbg(port, "lane %sabled\n", enable ? "en" : "dis");
+       return 0;
 }
 
 /**
@@ -993,7 +999,17 @@ static bool tb_port_is_width_supported(struct tb_port *port, int width)
        return !!(widths & width);
 }
 
-static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
+/**
+ * tb_port_set_link_width() - Set target link width of the lane adapter
+ * @port: Lane adapter
+ * @width: Target link width (%1 or %2)
+ *
+ * Sets the target link width of the lane adapter to @width. Does not
+ * enable/disable lane bonding. For that call tb_port_set_lane_bonding().
+ *
+ * Return: %0 in case of success and negative errno in case of error
+ */
+int tb_port_set_link_width(struct tb_port *port, unsigned int width)
 {
        u32 val;
        int ret;
@@ -1020,12 +1036,58 @@ static int tb_port_set_link_width(struct tb_port *port, unsigned int width)
                return -EINVAL;
        }
 
-       val |= LANE_ADP_CS_1_LB;
-
        return tb_port_write(port, &val, TB_CFG_PORT,
                             port->cap_phy + LANE_ADP_CS_1, 1);
 }
 
+/**
+ * tb_port_set_lane_bonding() - Enable/disable lane bonding
+ * @port: Lane adapter
+ * @bonding: enable/disable bonding
+ *
+ * Enables or disables lane bonding. This should be called after target
+ * link width has been set (tb_port_set_link_width()). Note in most
+ * cases one should use tb_port_lane_bonding_enable() instead to enable
+ * lane bonding.
+ *
+ * As a side effect sets @port->bonding accordingly (and does the same
+ * for lane 1 too).
+ *
+ * Return: %0 in case of success and negative errno in case of error
+ */
+int tb_port_set_lane_bonding(struct tb_port *port, bool bonding)
+{
+       u32 val;
+       int ret;
+
+       if (!port->cap_phy)
+               return -EINVAL;
+
+       ret = tb_port_read(port, &val, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       if (bonding)
+               val |= LANE_ADP_CS_1_LB;
+       else
+               val &= ~LANE_ADP_CS_1_LB;
+
+       ret = tb_port_write(port, &val, TB_CFG_PORT,
+                           port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+
+       /*
+        * When lane 0 bonding is set it will affect lane 1 too so
+        * update both.
+        */
+       port->bonded = bonding;
+       port->dual_link_port->bonded = bonding;
+
+       return 0;
+}
+
 /**
  * tb_port_lane_bonding_enable() - Enable bonding on port
  * @port: port to enable
@@ -1050,22 +1112,27 @@ int tb_port_lane_bonding_enable(struct tb_port *port)
        if (ret == 1) {
                ret = tb_port_set_link_width(port, 2);
                if (ret)
-                       return ret;
+                       goto err_lane0;
        }
 
        ret = tb_port_get_link_width(port->dual_link_port);
        if (ret == 1) {
                ret = tb_port_set_link_width(port->dual_link_port, 2);
-               if (ret) {
-                       tb_port_set_link_width(port, 1);
-                       return ret;
-               }
+               if (ret)
+                       goto err_lane0;
        }
 
-       port->bonded = true;
-       port->dual_link_port->bonded = true;
+       ret = tb_port_set_lane_bonding(port, true);
+       if (ret)
+               goto err_lane1;
 
        return 0;
+
+err_lane1:
+       tb_port_set_link_width(port->dual_link_port, 1);
+err_lane0:
+       tb_port_set_link_width(port, 1);
+       return ret;
 }
 
 /**
@@ -1074,13 +1141,10 @@ int tb_port_lane_bonding_enable(struct tb_port *port)
  *
  * Disable bonding by setting the link width of the port and the
  * other port in case of dual link port.
- *
  */
 void tb_port_lane_bonding_disable(struct tb_port *port)
 {
-       port->dual_link_port->bonded = false;
-       port->bonded = false;
-
+       tb_port_set_lane_bonding(port, false);
        tb_port_set_link_width(port->dual_link_port, 1);
        tb_port_set_link_width(port, 1);
 }
@@ -1104,10 +1168,17 @@ int tb_port_wait_for_link_width(struct tb_port *port, int width,
 
        do {
                ret = tb_port_get_link_width(port);
-               if (ret < 0)
-                       return ret;
-               else if (ret == width)
+               if (ret < 0) {
+                       /*
+                        * Sometimes we get port locked error when
+                        * polling the lanes so we can ignore it and
+                        * retry.
+                        */
+                       if (ret != -EACCES)
+                               return ret;
+               } else if (ret == width) {
                        return 0;
+               }
 
                usleep_range(1000, 2000);
        } while (ktime_before(ktime_get(), timeout));
index 9beb47b..9a3214f 100644 (file)
@@ -169,12 +169,6 @@ static void tb_discover_tunnels(struct tb *tb)
 
 static int tb_port_configure_xdomain(struct tb_port *port)
 {
-       /*
-        * XDomain paths currently only support single lane so we must
-        * disable the other lane according to USB4 spec.
-        */
-       tb_port_disable(port->dual_link_port);
-
        if (tb_switch_is_usb4(port->sw))
                return usb4_port_configure_xdomain(port);
        return tb_lc_configure_xdomain(port);
@@ -867,7 +861,7 @@ static struct tb_port *tb_find_dp_out(struct tb *tb, struct tb_port *in)
 
 static void tb_tunnel_dp(struct tb *tb)
 {
-       int available_up, available_down, ret;
+       int available_up, available_down, ret, link_nr;
        struct tb_cm *tcm = tb_priv(tb);
        struct tb_port *port, *in, *out;
        struct tb_tunnel *tunnel;
@@ -912,6 +906,20 @@ static void tb_tunnel_dp(struct tb *tb)
                return;
        }
 
+       /*
+        * This is only applicable to links that are not bonded (so
+        * when Thunderbolt 1 hardware is involved somewhere in the
+        * topology). For these try to share the DP bandwidth between
+        * the two lanes.
+        */
+       link_nr = 1;
+       list_for_each_entry(tunnel, &tcm->tunnel_list, list) {
+               if (tb_tunnel_is_dp(tunnel)) {
+                       link_nr = 0;
+                       break;
+               }
+       }
+
        /*
         * DP stream needs the domain to be active so runtime resume
         * both ends of the tunnel.
@@ -943,7 +951,8 @@ static void tb_tunnel_dp(struct tb *tb)
        tb_dbg(tb, "available bandwidth for new DP tunnel %u/%u Mb/s\n",
               available_up, available_down);
 
-       tunnel = tb_tunnel_alloc_dp(tb, in, out, available_up, available_down);
+       tunnel = tb_tunnel_alloc_dp(tb, in, out, link_nr, available_up,
+                                   available_down);
        if (!tunnel) {
                tb_port_dbg(out, "could not allocate DP tunnel\n");
                goto err_reclaim;
index b6fcd8d..4602c69 100644 (file)
@@ -674,7 +674,7 @@ static inline int tb_port_write(struct tb_port *port, const void *buffer,
 #define __TB_PORT_PRINT(level, _port, fmt, arg...)                      \
        do {                                                            \
                const struct tb_port *__port = (_port);                 \
-               level(__port->sw->tb, "%llx:%x: " fmt,                  \
+               level(__port->sw->tb, "%llx:%u: " fmt,                  \
                      tb_route(__port->sw), __port->port, ## arg);      \
        } while (0)
 #define tb_port_WARN(port, fmt, arg...) \
@@ -991,6 +991,7 @@ int tb_switch_pcie_l1_enable(struct tb_switch *sw);
 int tb_switch_xhci_connect(struct tb_switch *sw);
 void tb_switch_xhci_disconnect(struct tb_switch *sw);
 
+int tb_port_state(struct tb_port *port);
 int tb_wait_for_port(struct tb_port *port, bool wait_if_unplugged);
 int tb_port_add_nfc_credits(struct tb_port *port, int credits);
 int tb_port_clear_counter(struct tb_port *port, int counter);
@@ -1023,7 +1024,8 @@ static inline bool tb_port_use_credit_allocation(const struct tb_port *port)
 
 int tb_port_get_link_speed(struct tb_port *port);
 int tb_port_get_link_width(struct tb_port *port);
-int tb_port_state(struct tb_port *port);
+int tb_port_set_link_width(struct tb_port *port, unsigned int width);
+int tb_port_set_lane_bonding(struct tb_port *port, bool bonding);
 int tb_port_lane_bonding_enable(struct tb_port *port);
 void tb_port_lane_bonding_disable(struct tb_port *port);
 int tb_port_wait_for_link_width(struct tb_port *port, int width,
index fe1afa4..33c4c7a 100644 (file)
@@ -527,6 +527,10 @@ enum tb_xdp_type {
        PROPERTIES_CHANGED_RESPONSE,
        ERROR_RESPONSE,
        UUID_REQUEST = 12,
+       LINK_STATE_STATUS_REQUEST = 15,
+       LINK_STATE_STATUS_RESPONSE,
+       LINK_STATE_CHANGE_REQUEST,
+       LINK_STATE_CHANGE_RESPONSE,
 };
 
 struct tb_xdp_header {
@@ -540,6 +544,41 @@ struct tb_xdp_error_response {
        u32 error;
 };
 
+struct tb_xdp_link_state_status {
+       struct tb_xdp_header hdr;
+};
+
+struct tb_xdp_link_state_status_response {
+       union {
+               struct tb_xdp_error_response err;
+               struct {
+                       struct tb_xdp_header hdr;
+                       u32 status;
+                       u8 slw;
+                       u8 tlw;
+                       u8 sls;
+                       u8 tls;
+               };
+       };
+};
+
+struct tb_xdp_link_state_change {
+       struct tb_xdp_header hdr;
+       u8 tlw;
+       u8 tls;
+       u16 reserved;
+};
+
+struct tb_xdp_link_state_change_response {
+       union {
+               struct tb_xdp_error_response err;
+               struct {
+                       struct tb_xdp_header hdr;
+                       u32 status;
+               };
+       };
+};
+
 struct tb_xdp_uuid {
        struct tb_xdp_header hdr;
 };
index b301eeb..6a16f61 100644 (file)
@@ -311,11 +311,16 @@ struct tb_regs_port_header {
 
 /* Lane adapter registers */
 #define LANE_ADP_CS_0                          0x00
+#define LANE_ADP_CS_0_SUPPORTED_SPEED_MASK     GENMASK(19, 16)
+#define LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT    16
 #define LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK     GENMASK(25, 20)
 #define LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT    20
+#define LANE_ADP_CS_0_SUPPORTED_WIDTH_DUAL     0x2
 #define LANE_ADP_CS_0_CL0S_SUPPORT             BIT(26)
 #define LANE_ADP_CS_0_CL1_SUPPORT              BIT(27)
 #define LANE_ADP_CS_1                          0x01
+#define LANE_ADP_CS_1_TARGET_SPEED_MASK                GENMASK(3, 0)
+#define LANE_ADP_CS_1_TARGET_SPEED_GEN3                0xc
 #define LANE_ADP_CS_1_TARGET_WIDTH_MASK                GENMASK(9, 4)
 #define LANE_ADP_CS_1_TARGET_WIDTH_SHIFT       4
 #define LANE_ADP_CS_1_TARGET_WIDTH_SINGLE      0x1
index 1f69bab..99b30f2 100644 (file)
@@ -341,6 +341,47 @@ static struct tb_switch *alloc_dev_with_dpin(struct kunit *test,
        return sw;
 }
 
+static struct tb_switch *alloc_dev_without_dp(struct kunit *test,
+                                             struct tb_switch *parent,
+                                             u64 route, bool bonded)
+{
+       struct tb_switch *sw;
+       int i;
+
+       sw = alloc_dev_default(test, parent, route, bonded);
+       if (!sw)
+               return NULL;
+       /*
+        * Device with:
+        * 2x USB4 Adapters (adapters 1,2 and 3,4),
+        * 1x PCIe Upstream (adapter 9),
+        * 1x PCIe Downstream (adapter 10),
+        * 1x USB3 Upstream (adapter 16),
+        * 1x USB3 Downstream (adapter 17)
+        */
+       for (i = 5; i <= 8; i++)
+               sw->ports[i].disabled = true;
+
+       for (i = 11; i <= 14; i++)
+               sw->ports[i].disabled = true;
+
+       sw->ports[13].cap_adap = 0;
+       sw->ports[14].cap_adap = 0;
+
+       for (i = 18; i <= 19; i++)
+               sw->ports[i].disabled = true;
+
+       sw->generation = 4;
+       sw->credit_allocation = true;
+       sw->max_usb3_credits = 109;
+       sw->min_dp_aux_credits = 0;
+       sw->min_dp_main_credits = 0;
+       sw->max_pcie_credits = 30;
+       sw->max_dma_credits = 1;
+
+       return sw;
+}
+
 static struct tb_switch *alloc_dev_usb4(struct kunit *test,
                                        struct tb_switch *parent,
                                        u64 route, bool bonded)
@@ -1348,7 +1389,7 @@ static void tb_test_tunnel_dp(struct kunit *test)
        in = &host->ports[5];
        out = &dev->ports[13];
 
-       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, tunnel != NULL);
        KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
        KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1394,7 +1435,7 @@ static void tb_test_tunnel_dp_chain(struct kunit *test)
        in = &host->ports[5];
        out = &dev4->ports[14];
 
-       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, tunnel != NULL);
        KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
        KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1444,7 +1485,7 @@ static void tb_test_tunnel_dp_tree(struct kunit *test)
        in = &dev2->ports[13];
        out = &dev5->ports[13];
 
-       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, tunnel != NULL);
        KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
        KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1509,7 +1550,7 @@ static void tb_test_tunnel_dp_max_length(struct kunit *test)
        in = &dev6->ports[13];
        out = &dev12->ports[13];
 
-       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, tunnel != NULL);
        KUNIT_EXPECT_EQ(test, tunnel->type, TB_TUNNEL_DP);
        KUNIT_EXPECT_PTR_EQ(test, tunnel->src_port, in);
@@ -1627,7 +1668,7 @@ static void tb_test_tunnel_port_on_path(struct kunit *test)
        in = &dev2->ports[13];
        out = &dev5->ports[13];
 
-       dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       dp_tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, dp_tunnel != NULL);
 
        KUNIT_EXPECT_TRUE(test, tb_tunnel_port_on_path(dp_tunnel, in));
@@ -1996,6 +2037,56 @@ static void tb_test_credit_alloc_pcie(struct kunit *test)
        tb_tunnel_free(tunnel);
 }
 
+static void tb_test_credit_alloc_without_dp(struct kunit *test)
+{
+       struct tb_switch *host, *dev;
+       struct tb_port *up, *down;
+       struct tb_tunnel *tunnel;
+       struct tb_path *path;
+
+       host = alloc_host_usb4(test);
+       dev = alloc_dev_without_dp(test, host, 0x1, true);
+
+       /*
+        * The device has no DP therefore baMinDPmain = baMinDPaux = 0
+        *
+        * Create PCIe path with buffers less than baMaxPCIe.
+        *
+        * For a device with buffers configurations:
+        * baMaxUSB3 = 109
+        * baMinDPaux = 0
+        * baMinDPmain = 0
+        * baMaxPCIe = 30
+        * baMaxHI = 1
+        * Remaining Buffers = Total - (CP + DP) = 120 - (2 + 0) = 118
+        * PCIe Credits = Max(6, Min(baMaxPCIe, Remaining Buffers - baMaxUSB3)
+        *              = Max(6, Min(30, 9) = 9
+        */
+       down = &host->ports[8];
+       up = &dev->ports[9];
+       tunnel = tb_tunnel_alloc_pci(NULL, up, down);
+       KUNIT_ASSERT_TRUE(test, tunnel != NULL);
+       KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)2);
+
+       /* PCIe downstream path */
+       path = tunnel->paths[0];
+       KUNIT_ASSERT_EQ(test, path->path_length, 2);
+       KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+       KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+       KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+       KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 9U);
+
+       /* PCIe upstream path */
+       path = tunnel->paths[1];
+       KUNIT_ASSERT_EQ(test, path->path_length, 2);
+       KUNIT_EXPECT_EQ(test, path->hops[0].nfc_credits, 0U);
+       KUNIT_EXPECT_EQ(test, path->hops[0].initial_credits, 7U);
+       KUNIT_EXPECT_EQ(test, path->hops[1].nfc_credits, 0U);
+       KUNIT_EXPECT_EQ(test, path->hops[1].initial_credits, 64U);
+
+       tb_tunnel_free(tunnel);
+}
+
 static void tb_test_credit_alloc_dp(struct kunit *test)
 {
        struct tb_switch *host, *dev;
@@ -2009,7 +2100,7 @@ static void tb_test_credit_alloc_dp(struct kunit *test)
        in = &host->ports[5];
        out = &dev->ports[14];
 
-       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       tunnel = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, tunnel != NULL);
        KUNIT_ASSERT_EQ(test, tunnel->npaths, (size_t)3);
 
@@ -2245,7 +2336,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL1(struct kunit *test,
 
        in = &host->ports[5];
        out = &dev->ports[13];
-       dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       dp_tunnel1 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, dp_tunnel1 != NULL);
        KUNIT_ASSERT_EQ(test, dp_tunnel1->npaths, (size_t)3);
 
@@ -2282,7 +2373,7 @@ static struct tb_tunnel *TB_TEST_DP_TUNNEL2(struct kunit *test,
 
        in = &host->ports[6];
        out = &dev->ports[14];
-       dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 0, 0);
+       dp_tunnel2 = tb_tunnel_alloc_dp(NULL, in, out, 1, 0, 0);
        KUNIT_ASSERT_TRUE(test, dp_tunnel2 != NULL);
        KUNIT_ASSERT_EQ(test, dp_tunnel2->npaths, (size_t)3);
 
@@ -2709,6 +2800,7 @@ static struct kunit_case tb_test_cases[] = {
        KUNIT_CASE(tb_test_credit_alloc_legacy_not_bonded),
        KUNIT_CASE(tb_test_credit_alloc_legacy_bonded),
        KUNIT_CASE(tb_test_credit_alloc_pcie),
+       KUNIT_CASE(tb_test_credit_alloc_without_dp),
        KUNIT_CASE(tb_test_credit_alloc_dp),
        KUNIT_CASE(tb_test_credit_alloc_usb3),
        KUNIT_CASE(tb_test_credit_alloc_dma),
index 118742e..2c3cf7f 100644 (file)
@@ -102,8 +102,11 @@ static unsigned int tb_available_credits(const struct tb_port *port,
                 * Maximum number of DP streams possible through the
                 * lane adapter.
                 */
-               ndp = (credits - (usb3 + pcie + spare)) /
-                     (sw->min_dp_aux_credits + sw->min_dp_main_credits);
+               if (sw->min_dp_aux_credits + sw->min_dp_main_credits)
+                       ndp = (credits - (usb3 + pcie + spare)) /
+                             (sw->min_dp_aux_credits + sw->min_dp_main_credits);
+               else
+                       ndp = 0;
        } else {
                ndp = 0;
        }
@@ -858,6 +861,7 @@ err_free:
  * @tb: Pointer to the domain structure
  * @in: DP in adapter port
  * @out: DP out adapter port
+ * @link_nr: Preferred lane adapter when the link is not bonded
  * @max_up: Maximum available upstream bandwidth for the DP tunnel (%0
  *         if not limited)
  * @max_down: Maximum available downstream bandwidth for the DP tunnel
@@ -869,8 +873,8 @@ err_free:
  * Return: Returns a tb_tunnel on success or NULL on failure.
  */
 struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
-                                    struct tb_port *out, int max_up,
-                                    int max_down)
+                                    struct tb_port *out, int link_nr,
+                                    int max_up, int max_down)
 {
        struct tb_tunnel *tunnel;
        struct tb_path **paths;
@@ -894,21 +898,21 @@ struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
        paths = tunnel->paths;
 
        path = tb_path_alloc(tb, in, TB_DP_VIDEO_HOPID, out, TB_DP_VIDEO_HOPID,
-                            1, "Video");
+                            link_nr, "Video");
        if (!path)
                goto err_free;
        tb_dp_init_video_path(path);
        paths[TB_DP_VIDEO_PATH_OUT] = path;
 
        path = tb_path_alloc(tb, in, TB_DP_AUX_TX_HOPID, out,
-                            TB_DP_AUX_TX_HOPID, 1, "AUX TX");
+                            TB_DP_AUX_TX_HOPID, link_nr, "AUX TX");
        if (!path)
                goto err_free;
        tb_dp_init_aux_path(path);
        paths[TB_DP_AUX_PATH_OUT] = path;
 
        path = tb_path_alloc(tb, out, TB_DP_AUX_RX_HOPID, in,
-                            TB_DP_AUX_RX_HOPID, 1, "AUX RX");
+                            TB_DP_AUX_RX_HOPID, link_nr, "AUX RX");
        if (!path)
                goto err_free;
        tb_dp_init_aux_path(path);
index 03e5607..bb4d1f1 100644 (file)
@@ -71,8 +71,8 @@ struct tb_tunnel *tb_tunnel_alloc_pci(struct tb *tb, struct tb_port *up,
 struct tb_tunnel *tb_tunnel_discover_dp(struct tb *tb, struct tb_port *in,
                                        bool alloc_hopid);
 struct tb_tunnel *tb_tunnel_alloc_dp(struct tb *tb, struct tb_port *in,
-                                    struct tb_port *out, int max_up,
-                                    int max_down);
+                                    struct tb_port *out, int link_nr,
+                                    int max_up, int max_down);
 struct tb_tunnel *tb_tunnel_alloc_dma(struct tb *tb, struct tb_port *nhi,
                                      struct tb_port *dst, int transmit_path,
                                      int transmit_ring, int receive_path,
index 29e2a4f..6b02945 100644 (file)
@@ -7,9 +7,37 @@
  */
 
 #include <linux/pm_runtime.h>
+#include <linux/component.h>
+#include <linux/property.h>
 
 #include "tb.h"
 
+static int connector_bind(struct device *dev, struct device *connector, void *data)
+{
+       int ret;
+
+       ret = sysfs_create_link(&dev->kobj, &connector->kobj, "connector");
+       if (ret)
+               return ret;
+
+       ret = sysfs_create_link(&connector->kobj, &dev->kobj, dev_name(dev));
+       if (ret)
+               sysfs_remove_link(&dev->kobj, "connector");
+
+       return ret;
+}
+
+static void connector_unbind(struct device *dev, struct device *connector, void *data)
+{
+       sysfs_remove_link(&connector->kobj, dev_name(dev));
+       sysfs_remove_link(&dev->kobj, "connector");
+}
+
+static const struct component_ops connector_ops = {
+       .bind = connector_bind,
+       .unbind = connector_unbind,
+};
+
 static ssize_t link_show(struct device *dev, struct device_attribute *attr,
                         char *buf)
 {
@@ -246,6 +274,14 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port)
                return ERR_PTR(ret);
        }
 
+       if (dev_fwnode(&usb4->dev)) {
+               ret = component_add(&usb4->dev, &connector_ops);
+               if (ret) {
+                       dev_err(&usb4->dev, "failed to add component\n");
+                       device_unregister(&usb4->dev);
+               }
+       }
+
        pm_runtime_no_callbacks(&usb4->dev);
        pm_runtime_set_active(&usb4->dev);
        pm_runtime_enable(&usb4->dev);
@@ -265,6 +301,8 @@ struct usb4_port *usb4_port_device_add(struct tb_port *port)
  */
 void usb4_port_device_remove(struct usb4_port *usb4)
 {
+       if (dev_fwnode(&usb4->dev))
+               component_del(&usb4->dev, &connector_ops);
        device_unregister(&usb4->dev);
 }
 
index 01d6b72..c31c0d9 100644 (file)
 
 #include "tb.h"
 
-#define XDOMAIN_DEFAULT_TIMEOUT                        1000 /* ms */
-#define XDOMAIN_UUID_RETRIES                   10
-#define XDOMAIN_PROPERTIES_RETRIES             10
-#define XDOMAIN_PROPERTIES_CHANGED_RETRIES     10
-#define XDOMAIN_BONDING_WAIT                   100  /* ms */
+#define XDOMAIN_SHORT_TIMEOUT                  100     /* ms */
+#define XDOMAIN_DEFAULT_TIMEOUT                        1000    /* ms */
+#define XDOMAIN_BONDING_TIMEOUT                        10000   /* ms */
+#define XDOMAIN_RETRIES                                10
 #define XDOMAIN_DEFAULT_MAX_HOPID              15
 
+enum {
+       XDOMAIN_STATE_INIT,
+       XDOMAIN_STATE_UUID,
+       XDOMAIN_STATE_LINK_STATUS,
+       XDOMAIN_STATE_LINK_STATE_CHANGE,
+       XDOMAIN_STATE_LINK_STATUS2,
+       XDOMAIN_STATE_BONDING_UUID_LOW,
+       XDOMAIN_STATE_BONDING_UUID_HIGH,
+       XDOMAIN_STATE_PROPERTIES,
+       XDOMAIN_STATE_ENUMERATED,
+       XDOMAIN_STATE_ERROR,
+};
+
+static const char * const state_names[] = {
+       [XDOMAIN_STATE_INIT] = "INIT",
+       [XDOMAIN_STATE_UUID] = "UUID",
+       [XDOMAIN_STATE_LINK_STATUS] = "LINK_STATUS",
+       [XDOMAIN_STATE_LINK_STATE_CHANGE] = "LINK_STATE_CHANGE",
+       [XDOMAIN_STATE_LINK_STATUS2] = "LINK_STATUS2",
+       [XDOMAIN_STATE_BONDING_UUID_LOW] = "BONDING_UUID_LOW",
+       [XDOMAIN_STATE_BONDING_UUID_HIGH] = "BONDING_UUID_HIGH",
+       [XDOMAIN_STATE_PROPERTIES] = "PROPERTIES",
+       [XDOMAIN_STATE_ENUMERATED] = "ENUMERATED",
+       [XDOMAIN_STATE_ERROR] = "ERROR",
+};
+
 struct xdomain_request_work {
        struct work_struct work;
        struct tb_xdp_header *pkg;
@@ -235,7 +260,7 @@ static int tb_xdp_handle_error(const struct tb_xdp_error_response *res)
 }
 
 static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry,
-                              uuid_t *uuid)
+                              uuid_t *uuid, u64 *remote_route)
 {
        struct tb_xdp_uuid_response res;
        struct tb_xdp_uuid req;
@@ -258,6 +283,8 @@ static int tb_xdp_uuid_request(struct tb_ctl *ctl, u64 route, int retry,
                return ret;
 
        uuid_copy(uuid, &res.src_uuid);
+       *remote_route = (u64)res.src_route_hi << 32 | res.src_route_lo;
+
        return 0;
 }
 
@@ -473,6 +500,112 @@ tb_xdp_properties_changed_response(struct tb_ctl *ctl, u64 route, u8 sequence)
                                     TB_CFG_PKG_XDOMAIN_RESP);
 }
 
+static int tb_xdp_link_state_status_request(struct tb_ctl *ctl, u64 route,
+                                           u8 sequence, u8 *slw, u8 *tlw,
+                                           u8 *sls, u8 *tls)
+{
+       struct tb_xdp_link_state_status_response res;
+       struct tb_xdp_link_state_status req;
+       int ret;
+
+       memset(&req, 0, sizeof(req));
+       tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_STATUS_REQUEST,
+                          sizeof(req));
+
+       memset(&res, 0, sizeof(res));
+       ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ,
+                                  &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP,
+                                  XDOMAIN_DEFAULT_TIMEOUT);
+       if (ret)
+               return ret;
+
+       ret = tb_xdp_handle_error(&res.err);
+       if (ret)
+               return ret;
+
+       if (res.status != 0)
+               return -EREMOTEIO;
+
+       *slw = res.slw;
+       *tlw = res.tlw;
+       *sls = res.sls;
+       *tls = res.tls;
+
+       return 0;
+}
+
+static int tb_xdp_link_state_status_response(struct tb *tb, struct tb_ctl *ctl,
+                                            struct tb_xdomain *xd, u8 sequence)
+{
+       struct tb_switch *sw = tb_to_switch(xd->dev.parent);
+       struct tb_xdp_link_state_status_response res;
+       struct tb_port *port = tb_port_at(xd->route, sw);
+       u32 val[2];
+       int ret;
+
+       memset(&res, 0, sizeof(res));
+       tb_xdp_fill_header(&res.hdr, xd->route, sequence,
+                          LINK_STATE_STATUS_RESPONSE, sizeof(res));
+
+       ret = tb_port_read(port, val, TB_CFG_PORT,
+                          port->cap_phy + LANE_ADP_CS_0, ARRAY_SIZE(val));
+       if (ret)
+               return ret;
+
+       res.slw = (val[0] & LANE_ADP_CS_0_SUPPORTED_WIDTH_MASK) >>
+                       LANE_ADP_CS_0_SUPPORTED_WIDTH_SHIFT;
+       res.sls = (val[0] & LANE_ADP_CS_0_SUPPORTED_SPEED_MASK) >>
+                       LANE_ADP_CS_0_SUPPORTED_SPEED_SHIFT;
+       res.tls = val[1] & LANE_ADP_CS_1_TARGET_SPEED_MASK;
+       res.tlw = (val[1] & LANE_ADP_CS_1_TARGET_WIDTH_MASK) >>
+                       LANE_ADP_CS_1_TARGET_WIDTH_SHIFT;
+
+       return __tb_xdomain_response(ctl, &res, sizeof(res),
+                                    TB_CFG_PKG_XDOMAIN_RESP);
+}
+
+static int tb_xdp_link_state_change_request(struct tb_ctl *ctl, u64 route,
+                                           u8 sequence, u8 tlw, u8 tls)
+{
+       struct tb_xdp_link_state_change_response res;
+       struct tb_xdp_link_state_change req;
+       int ret;
+
+       memset(&req, 0, sizeof(req));
+       tb_xdp_fill_header(&req.hdr, route, sequence, LINK_STATE_CHANGE_REQUEST,
+                          sizeof(req));
+       req.tlw = tlw;
+       req.tls = tls;
+
+       memset(&res, 0, sizeof(res));
+       ret = __tb_xdomain_request(ctl, &req, sizeof(req), TB_CFG_PKG_XDOMAIN_REQ,
+                                  &res, sizeof(res), TB_CFG_PKG_XDOMAIN_RESP,
+                                  XDOMAIN_DEFAULT_TIMEOUT);
+       if (ret)
+               return ret;
+
+       ret = tb_xdp_handle_error(&res.err);
+       if (ret)
+               return ret;
+
+       return res.status != 0 ? -EREMOTEIO : 0;
+}
+
+static int tb_xdp_link_state_change_response(struct tb_ctl *ctl, u64 route,
+                                            u8 sequence, u32 status)
+{
+       struct tb_xdp_link_state_change_response res;
+
+       memset(&res, 0, sizeof(res));
+       tb_xdp_fill_header(&res.hdr, route, sequence, LINK_STATE_CHANGE_RESPONSE,
+                          sizeof(res));
+
+       res.status = status;
+
+       return __tb_xdomain_response(ctl, &res, sizeof(res),
+                                    TB_CFG_PKG_XDOMAIN_RESP);
+}
+
 /**
  * tb_register_protocol_handler() - Register protocol handler
  * @handler: Handler to register
@@ -600,14 +733,13 @@ static void tb_xdp_handle_request(struct work_struct *work)
                goto out;
        }
 
-       tb_dbg(tb, "%llx: received XDomain request %#x\n", route, pkg->type);
-
        xd = tb_xdomain_find_by_route_locked(tb, route);
        if (xd)
                update_property_block(xd);
 
        switch (pkg->type) {
        case PROPERTIES_REQUEST:
+               tb_dbg(tb, "%llx: received XDomain properties request\n", route);
                if (xd) {
                        ret = tb_xdp_properties_response(tb, ctl, xd, sequence,
                                (const struct tb_xdp_properties *)pkg);
@@ -615,6 +747,9 @@ static void tb_xdp_handle_request(struct work_struct *work)
                break;
 
        case PROPERTIES_CHANGED_REQUEST:
+               tb_dbg(tb, "%llx: received XDomain properties changed request\n",
+                      route);
+
                ret = tb_xdp_properties_changed_response(ctl, route, sequence);
 
                /*
@@ -622,18 +757,51 @@ static void tb_xdp_handle_request(struct work_struct *work)
                 * the xdomain related to this connection as well in
                 * case there is a change in services it offers.
                 */
-               if (xd && device_is_registered(&xd->dev)) {
-                       queue_delayed_work(tb->wq, &xd->get_properties_work,
-                                          msecs_to_jiffies(50));
-               }
+               if (xd && device_is_registered(&xd->dev))
+                       queue_delayed_work(tb->wq, &xd->state_work,
+                                          msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
                break;
 
        case UUID_REQUEST_OLD:
        case UUID_REQUEST:
+               tb_dbg(tb, "%llx: received XDomain UUID request\n", route);
                ret = tb_xdp_uuid_response(ctl, route, sequence, uuid);
                break;
 
+       case LINK_STATE_STATUS_REQUEST:
+               tb_dbg(tb, "%llx: received XDomain link state status request\n",
+                      route);
+
+               if (xd) {
+                       ret = tb_xdp_link_state_status_response(tb, ctl, xd,
+                                                               sequence);
+               } else {
+                       tb_xdp_error_response(ctl, route, sequence,
+                                             ERROR_NOT_READY);
+               }
+               break;
+
+       case LINK_STATE_CHANGE_REQUEST:
+               tb_dbg(tb, "%llx: received XDomain link state change request\n",
+                      route);
+
+               if (xd && xd->state == XDOMAIN_STATE_BONDING_UUID_HIGH) {
+                       const struct tb_xdp_link_state_change *lsc =
+                               (const struct tb_xdp_link_state_change *)pkg;
+
+                       ret = tb_xdp_link_state_change_response(ctl, route,
+                                                               sequence, 0);
+                       xd->target_link_width = lsc->tlw;
+                       queue_delayed_work(tb->wq, &xd->state_work,
+                                          msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
+               } else {
+                       tb_xdp_error_response(ctl, route, sequence,
+                                             ERROR_NOT_READY);
+               }
+               break;
+
        default:
+               tb_dbg(tb, "%llx: unknown XDomain request %#x\n", route, pkg->type);
                tb_xdp_error_response(ctl, route, sequence,
                                      ERROR_NOT_SUPPORTED);
                break;
@@ -1000,32 +1168,38 @@ static int tb_xdomain_update_link_attributes(struct tb_xdomain *xd)
        return 0;
 }
 
-static void tb_xdomain_get_uuid(struct work_struct *work)
+static int tb_xdomain_get_uuid(struct tb_xdomain *xd)
 {
-       struct tb_xdomain *xd = container_of(work, typeof(*xd),
-                                            get_uuid_work.work);
        struct tb *tb = xd->tb;
        uuid_t uuid;
+       u64 route;
        int ret;
 
        dev_dbg(&xd->dev, "requesting remote UUID\n");
 
-       ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->uuid_retries, &uuid);
+       ret = tb_xdp_uuid_request(tb->ctl, xd->route, xd->state_retries, &uuid,
+                                 &route);
        if (ret < 0) {
-               if (xd->uuid_retries-- > 0) {
+               if (xd->state_retries-- > 0) {
                        dev_dbg(&xd->dev, "failed to request UUID, retrying\n");
-                       queue_delayed_work(xd->tb->wq, &xd->get_uuid_work,
-                                          msecs_to_jiffies(100));
+                       return -EAGAIN;
                } else {
                        dev_dbg(&xd->dev, "failed to read remote UUID\n");
                }
-               return;
+               return ret;
        }
 
        dev_dbg(&xd->dev, "got remote UUID %pUb\n", &uuid);
 
-       if (uuid_equal(&uuid, xd->local_uuid))
-               dev_dbg(&xd->dev, "intra-domain loop detected\n");
+       if (uuid_equal(&uuid, xd->local_uuid)) {
+               if (route == xd->route)
+                       dev_dbg(&xd->dev, "loop back detected\n");
+               else
+                       dev_dbg(&xd->dev, "intra-domain loop detected\n");
+
+               /* Don't bond lanes automatically for loops */
+               xd->bonding_possible = false;
+       }
 
        /*
         * If the UUID is different, there is another domain connected
@@ -1035,27 +1209,152 @@ static void tb_xdomain_get_uuid(struct work_struct *work)
        if (xd->remote_uuid && !uuid_equal(&uuid, xd->remote_uuid)) {
                dev_dbg(&xd->dev, "remote UUID is different, unplugging\n");
                xd->is_unplugged = true;
-               return;
+               return -ENODEV;
        }
 
        /* First time fill in the missing UUID */
        if (!xd->remote_uuid) {
                xd->remote_uuid = kmemdup(&uuid, sizeof(uuid_t), GFP_KERNEL);
                if (!xd->remote_uuid)
-                       return;
+                       return -ENOMEM;
        }
 
-       /* Now we can start the normal properties exchange */
-       queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-                          msecs_to_jiffies(100));
-       queue_delayed_work(xd->tb->wq, &xd->get_properties_work,
-                          msecs_to_jiffies(1000));
+       return 0;
 }
 
-static void tb_xdomain_get_properties(struct work_struct *work)
+static int tb_xdomain_get_link_status(struct tb_xdomain *xd)
+{
+       struct tb *tb = xd->tb;
+       u8 slw, tlw, sls, tls;
+       int ret;
+
+       dev_dbg(&xd->dev, "sending link state status request to %pUb\n",
+               xd->remote_uuid);
+
+       ret = tb_xdp_link_state_status_request(tb->ctl, xd->route,
+                                              xd->state_retries, &slw, &tlw, &sls,
+                                              &tls);
+       if (ret) {
+               if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) {
+                       dev_dbg(&xd->dev,
+                               "failed to request remote link status, retrying\n");
+                       return -EAGAIN;
+               }
+               dev_dbg(&xd->dev, "failed to receive remote link status\n");
+               return ret;
+       }
+
+       dev_dbg(&xd->dev, "remote link supports width %#x speed %#x\n", slw, sls);
+
+       if (slw < LANE_ADP_CS_0_SUPPORTED_WIDTH_DUAL) {
+               dev_dbg(&xd->dev, "remote adapter is single lane only\n");
+               return -EOPNOTSUPP;
+       }
+
+       return 0;
+}
+
+static int tb_xdomain_link_state_change(struct tb_xdomain *xd,
+                                       unsigned int width)
+{
+       struct tb_switch *sw = tb_to_switch(xd->dev.parent);
+       struct tb_port *port = tb_port_at(xd->route, sw);
+       struct tb *tb = xd->tb;
+       u8 tlw, tls;
+       u32 val;
+       int ret;
+
+       if (width == 2)
+               tlw = LANE_ADP_CS_1_TARGET_WIDTH_DUAL;
+       else if (width == 1)
+               tlw = LANE_ADP_CS_1_TARGET_WIDTH_SINGLE;
+       else
+               return -EINVAL;
+
+       /* Use the current target speed */
+       ret = tb_port_read(port, &val, TB_CFG_PORT, port->cap_phy + LANE_ADP_CS_1, 1);
+       if (ret)
+               return ret;
+       tls = val & LANE_ADP_CS_1_TARGET_SPEED_MASK;
+
+       dev_dbg(&xd->dev, "sending link state change request with width %#x speed %#x\n",
+               tlw, tls);
+
+       ret = tb_xdp_link_state_change_request(tb->ctl, xd->route,
+                                              xd->state_retries, tlw, tls);
+       if (ret) {
+               if (ret != -EOPNOTSUPP && xd->state_retries-- > 0) {
+                       dev_dbg(&xd->dev,
+                               "failed to change remote link state, retrying\n");
+                       return -EAGAIN;
+               }
+               dev_err(&xd->dev, "failed request link state change, aborting\n");
+               return ret;
+       }
+
+       dev_dbg(&xd->dev, "received link state change response\n");
+       return 0;
+}
+
+static int tb_xdomain_bond_lanes_uuid_high(struct tb_xdomain *xd)
+{
+       struct tb_port *port;
+       int ret, width;
+
+       if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_SINGLE) {
+               width = 1;
+       } else if (xd->target_link_width == LANE_ADP_CS_1_TARGET_WIDTH_DUAL) {
+               width = 2;
+       } else {
+               if (xd->state_retries-- > 0) {
+                       dev_dbg(&xd->dev,
+                               "link state change request not received yet, retrying\n");
+                       return -EAGAIN;
+               }
+               dev_dbg(&xd->dev, "timeout waiting for link change request\n");
+               return -ETIMEDOUT;
+       }
+
+       port = tb_port_at(xd->route, tb_xdomain_parent(xd));
+
+       /*
+        * We can't use tb_xdomain_lane_bonding_enable() here because it
+        * is the other side that initiates lane bonding. So here we
+        * just set the width to both lane adapters and wait for the
+        * link to transition bonded.
+        */
+       ret = tb_port_set_link_width(port->dual_link_port, width);
+       if (ret) {
+               tb_port_warn(port->dual_link_port,
+                            "failed to set link width to %d\n", width);
+               return ret;
+       }
+
+       ret = tb_port_set_link_width(port, width);
+       if (ret) {
+               tb_port_warn(port, "failed to set link width to %d\n", width);
+               return ret;
+       }
+
+       ret = tb_port_wait_for_link_width(port, width, XDOMAIN_BONDING_TIMEOUT);
+       if (ret) {
+               dev_warn(&xd->dev, "error waiting for link width to become %d\n",
+                        width);
+               return ret;
+       }
+
+       port->bonded = width == 2;
+       port->dual_link_port->bonded = width == 2;
+
+       tb_port_update_credits(port);
+       tb_xdomain_update_link_attributes(xd);
+
+       dev_dbg(&xd->dev, "lane bonding %sabled\n", width == 2 ? "en" : "dis");
+       return 0;
+}
+
+static int tb_xdomain_get_properties(struct tb_xdomain *xd)
 {
-       struct tb_xdomain *xd = container_of(work, typeof(*xd),
-                                            get_properties_work.work);
        struct tb_property_dir *dir;
        struct tb *tb = xd->tb;
        bool update = false;
@@ -1066,34 +1365,35 @@ static void tb_xdomain_get_properties(struct work_struct *work)
        dev_dbg(&xd->dev, "requesting remote properties\n");
 
        ret = tb_xdp_properties_request(tb->ctl, xd->route, xd->local_uuid,
-                                       xd->remote_uuid, xd->properties_retries,
+                                       xd->remote_uuid, xd->state_retries,
                                        &block, &gen);
        if (ret < 0) {
-               if (xd->properties_retries-- > 0) {
+               if (xd->state_retries-- > 0) {
                        dev_dbg(&xd->dev,
                                "failed to request remote properties, retrying\n");
-                       queue_delayed_work(xd->tb->wq, &xd->get_properties_work,
-                                          msecs_to_jiffies(1000));
+                       return -EAGAIN;
                } else {
                        /* Give up now */
                        dev_err(&xd->dev,
                                "failed read XDomain properties from %pUb\n",
                                xd->remote_uuid);
                }
-               return;
-       }
 
-       xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES;
+               return ret;
+       }
 
        mutex_lock(&xd->lock);
 
        /* Only accept newer generation properties */
-       if (xd->remote_properties && gen <= xd->remote_property_block_gen)
+       if (xd->remote_properties && gen <= xd->remote_property_block_gen) {
+               ret = 0;
                goto err_free_block;
+       }
 
        dir = tb_property_parse_dir(block, ret);
        if (!dir) {
                dev_err(&xd->dev, "failed to parse XDomain properties\n");
+               ret = -ENOMEM;
                goto err_free_block;
        }
 
@@ -1124,9 +1424,16 @@ static void tb_xdomain_get_properties(struct work_struct *work)
         * registered, we notify the userspace that it has changed.
         */
        if (!update) {
+               struct tb_port *port;
+
+               /* Now disable lane 1 if bonding was not enabled */
+               port = tb_port_at(xd->route, tb_xdomain_parent(xd));
+               if (!port->bonded)
+                       tb_port_disable(port->dual_link_port);
+
                if (device_add(&xd->dev)) {
                        dev_err(&xd->dev, "failed to add XDomain device\n");
-                       return;
+                       return -ENODEV;
                }
                dev_info(&xd->dev, "new host found, vendor=%#x device=%#x\n",
                         xd->vendor, xd->device);
@@ -1138,13 +1445,193 @@ static void tb_xdomain_get_properties(struct work_struct *work)
        }
 
        enumerate_services(xd);
-       return;
+       return 0;
 
 err_free_dir:
        tb_property_free_dir(dir);
 err_free_block:
        kfree(block);
        mutex_unlock(&xd->lock);
+
+       return ret;
+}
+
+static void tb_xdomain_queue_uuid(struct tb_xdomain *xd)
+{
+       xd->state = XDOMAIN_STATE_UUID;
+       xd->state_retries = XDOMAIN_RETRIES;
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
+}
+
+static void tb_xdomain_queue_link_status(struct tb_xdomain *xd)
+{
+       xd->state = XDOMAIN_STATE_LINK_STATUS;
+       xd->state_retries = XDOMAIN_RETRIES;
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
+}
+
+static void tb_xdomain_queue_link_status2(struct tb_xdomain *xd)
+{
+       xd->state = XDOMAIN_STATE_LINK_STATUS2;
+       xd->state_retries = XDOMAIN_RETRIES;
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
+}
+
+static void tb_xdomain_queue_bonding(struct tb_xdomain *xd)
+{
+       if (memcmp(xd->local_uuid, xd->remote_uuid, UUID_SIZE) > 0) {
+               dev_dbg(&xd->dev, "we have higher UUID, other side bonds the lanes\n");
+               xd->state = XDOMAIN_STATE_BONDING_UUID_HIGH;
+       } else {
+               dev_dbg(&xd->dev, "we have lower UUID, bonding lanes\n");
+               xd->state = XDOMAIN_STATE_LINK_STATE_CHANGE;
+       }
+
+       xd->state_retries = XDOMAIN_RETRIES;
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
+}
+
+static void tb_xdomain_queue_bonding_uuid_low(struct tb_xdomain *xd)
+{
+       xd->state = XDOMAIN_STATE_BONDING_UUID_LOW;
+       xd->state_retries = XDOMAIN_RETRIES;
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
+}
+
+static void tb_xdomain_queue_properties(struct tb_xdomain *xd)
+{
+       xd->state = XDOMAIN_STATE_PROPERTIES;
+       xd->state_retries = XDOMAIN_RETRIES;
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
+}
+
+static void tb_xdomain_queue_properties_changed(struct tb_xdomain *xd)
+{
+       xd->properties_changed_retries = XDOMAIN_RETRIES;
+       queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
+                          msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
+}
+
+static void tb_xdomain_state_work(struct work_struct *work)
+{
+       struct tb_xdomain *xd = container_of(work, typeof(*xd), state_work.work);
+       int ret, state = xd->state;
+
+       if (WARN_ON_ONCE(state < XDOMAIN_STATE_INIT ||
+                        state > XDOMAIN_STATE_ERROR))
+               return;
+
+       dev_dbg(&xd->dev, "running state %s\n", state_names[state]);
+
+       switch (state) {
+       case XDOMAIN_STATE_INIT:
+               if (xd->needs_uuid) {
+                       tb_xdomain_queue_uuid(xd);
+               } else {
+                       tb_xdomain_queue_properties_changed(xd);
+                       tb_xdomain_queue_properties(xd);
+               }
+               break;
+
+       case XDOMAIN_STATE_UUID:
+               ret = tb_xdomain_get_uuid(xd);
+               if (ret) {
+                       if (ret == -EAGAIN)
+                               goto retry_state;
+                       xd->state = XDOMAIN_STATE_ERROR;
+               } else {
+                       tb_xdomain_queue_properties_changed(xd);
+                       if (xd->bonding_possible)
+                               tb_xdomain_queue_link_status(xd);
+                       else
+                               tb_xdomain_queue_properties(xd);
+               }
+               break;
+
+       case XDOMAIN_STATE_LINK_STATUS:
+               ret = tb_xdomain_get_link_status(xd);
+               if (ret) {
+                       if (ret == -EAGAIN)
+                               goto retry_state;
+
+                       /*
+                        * If any of the lane bonding states fail we skip
+                        * bonding completely and try to continue from
+                        * reading properties.
+                        */
+                       tb_xdomain_queue_properties(xd);
+               } else {
+                       tb_xdomain_queue_bonding(xd);
+               }
+               break;
+
+       case XDOMAIN_STATE_LINK_STATE_CHANGE:
+               ret = tb_xdomain_link_state_change(xd, 2);
+               if (ret) {
+                       if (ret == -EAGAIN)
+                               goto retry_state;
+                       tb_xdomain_queue_properties(xd);
+               } else {
+                       tb_xdomain_queue_link_status2(xd);
+               }
+               break;
+
+       case XDOMAIN_STATE_LINK_STATUS2:
+               ret = tb_xdomain_get_link_status(xd);
+               if (ret) {
+                       if (ret == -EAGAIN)
+                               goto retry_state;
+                       tb_xdomain_queue_properties(xd);
+               } else {
+                       tb_xdomain_queue_bonding_uuid_low(xd);
+               }
+               break;
+
+       case XDOMAIN_STATE_BONDING_UUID_LOW:
+               tb_xdomain_lane_bonding_enable(xd);
+               tb_xdomain_queue_properties(xd);
+               break;
+
+       case XDOMAIN_STATE_BONDING_UUID_HIGH:
+               if (tb_xdomain_bond_lanes_uuid_high(xd) == -EAGAIN)
+                       goto retry_state;
+               tb_xdomain_queue_properties(xd);
+               break;
+
+       case XDOMAIN_STATE_PROPERTIES:
+               ret = tb_xdomain_get_properties(xd);
+               if (ret) {
+                       if (ret == -EAGAIN)
+                               goto retry_state;
+                       xd->state = XDOMAIN_STATE_ERROR;
+               } else {
+                       xd->state = XDOMAIN_STATE_ENUMERATED;
+               }
+               break;
+
+       case XDOMAIN_STATE_ENUMERATED:
+               tb_xdomain_queue_properties(xd);
+               break;
+
+       case XDOMAIN_STATE_ERROR:
+               break;
+
+       default:
+               dev_warn(&xd->dev, "unexpected state %d\n", state);
+               break;
+       }
+
+       return;
+
+retry_state:
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
 }
 
 static void tb_xdomain_properties_changed(struct work_struct *work)
@@ -1163,13 +1650,13 @@ static void tb_xdomain_properties_changed(struct work_struct *work)
                                "failed to send properties changed notification, retrying\n");
                        queue_delayed_work(xd->tb->wq,
                                           &xd->properties_changed_work,
-                                          msecs_to_jiffies(1000));
+                                          msecs_to_jiffies(XDOMAIN_DEFAULT_TIMEOUT));
                }
                dev_err(&xd->dev, "failed to send properties changed notification\n");
                return;
        }
 
-       xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES;
+       xd->properties_changed_retries = XDOMAIN_RETRIES;
 }
 
 static ssize_t device_show(struct device *dev, struct device_attribute *attr,
@@ -1304,31 +1791,17 @@ static void tb_xdomain_release(struct device *dev)
 
 static void start_handshake(struct tb_xdomain *xd)
 {
-       xd->uuid_retries = XDOMAIN_UUID_RETRIES;
-       xd->properties_retries = XDOMAIN_PROPERTIES_RETRIES;
-       xd->properties_changed_retries = XDOMAIN_PROPERTIES_CHANGED_RETRIES;
-
-       if (xd->needs_uuid) {
-               queue_delayed_work(xd->tb->wq, &xd->get_uuid_work,
-                                  msecs_to_jiffies(100));
-       } else {
-               /* Start exchanging properties with the other host */
-               queue_delayed_work(xd->tb->wq, &xd->properties_changed_work,
-                                  msecs_to_jiffies(100));
-               queue_delayed_work(xd->tb->wq, &xd->get_properties_work,
-                                  msecs_to_jiffies(1000));
-       }
+       xd->state = XDOMAIN_STATE_INIT;
+       queue_delayed_work(xd->tb->wq, &xd->state_work,
+                          msecs_to_jiffies(XDOMAIN_SHORT_TIMEOUT));
 }
 
 static void stop_handshake(struct tb_xdomain *xd)
 {
-       xd->uuid_retries = 0;
-       xd->properties_retries = 0;
-       xd->properties_changed_retries = 0;
-
-       cancel_delayed_work_sync(&xd->get_uuid_work);
-       cancel_delayed_work_sync(&xd->get_properties_work);
        cancel_delayed_work_sync(&xd->properties_changed_work);
+       cancel_delayed_work_sync(&xd->state_work);
+       xd->properties_changed_retries = 0;
+       xd->state_retries = 0;
 }
 
 static int __maybe_unused tb_xdomain_suspend(struct device *dev)
@@ -1389,8 +1862,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent,
        ida_init(&xd->in_hopids);
        ida_init(&xd->out_hopids);
        mutex_init(&xd->lock);
-       INIT_DELAYED_WORK(&xd->get_uuid_work, tb_xdomain_get_uuid);
-       INIT_DELAYED_WORK(&xd->get_properties_work, tb_xdomain_get_properties);
+       INIT_DELAYED_WORK(&xd->state_work, tb_xdomain_state_work);
        INIT_DELAYED_WORK(&xd->properties_changed_work,
                          tb_xdomain_properties_changed);
 
@@ -1405,6 +1877,7 @@ struct tb_xdomain *tb_xdomain_alloc(struct tb *tb, struct device *parent,
                        goto err_free_local_uuid;
        } else {
                xd->needs_uuid = true;
+               xd->bonding_possible = !!down->dual_link_port;
        }
 
        device_initialize(&xd->dev);
@@ -1523,9 +1996,9 @@ int tb_xdomain_lane_bonding_enable(struct tb_xdomain *xd)
                return ret;
        }
 
-       ret = tb_port_wait_for_link_width(port, 2, 100);
+       ret = tb_port_wait_for_link_width(port, 2, XDOMAIN_BONDING_TIMEOUT);
        if (ret) {
-               tb_port_warn(port, "timeout enabling lane bonding\n");
+               tb_port_warn(port, "failed to enable lane bonding\n");
                return ret;
        }
 
index e3a49d8..3622171 100644 (file)
@@ -1091,7 +1091,7 @@ int usbatm_usb_probe(struct usb_interface *intf, const struct usb_device_id *id,
                        snd_buf_bytes - (snd_buf_bytes % instance->tx_channel.stride));
 
        /* rx buffer size must be a positive multiple of the endpoint maxpacket */
-       maxpacket = usb_maxpacket(usb_dev, instance->rx_channel.endpoint, 0);
+       maxpacket = usb_maxpacket(usb_dev, instance->rx_channel.endpoint);
 
        if ((maxpacket < 1) || (maxpacket > UDSL_MAX_BUF_SIZE)) {
                dev_err(dev, "%s: invalid endpoint %02x!\n", __func__,
index 53838e7..6db5cb1 100644 (file)
@@ -189,14 +189,12 @@ static int c67x00_drv_remove(struct platform_device *pdev)
        c67x00_ll_release(c67x00);
 
        res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (res)
-               free_irq(res->start, c67x00);
+       free_irq(res->start, c67x00);
 
        iounmap(c67x00->hpi.base);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
-               release_mem_region(res->start, resource_size(res));
+       release_mem_region(res->start, resource_size(res));
 
        kfree(c67x00);
 
index c7d3e90..a09fa68 100644 (file)
@@ -655,7 +655,7 @@ static int c67x00_add_data_urb(struct c67x00_hcd *c67x00, struct urb *urb)
                               usb_pipeout(urb->pipe));
        remaining = urb->transfer_buffer_length - urb->actual_length;
 
-       maxps = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+       maxps = usb_maxpacket(urb->dev, urb->pipe);
 
        need_empty = (urb->transfer_flags & URB_ZERO_PACKET) &&
            usb_pipeout(urb->pipe) && !(remaining % maxps);
@@ -866,7 +866,7 @@ static inline int c67x00_end_of_data(struct c67x00_td *td)
        if (unlikely(!act_bytes))
                return 1;       /* This was an empty packet */
 
-       maxps = usb_maxpacket(td_udev(td), td->pipe, usb_pipeout(td->pipe));
+       maxps = usb_maxpacket(td_udev(td), td->pipe);
 
        if (unlikely(act_bytes < maxps))
                return 1;       /* Smaller then full packet */
index d6d515d..5c15c48 100644 (file)
@@ -2038,7 +2038,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
        u8 mult = 0;
        int ret;
 
-       buffering = CDNS3_EP_BUF_SIZE - 1;
+       buffering = priv_dev->ep_buf_size - 1;
 
        cdns3_configure_dmult(priv_dev, priv_ep);
 
@@ -2057,7 +2057,7 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
                break;
        default:
                ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
-               mult = CDNS3_EP_ISO_HS_MULT - 1;
+               mult = priv_dev->ep_iso_burst - 1;
                buffering = mult + 1;
        }
 
@@ -2073,14 +2073,14 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
                mult = 0;
                max_packet_size = 1024;
                if (priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
-                       maxburst = CDNS3_EP_ISO_SS_BURST - 1;
+                       maxburst = priv_dev->ep_iso_burst - 1;
                        buffering = (mult + 1) *
                                    (maxburst + 1);
 
                        if (priv_ep->interval > 1)
                                buffering++;
                } else {
-                       maxburst = CDNS3_EP_BUF_SIZE - 1;
+                       maxburst = priv_dev->ep_buf_size - 1;
                }
                break;
        default:
@@ -2095,6 +2095,10 @@ int cdns3_ep_config(struct cdns3_endpoint *priv_ep, bool enable)
        else
                priv_ep->trb_burst_size = 16;
 
+       mult = min_t(u8, mult, EP_CFG_MULT_MAX);
+       buffering = min_t(u8, buffering, EP_CFG_BUFFERING_MAX);
+       maxburst = min_t(u8, maxburst, EP_CFG_MAXBURST_MAX);
+
        /* onchip buffer is only allocated before configuration */
        if (!priv_dev->hw_configured_flag) {
                ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1,
@@ -2961,6 +2965,40 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
        return 0;
 }
 
+/**
+ * cdns3_gadget_check_config - ensure cdns3 can support the USB configuration
+ * @gadget: pointer to the USB gadget
+ *
+ * Used to record the maximum number of endpoints being used in a USB composite
+ * device. (across all configurations)  This is to be used in the calculation
+ * of the TXFIFO sizes when resizing internal memory for individual endpoints.
+ * It will help ensured that the resizing logic reserves enough space for at
+ * least one max packet.
+ */
+static int cdns3_gadget_check_config(struct usb_gadget *gadget)
+{
+       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+       struct usb_ep *ep;
+       int n_in = 0;
+       int total;
+
+       list_for_each_entry(ep, &gadget->ep_list, ep_list) {
+               if (ep->claimed && (ep->address & USB_DIR_IN))
+                       n_in++;
+       }
+
+       /* 2KB are reserved for EP0, 1KB for out*/
+       total = 2 + n_in + 1;
+
+       if (total > priv_dev->onchip_buffers)
+               return -ENOMEM;
+
+       priv_dev->ep_buf_size = priv_dev->ep_iso_burst =
+                       (priv_dev->onchip_buffers - 2) / (n_in + 1);
+
+       return 0;
+}
+
 static const struct usb_gadget_ops cdns3_gadget_ops = {
        .get_frame = cdns3_gadget_get_frame,
        .wakeup = cdns3_gadget_wakeup,
@@ -2969,6 +3007,7 @@ static const struct usb_gadget_ops cdns3_gadget_ops = {
        .udc_start = cdns3_gadget_udc_start,
        .udc_stop = cdns3_gadget_udc_stop,
        .match_ep = cdns3_gadget_match_ep,
+       .check_config = cdns3_gadget_check_config,
 };
 
 static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
index c5660f2..fbe4a8e 100644 (file)
@@ -562,15 +562,18 @@ struct cdns3_usb_regs {
 /* Max burst size (used only in SS mode). */
 #define EP_CFG_MAXBURST_MASK   GENMASK(11, 8)
 #define EP_CFG_MAXBURST(p)     (((p) << 8) & EP_CFG_MAXBURST_MASK)
+#define EP_CFG_MAXBURST_MAX    15
 /* ISO max burst. */
 #define EP_CFG_MULT_MASK       GENMASK(15, 14)
 #define EP_CFG_MULT(p)         (((p) << 14) & EP_CFG_MULT_MASK)
+#define EP_CFG_MULT_MAX                2
 /* ISO max burst. */
 #define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16)
 #define EP_CFG_MAXPKTSIZE(p)   (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
 /* Max number of buffered packets. */
 #define EP_CFG_BUFFERING_MASK  GENMASK(31, 27)
 #define EP_CFG_BUFFERING(p)    (((p) << 27) & EP_CFG_BUFFERING_MASK)
+#define EP_CFG_BUFFERING_MAX   15
 
 /* EP_CMD - bitmasks */
 /* Endpoint reset. */
@@ -1094,9 +1097,6 @@ struct cdns3_trb {
 #define CDNS3_ENDPOINTS_MAX_COUNT      32
 #define CDNS3_EP_ZLP_BUF_SIZE          1024
 
-#define CDNS3_EP_BUF_SIZE              4       /* KB */
-#define CDNS3_EP_ISO_HS_MULT           3
-#define CDNS3_EP_ISO_SS_BURST          3
 #define CDNS3_MAX_NUM_DESCMISS_BUF     32
 #define CDNS3_DESCMIS_BUF_SIZE         2048    /* Bytes */
 #define CDNS3_WA2_NUM_BUFFERS          128
@@ -1333,6 +1333,9 @@ struct cdns3_device {
        /*in KB */
        u16                             onchip_buffers;
        u16                             onchip_used_size;
+
+       u16                             ep_buf_size;
+       u16                             ep_iso_burst;
 };
 
 void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
index d8b0041..2c14a96 100644 (file)
@@ -228,8 +228,6 @@ static char *usb_dump_interface(int speed, char *start, char *end,
 
        start = usb_dump_interface_descriptor(start, end, intfc, iface, setno);
        for (i = 0; i < desc->desc.bNumEndpoints; i++) {
-               if (start > end)
-                       return start;
                start = usb_dump_endpoint_descriptor(speed,
                                start, end, &desc->endpoint[i].desc);
        }
@@ -302,8 +300,6 @@ static char *usb_dump_config(int speed, char *start, char *end,
                intfc = config->intf_cache[i];
                interface = config->interface[i];
                for (j = 0; j < intfc->num_altsetting; j++) {
-                       if (start > end)
-                               return start;
                        start = usb_dump_interface(speed,
                                start, end, intfc, interface, j);
                }
@@ -369,19 +365,11 @@ static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
 {
        int i;
 
-       if (start > end)
-               return start;
-
        start = usb_dump_device_descriptor(start, end, &dev->descriptor);
 
-       if (start > end)
-               return start;
-
        start = usb_dump_device_strings(start, end, dev);
 
        for (i = 0; i < dev->descriptor.bNumConfigurations; i++) {
-               if (start > end)
-                       return start;
                start = usb_dump_config(dev->speed,
                                start, end, dev->config + i,
                                /* active ? */
@@ -390,41 +378,6 @@ static char *usb_dump_desc(char *start, char *end, struct usb_device *dev)
        return start;
 }
 
-
-#ifdef PROC_EXTRA /* TBD: may want to add this code later */
-
-static char *usb_dump_hub_descriptor(char *start, char *end,
-                                    const struct usb_hub_descriptor *desc)
-{
-       int leng = USB_DT_HUB_NONVAR_SIZE;
-       unsigned char *ptr = (unsigned char *)desc;
-
-       if (start > end)
-               return start;
-       start += sprintf(start, "Interface:");
-       while (leng && start <= end) {
-               start += sprintf(start, " %02x", *ptr);
-               ptr++; leng--;
-       }
-       *start++ = '\n';
-       return start;
-}
-
-static char *usb_dump_string(char *start, char *end,
-                            const struct usb_device *dev, char *id, int index)
-{
-       if (start > end)
-               return start;
-       start += sprintf(start, "Interface:");
-       if (index <= dev->maxstring && dev->stringindex &&
-           dev->stringindex[index])
-               start += sprintf(start, "%s: %.100s ", id,
-                                dev->stringindex[index]);
-       return start;
-}
-
-#endif /* PROC_EXTRA */
-
 /*****************************************************************/
 
 /* This is a recursive function. Parameters:
index 355ed33..b87452e 100644 (file)
@@ -1533,22 +1533,23 @@ static void choose_wakeup(struct usb_device *udev, pm_message_t msg)
 {
        int     w;
 
-       /* Remote wakeup is needed only when we actually go to sleep.
-        * For things like FREEZE and QUIESCE, if the device is already
-        * autosuspended then its current wakeup setting is okay.
+       /*
+        * For FREEZE/QUIESCE, disable remote wakeups so no interrupts get
+        * generated.
         */
        if (msg.event == PM_EVENT_FREEZE || msg.event == PM_EVENT_QUIESCE) {
-               if (udev->state != USB_STATE_SUSPENDED)
-                       udev->do_remote_wakeup = 0;
-               return;
-       }
+               w = 0;
 
-       /* Enable remote wakeup if it is allowed, even if no interface drivers
-        * actually want it.
-        */
-       w = device_may_wakeup(&udev->dev);
+       } else {
+               /*
+                * Enable remote wakeup if it is allowed, even if no interface
+                * drivers actually want it.
+                */
+               w = device_may_wakeup(&udev->dev);
+       }
 
-       /* If the device is autosuspended with the wrong wakeup setting,
+       /*
+        * If the device is autosuspended with the wrong wakeup setting,
         * autoresume now so the setting can be changed.
         */
        if (udev->state == USB_STATE_SUSPENDED && w != udev->do_remote_wakeup)
index 8176bc8..482dae7 100644 (file)
@@ -15,7 +15,6 @@
 #ifdef CONFIG_PPC_PMAC
 #include <asm/machdep.h>
 #include <asm/pmac_feature.h>
-#include <asm/prom.h>
 #endif
 
 #include "usb.h"
@@ -616,10 +615,10 @@ const struct dev_pm_ops usb_hcd_pci_pm_ops = {
        .suspend_noirq  = hcd_pci_suspend_noirq,
        .resume_noirq   = hcd_pci_resume_noirq,
        .resume         = hcd_pci_resume,
-       .freeze         = check_root_hub_suspended,
+       .freeze         = hcd_pci_suspend,
        .freeze_noirq   = check_root_hub_suspended,
        .thaw_noirq     = NULL,
-       .thaw           = NULL,
+       .thaw           = hcd_pci_resume,
        .poweroff       = hcd_pci_suspend,
        .poweroff_noirq = hcd_pci_suspend_noirq,
        .restore_noirq  = hcd_pci_resume_noirq,
index d9712c2..06eea88 100644 (file)
@@ -2816,6 +2816,7 @@ int usb_add_hcd(struct usb_hcd *hcd,
 {
        int retval;
        struct usb_device *rhdev;
+       struct usb_hcd *shared_hcd;
 
        if (!hcd->skip_phy_initialization && usb_hcd_is_primary_hcd(hcd)) {
                hcd->phy_roothub = usb_phy_roothub_alloc(hcd->self.sysdev);
@@ -2976,13 +2977,26 @@ int usb_add_hcd(struct usb_hcd *hcd,
                goto err_hcd_driver_start;
        }
 
+       /* starting here, usbcore will pay attention to the shared HCD roothub */
+       shared_hcd = hcd->shared_hcd;
+       if (!usb_hcd_is_primary_hcd(hcd) && shared_hcd && HCD_DEFER_RH_REGISTER(shared_hcd)) {
+               retval = register_root_hub(shared_hcd);
+               if (retval != 0)
+                       goto err_register_root_hub;
+
+               if (shared_hcd->uses_new_polling && HCD_POLL_RH(shared_hcd))
+                       usb_hcd_poll_rh_status(shared_hcd);
+       }
+
        /* starting here, usbcore will pay attention to this root hub */
-       retval = register_root_hub(hcd);
-       if (retval != 0)
-               goto err_register_root_hub;
+       if (!HCD_DEFER_RH_REGISTER(hcd)) {
+               retval = register_root_hub(hcd);
+               if (retval != 0)
+                       goto err_register_root_hub;
 
-       if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
-               usb_hcd_poll_rh_status(hcd);
+               if (hcd->uses_new_polling && HCD_POLL_RH(hcd))
+                       usb_hcd_poll_rh_status(hcd);
+       }
 
        return retval;
 
@@ -3020,6 +3034,7 @@ EXPORT_SYMBOL_GPL(usb_add_hcd);
 void usb_remove_hcd(struct usb_hcd *hcd)
 {
        struct usb_device *rhdev = hcd->self.root_hub;
+       bool rh_registered;
 
        dev_info(hcd->self.controller, "remove, state %x\n", hcd->state);
 
@@ -3030,6 +3045,7 @@ void usb_remove_hcd(struct usb_hcd *hcd)
 
        dev_dbg(hcd->self.controller, "roothub graceful disconnect\n");
        spin_lock_irq (&hcd_root_hub_lock);
+       rh_registered = hcd->rh_registered;
        hcd->rh_registered = 0;
        spin_unlock_irq (&hcd_root_hub_lock);
 
@@ -3039,7 +3055,8 @@ void usb_remove_hcd(struct usb_hcd *hcd)
        cancel_work_sync(&hcd->died_work);
 
        mutex_lock(&usb_bus_idr_lock);
-       usb_disconnect(&rhdev);         /* Sets rhdev to NULL */
+       if (rh_registered)
+               usb_disconnect(&rhdev);         /* Sets rhdev to NULL */
        mutex_unlock(&usb_bus_idr_lock);
 
        /*
index 1460857..68e9121 100644 (file)
@@ -1635,7 +1635,7 @@ static int hub_configure(struct usb_hub *hub,
         * maxpktsize is defined in hcd.c's fake endpoint descriptors
         * to be big enough for at least USB_MAXCHILDREN ports. */
        pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(hdev, pipe);
 
        if (maxp > sizeof(*hub->buffer))
                maxp = sizeof(*hub->buffer);
@@ -5511,7 +5511,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 /* Handle notifying userspace about hub over-current events */
 static void port_over_current_notify(struct usb_port *port_dev)
 {
-       char *envp[3];
+       char *envp[3] = { NULL, NULL, NULL };
        struct device *hub_dev;
        char *port_dev_path;
 
@@ -5528,20 +5528,18 @@ static void port_over_current_notify(struct usb_port *port_dev)
 
        envp[0] = kasprintf(GFP_KERNEL, "OVER_CURRENT_PORT=%s", port_dev_path);
        if (!envp[0])
-               goto exit_path;
+               goto exit;
 
        envp[1] = kasprintf(GFP_KERNEL, "OVER_CURRENT_COUNT=%u",
                        port_dev->over_current_count);
        if (!envp[1])
                goto exit;
 
-       envp[2] = NULL;
        kobject_uevent_env(&hub_dev->kobj, KOBJ_CHANGE, envp);
 
-       kfree(envp[1]);
 exit:
+       kfree(envp[1]);
        kfree(envp[0]);
-exit_path:
        kfree(port_dev_path);
 }
 
index bb1da35..d4dcaef 100644 (file)
@@ -205,8 +205,11 @@ usb_acpi_find_companion_for_device(struct usb_device *udev)
        struct usb_hub *hub;
 
        if (!udev->parent) {
-               /* root hub is only child (_ADR=0) under its parent, the HC */
-               adev = ACPI_COMPANION(udev->dev.parent);
+               /*
+                * root hub is only child (_ADR=0) under its parent, the HC.
+                * sysdev pointer is the HC as seen from firmware.
+                */
+               adev = ACPI_COMPANION(udev->bus->sysdev);
                return acpi_find_child_device(adev, 0, false);
        }
 
index cf0bcd0..dc4fc72 100644 (file)
@@ -1153,6 +1153,7 @@ static void dwc2_set_turnaround_time(struct dwc2_hsotg *hsotg)
 int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
 {
        u32 usbcfg;
+       u32 otgctl;
        int retval = 0;
 
        if ((hsotg->params.speed == DWC2_SPEED_PARAM_FULL ||
@@ -1187,6 +1188,14 @@ int dwc2_phy_init(struct dwc2_hsotg *hsotg, bool select_phy)
                dwc2_writel(hsotg, usbcfg, GUSBCFG);
        }
 
+       if (!hsotg->params.activate_ingenic_overcurrent_detection) {
+               if (dwc2_is_host_mode(hsotg)) {
+                       otgctl = readl(hsotg->regs + GOTGCTL);
+                       otgctl |= GOTGCTL_VBVALOEN | GOTGCTL_VBVALOVAL;
+                       writel(otgctl, hsotg->regs + GOTGCTL);
+               }
+       }
+
        return retval;
 }
 
index 88c337b..0683852 100644 (file)
@@ -426,6 +426,10 @@ enum dwc2_ep0_state {
  *                     detection using GGPIO register.
  *                     0 - Deactivate the external level detection (default)
  *                     1 - Activate the external level detection
+ * @activate_ingenic_overcurrent_detection: Activate Ingenic overcurrent
+ *                     detection.
+ *                     0 - Deactivate the overcurrent detection
+ *                     1 - Activate the overcurrent detection (default)
  * @g_dma:              Enables gadget dma usage (default: autodetect).
  * @g_dma_desc:         Enables gadget descriptor DMA (default: autodetect).
  * @g_rx_fifo_size:    The periodic rx fifo size for the device, in
@@ -494,6 +498,7 @@ struct dwc2_core_params {
        u8 hird_threshold;
        bool activate_stm_fs_transceiver;
        bool activate_stm_id_vb_detection;
+       bool activate_ingenic_overcurrent_detection;
        bool ipg_isoc_en;
        u16 max_packet_count;
        u32 max_transfer_size;
index eee3504..fe2a58c 100644 (file)
@@ -4544,7 +4544,6 @@ static int dwc2_hsotg_udc_start(struct usb_gadget *gadget,
 
        WARN_ON(hsotg->driver);
 
-       driver->driver.bus = NULL;
        hsotg->driver = driver;
        hsotg->gadget.dev.of_node = hsotg->dev->of_node;
        hsotg->gadget.speed = USB_SPEED_UNKNOWN;
index 1306f4e..fdb8a42 100644 (file)
@@ -73,6 +73,47 @@ static void dwc2_set_his_params(struct dwc2_hsotg *hsotg)
        p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
 }
 
+static void dwc2_set_jz4775_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->phy_utmi_width = 16;
+       p->activate_ingenic_overcurrent_detection =
+               !device_property_read_bool(hsotg->dev, "disable-over-current");
+}
+
+static void dwc2_set_x1600_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->host_channels = 16;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->phy_utmi_width = 16;
+       p->activate_ingenic_overcurrent_detection =
+               !device_property_read_bool(hsotg->dev, "disable-over-current");
+}
+
+static void dwc2_set_x2000_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_caps.hnp_support = false;
+       p->speed = DWC2_SPEED_PARAM_HIGH;
+       p->host_rx_fifo_size = 1024;
+       p->host_nperio_tx_fifo_size = 1024;
+       p->host_perio_tx_fifo_size = 1024;
+       p->host_channels = 16;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_UTMI;
+       p->phy_utmi_width = 16;
+       p->activate_ingenic_overcurrent_detection =
+               !device_property_read_bool(hsotg->dev, "disable-over-current");
+}
+
 static void dwc2_set_s3c6400_params(struct dwc2_hsotg *hsotg)
 {
        struct dwc2_core_params *p = &hsotg->params;
@@ -221,7 +262,14 @@ static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
 
 const struct of_device_id dwc2_of_match_table[] = {
        { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
-       { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params  },
+       { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params },
+       { .compatible = "ingenic,jz4775-otg", .data = dwc2_set_jz4775_params },
+       { .compatible = "ingenic,jz4780-otg", .data = dwc2_set_jz4775_params },
+       { .compatible = "ingenic,x1000-otg", .data = dwc2_set_jz4775_params },
+       { .compatible = "ingenic,x1600-otg", .data = dwc2_set_x1600_params },
+       { .compatible = "ingenic,x1700-otg", .data = dwc2_set_x1600_params },
+       { .compatible = "ingenic,x1830-otg", .data = dwc2_set_x1600_params },
+       { .compatible = "ingenic,x2000-otg", .data = dwc2_set_x2000_params },
        { .compatible = "rockchip,rk3066-usb", .data = dwc2_set_rk_params },
        { .compatible = "lantiq,arx100-usb", .data = dwc2_set_ltq_params },
        { .compatible = "lantiq,xrx200-usb", .data = dwc2_set_ltq_params },
index c483f28..cd9a734 100644 (file)
@@ -159,4 +159,13 @@ config USB_DWC3_XILINX
          This driver handles both ZynqMP and Versal SoC operations.
          Say 'Y' or 'M' if you have one such device.
 
+config USB_DWC3_AM62
+       tristate "Texas Instruments AM62 Platforms"
+       depends on ARCH_K3 || COMPILE_TEST
+       default USB_DWC3
+       help
+         Support TI's AM62 platforms with DesignWare Core USB3 IP.
+         The Designware Core USB3 IP is progammed to operate in
+         in USB 2.0 mode only.
+         Say 'Y' or 'M' here if you have one such device
 endif
index 2d499de..9f66bd8 100644 (file)
@@ -42,6 +42,7 @@ endif
 # and allyesconfig builds.
 ##
 
+obj-$(CONFIG_USB_DWC3_AM62)            += dwc3-am62.o
 obj-$(CONFIG_USB_DWC3_OMAP)            += dwc3-omap.o
 obj-$(CONFIG_USB_DWC3_EXYNOS)          += dwc3-exynos.o
 obj-$(CONFIG_USB_DWC3_PCI)             += dwc3-pci.o
index d28cd1a..e027c04 100644 (file)
@@ -23,6 +23,7 @@
 #include <linux/delay.h>
 #include <linux/dma-mapping.h>
 #include <linux/of.h>
+#include <linux/of_graph.h>
 #include <linux/acpi.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/reset.h>
@@ -85,7 +86,7 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
                 * mode. If the controller supports DRD but the dr_mode is not
                 * specified or set to OTG, then set the mode to peripheral.
                 */
-               if (mode == USB_DR_MODE_OTG &&
+               if (mode == USB_DR_MODE_OTG && !dwc->edev &&
                    (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
                     !device_property_read_bool(dwc->dev, "usb-role-switch")) &&
                    !DWC3_VER_IS_PRIOR(DWC3, 330A))
@@ -297,6 +298,7 @@ int dwc3_core_soft_reset(struct dwc3 *dwc)
                        udelay(1);
        } while (--retries);
 
+       dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n");
        return -ETIMEDOUT;
 
 done:
@@ -342,7 +344,6 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
  *             from the default, this will set clock period in DWC3_GUCTL
  *             register.
  * @dwc: Pointer to our controller context structure
- * @ref_clk_per: reference clock period in ns
  */
 static void dwc3_ref_clk_period(struct dwc3 *dwc)
 {
@@ -964,10 +965,8 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc)
                return;
 
        vals = kcalloc(ntype, sizeof(u32), GFP_KERNEL);
-       if (!vals) {
-               dev_err(dev, "Error to get memory\n");
+       if (!vals)
                return;
-       }
 
        /* Get INCR burst type, and parse it */
        ret = device_property_read_u32_array(dev,
@@ -1268,40 +1267,36 @@ static int dwc3_core_get_phy(struct dwc3 *dwc)
 
        if (IS_ERR(dwc->usb2_phy)) {
                ret = PTR_ERR(dwc->usb2_phy);
-               if (ret == -ENXIO || ret == -ENODEV) {
+               if (ret == -ENXIO || ret == -ENODEV)
                        dwc->usb2_phy = NULL;
-               } else {
+               else
                        return dev_err_probe(dev, ret, "no usb2 phy configured\n");
-               }
        }
 
        if (IS_ERR(dwc->usb3_phy)) {
                ret = PTR_ERR(dwc->usb3_phy);
-               if (ret == -ENXIO || ret == -ENODEV) {
+               if (ret == -ENXIO || ret == -ENODEV)
                        dwc->usb3_phy = NULL;
-               } else {
+               else
                        return dev_err_probe(dev, ret, "no usb3 phy configured\n");
-               }
        }
 
        dwc->usb2_generic_phy = devm_phy_get(dev, "usb2-phy");
        if (IS_ERR(dwc->usb2_generic_phy)) {
                ret = PTR_ERR(dwc->usb2_generic_phy);
-               if (ret == -ENOSYS || ret == -ENODEV) {
+               if (ret == -ENOSYS || ret == -ENODEV)
                        dwc->usb2_generic_phy = NULL;
-               } else {
+               else
                        return dev_err_probe(dev, ret, "no usb2 phy configured\n");
-               }
        }
 
        dwc->usb3_generic_phy = devm_phy_get(dev, "usb3-phy");
        if (IS_ERR(dwc->usb3_generic_phy)) {
                ret = PTR_ERR(dwc->usb3_generic_phy);
-               if (ret == -ENOSYS || ret == -ENODEV) {
+               if (ret == -ENOSYS || ret == -ENODEV)
                        dwc->usb3_generic_phy = NULL;
-               } else {
+               else
                        return dev_err_probe(dev, ret, "no usb3 phy configured\n");
-               }
        }
 
        return 0;
@@ -1633,6 +1628,51 @@ static void dwc3_check_params(struct dwc3 *dwc)
        }
 }
 
+static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
+{
+       struct device *dev = dwc->dev;
+       struct device_node *np_phy;
+       struct extcon_dev *edev = NULL;
+       const char *name;
+
+       if (device_property_read_bool(dev, "extcon"))
+               return extcon_get_edev_by_phandle(dev, 0);
+
+       /*
+        * Device tree platforms should get extcon via phandle.
+        * On ACPI platforms, we get the name from a device property.
+        * This device property is for kernel internal use only and
+        * is expected to be set by the glue code.
+        */
+       if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) {
+               edev = extcon_get_extcon_dev(name);
+               if (!edev)
+                       return ERR_PTR(-EPROBE_DEFER);
+
+               return edev;
+       }
+
+       /*
+        * Try to get an extcon device from the USB PHY controller's "port"
+        * node. Check if it has the "port" node first, to avoid printing the
+        * error message from underlying code, as it's a valid case: extcon
+        * device (and "port" node) may be missing in case of "usb-role-switch"
+        * or OTG mode.
+        */
+       np_phy = of_parse_phandle(dev->of_node, "phys", 0);
+       if (of_graph_is_present(np_phy)) {
+               struct device_node *np_conn;
+
+               np_conn = of_graph_get_remote_node(np_phy, -1, -1);
+               if (np_conn)
+                       edev = extcon_find_edev_by_node(np_conn);
+               of_node_put(np_conn);
+       }
+       of_node_put(np_phy);
+
+       return edev;
+}
+
 static int dwc3_probe(struct platform_device *pdev)
 {
        struct device           *dev = &pdev->dev;
@@ -1768,6 +1808,13 @@ static int dwc3_probe(struct platform_device *pdev)
                goto err2;
        }
 
+       dwc->edev = dwc3_get_extcon(dwc);
+       if (IS_ERR(dwc->edev)) {
+               ret = PTR_ERR(dwc->edev);
+               dev_err_probe(dwc->dev, ret, "failed to get extcon\n");
+               goto err3;
+       }
+
        ret = dwc3_get_dr_mode(dwc);
        if (ret)
                goto err3;
index 5c9d467..81c486b 100644 (file)
@@ -1046,6 +1046,7 @@ struct dwc3_scratchpad_array {
  * @tx_thr_num_pkt_prd: periodic ESS transmit packet count
  * @tx_max_burst_prd: max periodic ESS transmit burst size
  * @tx_fifo_resize_max_num: max number of fifos allocated during txfifo resize
+ * @clear_stall_protocol: endpoint number that requires a delayed status phase
  * @hsphy_interface: "utmi" or "ulpi"
  * @connected: true when we're connected to a host, false otherwise
  * @softconnect: true when gadget connect is called, false when disconnect runs
@@ -1266,6 +1267,7 @@ struct dwc3 {
        u8                      tx_thr_num_pkt_prd;
        u8                      tx_max_burst_prd;
        u8                      tx_fifo_resize_max_num;
+       u8                      clear_stall_protocol;
 
        const char              *hsphy_interface;
 
index 8cad9e7..039bf24 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/extcon.h>
-#include <linux/of_graph.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/property.h>
@@ -439,51 +438,6 @@ static int dwc3_drd_notifier(struct notifier_block *nb,
        return NOTIFY_DONE;
 }
 
-static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
-{
-       struct device *dev = dwc->dev;
-       struct device_node *np_phy;
-       struct extcon_dev *edev = NULL;
-       const char *name;
-
-       if (device_property_read_bool(dev, "extcon"))
-               return extcon_get_edev_by_phandle(dev, 0);
-
-       /*
-        * Device tree platforms should get extcon via phandle.
-        * On ACPI platforms, we get the name from a device property.
-        * This device property is for kernel internal use only and
-        * is expected to be set by the glue code.
-        */
-       if (device_property_read_string(dev, "linux,extcon-name", &name) == 0) {
-               edev = extcon_get_extcon_dev(name);
-               if (!edev)
-                       return ERR_PTR(-EPROBE_DEFER);
-
-               return edev;
-       }
-
-       /*
-        * Try to get an extcon device from the USB PHY controller's "port"
-        * node. Check if it has the "port" node first, to avoid printing the
-        * error message from underlying code, as it's a valid case: extcon
-        * device (and "port" node) may be missing in case of "usb-role-switch"
-        * or OTG mode.
-        */
-       np_phy = of_parse_phandle(dev->of_node, "phys", 0);
-       if (of_graph_is_present(np_phy)) {
-               struct device_node *np_conn;
-
-               np_conn = of_graph_get_remote_node(np_phy, -1, -1);
-               if (np_conn)
-                       edev = extcon_find_edev_by_node(np_conn);
-               of_node_put(np_conn);
-       }
-       of_node_put(np_phy);
-
-       return edev;
-}
-
 #if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
 #define ROLE_SWITCH 1
 static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
@@ -588,10 +542,6 @@ int dwc3_drd_init(struct dwc3 *dwc)
            device_property_read_bool(dwc->dev, "usb-role-switch"))
                return dwc3_setup_role_switch(dwc);
 
-       dwc->edev = dwc3_get_extcon(dwc);
-       if (IS_ERR(dwc->edev))
-               return PTR_ERR(dwc->edev);
-
        if (dwc->edev) {
                dwc->edev_nb.notifier_call = dwc3_drd_notifier;
                ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
new file mode 100644 (file)
index 0000000..fea7aca
--- /dev/null
@@ -0,0 +1,332 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * dwc3-am62.c - TI specific Glue layer for AM62 DWC3 USB Controller
+ *
+ * Copyright (C) 2022 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/pinctrl/consumer.h>
+
+/* USB WRAPPER register offsets */
+#define USBSS_PID                      0x0
+#define USBSS_OVERCURRENT_CTRL         0x4
+#define USBSS_PHY_CONFIG               0x8
+#define USBSS_PHY_TEST                 0xc
+#define USBSS_CORE_STAT                        0x14
+#define USBSS_HOST_VBUS_CTRL           0x18
+#define USBSS_MODE_CONTROL             0x1c
+#define USBSS_WAKEUP_CONFIG            0x30
+#define USBSS_WAKEUP_STAT              0x34
+#define USBSS_OVERRIDE_CONFIG          0x38
+#define USBSS_IRQ_MISC_STATUS_RAW      0x430
+#define USBSS_IRQ_MISC_STATUS          0x434
+#define USBSS_IRQ_MISC_ENABLE_SET      0x438
+#define USBSS_IRQ_MISC_ENABLE_CLR      0x43c
+#define USBSS_IRQ_MISC_EOI             0x440
+#define USBSS_INTR_TEST                        0x490
+#define USBSS_VBUS_FILTER              0x614
+#define USBSS_VBUS_STAT                        0x618
+#define USBSS_DEBUG_CFG                        0x708
+#define USBSS_DEBUG_DATA               0x70c
+#define USBSS_HOST_HUB_CTRL            0x714
+
+/* PHY CONFIG register bits */
+#define USBSS_PHY_VBUS_SEL_MASK                GENMASK(2, 1)
+#define USBSS_PHY_VBUS_SEL_SHIFT       1
+#define USBSS_PHY_LANE_REVERSE         BIT(0)
+
+/* MODE CONTROL register bits */
+#define USBSS_MODE_VALID       BIT(0)
+
+/* WAKEUP CONFIG register bits */
+#define USBSS_WAKEUP_CFG_OVERCURRENT_EN        BIT(3)
+#define USBSS_WAKEUP_CFG_LINESTATE_EN  BIT(2)
+#define USBSS_WAKEUP_CFG_SESSVALID_EN  BIT(1)
+#define USBSS_WAKEUP_CFG_VBUSVALID_EN  BIT(0)
+
+/* WAKEUP STAT register bits */
+#define USBSS_WAKEUP_STAT_OVERCURRENT  BIT(4)
+#define USBSS_WAKEUP_STAT_LINESTATE    BIT(3)
+#define USBSS_WAKEUP_STAT_SESSVALID    BIT(2)
+#define USBSS_WAKEUP_STAT_VBUSVALID    BIT(1)
+#define USBSS_WAKEUP_STAT_CLR          BIT(0)
+
+/* IRQ_MISC_STATUS_RAW register bits */
+#define USBSS_IRQ_MISC_RAW_VBUSVALID   BIT(22)
+#define USBSS_IRQ_MISC_RAW_SESSVALID   BIT(20)
+
+/* IRQ_MISC_STATUS register bits */
+#define USBSS_IRQ_MISC_VBUSVALID       BIT(22)
+#define USBSS_IRQ_MISC_SESSVALID       BIT(20)
+
+/* IRQ_MISC_ENABLE_SET register bits */
+#define USBSS_IRQ_MISC_ENABLE_SET_VBUSVALID    BIT(22)
+#define USBSS_IRQ_MISC_ENABLE_SET_SESSVALID    BIT(20)
+
+/* IRQ_MISC_ENABLE_CLR register bits */
+#define USBSS_IRQ_MISC_ENABLE_CLR_VBUSVALID    BIT(22)
+#define USBSS_IRQ_MISC_ENABLE_CLR_SESSVALID    BIT(20)
+
+/* IRQ_MISC_EOI register bits */
+#define USBSS_IRQ_MISC_EOI_VECTOR      BIT(0)
+
+/* VBUS_STAT register bits */
+#define USBSS_VBUS_STAT_SESSVALID      BIT(2)
+#define USBSS_VBUS_STAT_VBUSVALID      BIT(0)
+
+/* Mask for PHY PLL REFCLK */
+#define PHY_PLL_REFCLK_MASK    GENMASK(3, 0)
+
+#define DWC3_AM62_AUTOSUSPEND_DELAY    100
+
+struct dwc3_data {
+       struct device *dev;
+       void __iomem *usbss;
+       struct clk *usb2_refclk;
+       int rate_code;
+       struct regmap *syscon;
+       unsigned int offset;
+       unsigned int vbus_divider;
+};
+
+static const int dwc3_ti_rate_table[] = {      /* in KHZ */
+       9600,
+       10000,
+       12000,
+       19200,
+       20000,
+       24000,
+       25000,
+       26000,
+       38400,
+       40000,
+       58000,
+       50000,
+       52000,
+};
+
+static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset)
+{
+       return readl((data->usbss) + offset);
+}
+
+static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value)
+{
+       writel(value, (data->usbss) + offset);
+}
+
+static int phy_syscon_pll_refclk(struct dwc3_data *data)
+{
+       struct device *dev = data->dev;
+       struct device_node *node = dev->of_node;
+       struct of_phandle_args args;
+       struct regmap *syscon;
+       int ret;
+
+       syscon = syscon_regmap_lookup_by_phandle(node, "ti,syscon-phy-pll-refclk");
+       if (IS_ERR(syscon)) {
+               dev_err(dev, "unable to get ti,syscon-phy-pll-refclk regmap\n");
+               return PTR_ERR(syscon);
+       }
+
+       data->syscon = syscon;
+
+       ret = of_parse_phandle_with_fixed_args(node, "ti,syscon-phy-pll-refclk", 1,
+                                              0, &args);
+       if (ret)
+               return ret;
+
+       data->offset = args.args[0];
+
+       ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code);
+       if (ret) {
+               dev_err(dev, "failed to set phy pll reference clock rate\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int dwc3_ti_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = pdev->dev.of_node;
+       struct dwc3_data *data;
+       int i, ret;
+       unsigned long rate;
+       u32 reg;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->dev = dev;
+       platform_set_drvdata(pdev, data);
+
+       data->usbss = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(data->usbss)) {
+               dev_err(dev, "can't map IOMEM resource\n");
+               return PTR_ERR(data->usbss);
+       }
+
+       data->usb2_refclk = devm_clk_get(dev, "ref");
+       if (IS_ERR(data->usb2_refclk)) {
+               dev_err(dev, "can't get usb2_refclk\n");
+               return PTR_ERR(data->usb2_refclk);
+       }
+
+       /* Calculate the rate code */
+       rate = clk_get_rate(data->usb2_refclk);
+       rate /= 1000;   // To KHz
+       for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
+               if (dwc3_ti_rate_table[i] == rate)
+                       break;
+       }
+
+       if (i == ARRAY_SIZE(dwc3_ti_rate_table)) {
+               dev_err(dev, "unsupported usb2_refclk rate: %lu KHz\n", rate);
+               ret = -EINVAL;
+               goto err_clk_disable;
+       }
+
+       data->rate_code = i;
+
+       /* Read the syscon property and set the rate code */
+       ret = phy_syscon_pll_refclk(data);
+       if (ret)
+               goto err_clk_disable;
+
+       /* VBUS divider select */
+       data->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+       reg = dwc3_ti_readl(data, USBSS_PHY_CONFIG);
+       if (data->vbus_divider)
+               reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
+
+       dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg);
+
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       /*
+        * Don't ignore its dependencies with its children
+        */
+       pm_suspend_ignore_children(dev, false);
+       clk_prepare_enable(data->usb2_refclk);
+       pm_runtime_get_noresume(dev);
+
+       ret = of_platform_populate(node, NULL, NULL, dev);
+       if (ret) {
+               dev_err(dev, "failed to create dwc3 core: %d\n", ret);
+               goto err_pm_disable;
+       }
+
+       /* Set mode valid bit to indicate role is valid */
+       reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+       reg |= USBSS_MODE_VALID;
+       dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+
+       /* Setting up autosuspend */
+       pm_runtime_set_autosuspend_delay(dev, DWC3_AM62_AUTOSUSPEND_DELAY);
+       pm_runtime_use_autosuspend(dev);
+       pm_runtime_mark_last_busy(dev);
+       pm_runtime_put_autosuspend(dev);
+       return 0;
+
+err_pm_disable:
+       clk_disable_unprepare(data->usb2_refclk);
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+err_clk_disable:
+       clk_put(data->usb2_refclk);
+       return ret;
+}
+
+static int dwc3_ti_remove_core(struct device *dev, void *c)
+{
+       struct platform_device *pdev = to_platform_device(dev);
+
+       platform_device_unregister(pdev);
+       return 0;
+}
+
+static int dwc3_ti_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct dwc3_data *data = platform_get_drvdata(pdev);
+       u32 reg;
+
+       device_for_each_child(dev, NULL, dwc3_ti_remove_core);
+
+       /* Clear mode valid bit */
+       reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+       reg &= ~USBSS_MODE_VALID;
+       dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+
+       pm_runtime_put_sync(dev);
+       clk_disable_unprepare(data->usb2_refclk);
+       pm_runtime_disable(dev);
+       pm_runtime_set_suspended(dev);
+
+       clk_put(data->usb2_refclk);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int dwc3_ti_suspend_common(struct device *dev)
+{
+       struct dwc3_data *data = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(data->usb2_refclk);
+
+       return 0;
+}
+
+static int dwc3_ti_resume_common(struct device *dev)
+{
+       struct dwc3_data *data = dev_get_drvdata(dev);
+
+       clk_prepare_enable(data->usb2_refclk);
+
+       return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(dwc3_ti_pm_ops, dwc3_ti_suspend_common,
+                           dwc3_ti_resume_common, NULL);
+
+#define DEV_PM_OPS     (&dwc3_ti_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif /* CONFIG_PM */
+
+static const struct of_device_id dwc3_ti_of_match[] = {
+       { .compatible = "ti,am62-usb"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, dwc3_ti_of_match);
+
+static struct platform_driver dwc3_ti_driver = {
+       .probe          = dwc3_ti_probe,
+       .remove         = dwc3_ti_remove,
+       .driver         = {
+               .name   = "dwc3-am62",
+               .pm     = DEV_PM_OPS,
+               .of_match_table = dwc3_ti_of_match,
+       },
+};
+
+module_platform_driver(dwc3_ti_driver);
+
+MODULE_ALIAS("platform:dwc3-am62");
+MODULE_AUTHOR("Aswath Govindraju <a-govindraju@ti.com>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("DesignWare USB3 TI Glue Layer");
index 2e19e0e..ba51de7 100644 (file)
@@ -288,7 +288,7 @@ static void dwc3_pci_resume_work(struct work_struct *work)
        int ret;
 
        ret = pm_runtime_get_sync(&dwc3->dev);
-       if (ret) {
+       if (ret < 0) {
                pm_runtime_put_sync_autosuspend(&dwc3->dev);
                return;
        }
index a6f3a9b..67b237c 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
+#include <linux/of_gpio.h>
 #include <linux/of_platform.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
@@ -98,6 +99,7 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
 {
        struct device           *dev = priv_data->dev;
        struct reset_control    *crst, *hibrst, *apbrst;
+       struct gpio_desc        *reset_gpio;
        struct phy              *usb3_phy;
        int                     ret = 0;
        u32                     reg;
@@ -201,6 +203,21 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
        }
 
 skip_usb3_phy:
+       /* ulpi reset via gpio-modepin or gpio-framework driver */
+       reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+       if (IS_ERR(reset_gpio)) {
+               return dev_err_probe(dev, PTR_ERR(reset_gpio),
+                                    "Failed to request reset GPIO\n");
+       }
+
+       if (reset_gpio) {
+               /* Toggle ulpi to reset the phy. */
+               gpiod_set_value_cansleep(reset_gpio, 1);
+               usleep_range(5000, 10000);
+               gpiod_set_value_cansleep(reset_gpio, 0);
+               usleep_range(5000, 10000);
+       }
+
        /*
         * This routes the USB DMA traffic to go through FPD path instead
         * of reaching DDR directly. This traffic routing is needed to
index 1064be5..5d64266 100644 (file)
@@ -218,7 +218,7 @@ out:
        return ret;
 }
 
-static void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc)
 {
        struct dwc3_ep          *dep;
 
@@ -813,7 +813,7 @@ static void dwc3_ep0_inspect_setup(struct dwc3 *dwc,
        int ret = -EINVAL;
        u32 len;
 
-       if (!dwc->gadget_driver)
+       if (!dwc->gadget_driver || !dwc->connected)
                goto out;
 
        trace_dwc3_ctrl_req(ctrl);
@@ -1080,6 +1080,7 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
        unsigned int direction = !dwc->ep0_expect_in;
 
        dwc->delayed_status = false;
+       dwc->clear_stall_protocol = 0;
 
        if (dwc->ep0state != EP0_STATUS_PHASE)
                return;
@@ -1087,13 +1088,18 @@ void dwc3_ep0_send_delayed_status(struct dwc3 *dwc)
        __dwc3_ep0_do_control_status(dwc, dwc->eps[direction]);
 }
 
-static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
        struct dwc3_gadget_ep_cmd_params params;
        u32                     cmd;
        int                     ret;
 
-       if (!dep->resource_index)
+       /*
+        * For status/DATA OUT stage, TRB will be queued on ep0 out
+        * endpoint for which resource index is zero. Hence allow
+        * queuing ENDXFER command for ep0 out endpoint.
+        */
+       if (!dep->resource_index && dep->number)
                return;
 
        cmd = DWC3_DEPCMD_ENDTRANSFER;
index 0b9c249..00427d1 100644 (file)
@@ -657,7 +657,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
 /**
  * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value
  * @dwc: pointer to the DWC3 context
- * @nfifos: number of fifos to calculate for
  *
  * Calculates the size value based on the equation below:
  *
@@ -690,7 +689,7 @@ static int dwc3_gadget_calc_tx_fifo_size(struct dwc3 *dwc, int mult)
 }
 
 /**
- * dwc3_gadget_clear_tx_fifo_size - Clears txfifo allocation
+ * dwc3_gadget_clear_tx_fifos - Clears txfifo allocation
  * @dwc: pointer to the DWC3 context
  *
  * Iterates through all the endpoint registers and clears the previous txfifo
@@ -783,7 +782,8 @@ static int dwc3_gadget_resize_tx_fifos(struct dwc3_ep *dep)
                num_fifos = 3;
 
        if (dep->endpoint.maxburst > 6 &&
-           usb_endpoint_xfer_bulk(dep->endpoint.desc) && DWC3_IP_IS(DWC31))
+           (usb_endpoint_xfer_bulk(dep->endpoint.desc) ||
+            usb_endpoint_xfer_isoc(dep->endpoint.desc)) && DWC3_IP_IS(DWC31))
                num_fifos = dwc->tx_fifo_resize_max_num;
 
        /* FIFO size for a single buffer */
@@ -882,12 +882,13 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
                reg |= DWC3_DALEPENA_EP(dep->number);
                dwc3_writel(dwc->regs, DWC3_DALEPENA, reg);
 
+               dep->trb_dequeue = 0;
+               dep->trb_enqueue = 0;
+
                if (usb_endpoint_xfer_control(desc))
                        goto out;
 
                /* Initialize the TRB ring */
-               dep->trb_dequeue = 0;
-               dep->trb_enqueue = 0;
                memset(dep->trb_pool, 0,
                       sizeof(struct dwc3_trb) * DWC3_TRB_NUM);
 
@@ -2001,10 +2002,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
 static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
 {
        struct dwc3_request             *req;
-       struct dwc3_request             *tmp;
        struct dwc3                     *dwc = dep->dwc;
 
-       list_for_each_entry_safe(req, tmp, &dep->cancelled_list, list) {
+       while (!list_empty(&dep->cancelled_list)) {
+               req = next_request(&dep->cancelled_list);
                dwc3_gadget_ep_skip_trbs(dep, req);
                switch (req->status) {
                case DWC3_REQUEST_STATUS_DISCONNECTED:
@@ -2021,6 +2022,12 @@ static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep)
                        dwc3_gadget_giveback(dep, req, -ECONNRESET);
                        break;
                }
+               /*
+                * The endpoint is disabled, let the dwc3_remove_requests()
+                * handle the cleanup.
+                */
+               if (!dep->endpoint.desc)
+                       break;
        }
 }
 
@@ -2056,16 +2063,6 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
                if (r == req) {
                        struct dwc3_request *t;
 
-                       /*
-                        * If a Setup packet is received but yet to DMA out, the controller will
-                        * not process the End Transfer command of any endpoint. Polling of its
-                        * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
-                        * timeout. Delay issuing the End Transfer command until the Setup TRB is
-                        * prepared.
-                        */
-                       if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status)
-                               dep->flags |= DWC3_EP_DELAY_STOP;
-
                        /* wait until it is processed */
                        dwc3_stop_active_transfer(dep, true, true);
 
@@ -2152,6 +2149,9 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
                if (dep->flags & DWC3_EP_END_TRANSFER_PENDING ||
                    (dep->flags & DWC3_EP_DELAY_STOP)) {
                        dep->flags |= DWC3_EP_PENDING_CLEAR_STALL;
+                       if (protocol)
+                               dwc->clear_stall_protocol = dep->number;
+
                        return 0;
                }
 
@@ -2498,27 +2498,63 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc);
 static void __dwc3_gadget_stop(struct dwc3 *dwc);
 static int __dwc3_gadget_start(struct dwc3 *dwc);
 
-static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+static int dwc3_gadget_soft_disconnect(struct dwc3 *dwc)
 {
-       struct dwc3             *dwc = gadget_to_dwc(g);
-       unsigned long           flags;
-       int                     ret;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       dwc->connected = false;
 
-       is_on = !!is_on;
-       dwc->softconnect = is_on;
        /*
         * Per databook, when we want to stop the gadget, if a control transfer
         * is still in process, complete it and get the core into setup phase.
         */
-       if (!is_on && dwc->ep0state != EP0_SETUP_PHASE) {
+       if (dwc->ep0state != EP0_SETUP_PHASE) {
+               int ret;
+
                reinit_completion(&dwc->ep0_in_setup);
 
+               spin_unlock_irqrestore(&dwc->lock, flags);
                ret = wait_for_completion_timeout(&dwc->ep0_in_setup,
                                msecs_to_jiffies(DWC3_PULL_UP_TIMEOUT));
+               spin_lock_irqsave(&dwc->lock, flags);
                if (ret == 0)
                        dev_warn(dwc->dev, "timed out waiting for SETUP phase\n");
        }
 
+       /*
+        * In the Synopsys DesignWare Cores USB3 Databook Rev. 3.30a
+        * Section 4.1.8 Table 4-7, it states that for a device-initiated
+        * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
+        * command for any active transfers" before clearing the RunStop
+        * bit.
+        */
+       dwc3_stop_active_transfers(dwc);
+       __dwc3_gadget_stop(dwc);
+       spin_unlock_irqrestore(&dwc->lock, flags);
+
+       /*
+        * Note: if the GEVNTCOUNT indicates events in the event buffer, the
+        * driver needs to acknowledge them before the controller can halt.
+        * Simply let the interrupt handler acknowledges and handle the
+        * remaining event generated by the controller while polling for
+        * DSTS.DEVCTLHLT.
+        */
+       return dwc3_gadget_run_stop(dwc, false, false);
+}
+
+static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
+{
+       struct dwc3             *dwc = gadget_to_dwc(g);
+       int                     ret;
+
+       is_on = !!is_on;
+
+       if (dwc->pullups_connected == is_on)
+               return 0;
+
+       dwc->softconnect = is_on;
+
        /*
         * Avoid issuing a runtime resume if the device is already in the
         * suspended state during gadget disconnect.  DWC3 gadget was already
@@ -2541,42 +2577,8 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
                return 0;
        }
 
-       /*
-        * Synchronize and disable any further event handling while controller
-        * is being enabled/disabled.
-        */
-       disable_irq(dwc->irq_gadget);
-
-       spin_lock_irqsave(&dwc->lock, flags);
-
        if (!is_on) {
-               u32 count;
-
-               dwc->connected = false;
-               /*
-                * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
-                * Section 4.1.8 Table 4-7, it states that for a device-initiated
-                * disconnect, the SW needs to ensure that it sends "a DEPENDXFER
-                * command for any active transfers" before clearing the RunStop
-                * bit.
-                */
-               dwc3_stop_active_transfers(dwc);
-               __dwc3_gadget_stop(dwc);
-
-               /*
-                * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
-                * Section 1.3.4, it mentions that for the DEVCTRLHLT bit, the
-                * "software needs to acknowledge the events that are generated
-                * (by writing to GEVNTCOUNTn) while it is waiting for this bit
-                * to be set to '1'."
-                */
-               count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0));
-               count &= DWC3_GEVNTCOUNT_MASK;
-               if (count > 0) {
-                       dwc3_writel(dwc->regs, DWC3_GEVNTCOUNT(0), count);
-                       dwc->ev_buf->lpos = (dwc->ev_buf->lpos + count) %
-                                               dwc->ev_buf->length;
-               }
+               ret = dwc3_gadget_soft_disconnect(dwc);
        } else {
                /*
                 * In the Synopsys DWC_usb31 1.90a programming guide section
@@ -2584,18 +2586,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
                 * device-initiated disconnect requires a core soft reset
                 * (DCTL.CSftRst) before enabling the run/stop bit.
                 */
-               spin_unlock_irqrestore(&dwc->lock, flags);
                dwc3_core_soft_reset(dwc);
-               spin_lock_irqsave(&dwc->lock, flags);
 
                dwc3_event_buffers_setup(dwc);
                __dwc3_gadget_start(dwc);
+               ret = dwc3_gadget_run_stop(dwc, true, false);
        }
 
-       ret = dwc3_gadget_run_stop(dwc, is_on, false);
-       spin_unlock_irqrestore(&dwc->lock, flags);
-       enable_irq(dwc->irq_gadget);
-
        pm_runtime_put(dwc->dev);
 
        return ret;
@@ -2745,6 +2742,7 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
 
        /* begin to receive SETUP packets */
        dwc->ep0state = EP0_SETUP_PHASE;
+       dwc->ep0_bounced = false;
        dwc->link_state = DWC3_LINK_STATE_SS_DIS;
        dwc->delayed_status = false;
        dwc3_ep0_out_start(dwc);
@@ -3333,15 +3331,21 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event, int status)
 {
        struct dwc3_request     *req;
-       struct dwc3_request     *tmp;
 
-       list_for_each_entry_safe(req, tmp, &dep->started_list, list) {
+       while (!list_empty(&dep->started_list)) {
                int ret;
 
+               req = next_request(&dep->started_list);
                ret = dwc3_gadget_ep_cleanup_completed_request(dep, event,
                                req, status);
                if (ret)
                        break;
+               /*
+                * The endpoint is disabled, let the dwc3_remove_requests()
+                * handle the cleanup.
+                */
+               if (!dep->endpoint.desc)
+                       break;
        }
 }
 
@@ -3380,14 +3384,14 @@ static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
        struct dwc3             *dwc = dep->dwc;
        bool                    no_started_trb = true;
 
-       if (!dep->endpoint.desc)
-               return no_started_trb;
-
        dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
 
        if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
                goto out;
 
+       if (!dep->endpoint.desc)
+               return no_started_trb;
+
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
                list_empty(&dep->started_list) &&
                (list_empty(&dep->pending_list) || status == -EXDEV))
@@ -3512,7 +3516,7 @@ static void dwc3_gadget_endpoint_command_complete(struct dwc3_ep *dep,
                }
 
                dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
-               if (dwc->delayed_status)
+               if (dwc->clear_stall_protocol == dep->number)
                        dwc3_ep0_send_delayed_status(dwc);
        }
 
@@ -3673,11 +3677,34 @@ static void dwc3_reset_gadget(struct dwc3 *dwc)
 void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
        bool interrupt)
 {
+       struct dwc3 *dwc = dep->dwc;
+
+       /*
+        * Only issue End Transfer command to the control endpoint of a started
+        * Data Phase. Typically we should only do so in error cases such as
+        * invalid/unexpected direction as described in the control transfer
+        * flow of the programming guide.
+        */
+       if (dep->number <= 1 && dwc->ep0state != EP0_DATA_PHASE)
+               return;
+
        if (!(dep->flags & DWC3_EP_TRANSFER_STARTED) ||
            (dep->flags & DWC3_EP_DELAY_STOP) ||
            (dep->flags & DWC3_EP_END_TRANSFER_PENDING))
                return;
 
+       /*
+        * If a Setup packet is received but yet to DMA out, the controller will
+        * not process the End Transfer command of any endpoint. Polling of its
+        * DEPCMD.CmdAct may block setting up TRB for Setup packet, causing a
+        * timeout. Delay issuing the End Transfer command until the Setup TRB is
+        * prepared.
+        */
+       if (dwc->ep0state != EP0_SETUP_PHASE && !dwc->delayed_status) {
+               dep->flags |= DWC3_EP_DELAY_STOP;
+               return;
+       }
+
        /*
         * NOTICE: We are violating what the Databook says about the
         * EndTransfer command. Ideally we would _always_ wait for the
@@ -3795,6 +3822,27 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
        }
 
        dwc3_reset_gadget(dwc);
+
+       /*
+        * From SNPS databook section 8.1.2, the EP0 should be in setup
+        * phase. So ensure that EP0 is in setup phase by issuing a stall
+        * and restart if EP0 is not in setup phase.
+        */
+       if (dwc->ep0state != EP0_SETUP_PHASE) {
+               unsigned int    dir;
+
+               dir = !!dwc->ep0_expect_in;
+               if (dwc->ep0state == EP0_DATA_PHASE)
+                       dwc3_ep0_end_control_data(dwc, dwc->eps[dir]);
+               else
+                       dwc3_ep0_end_control_data(dwc, dwc->eps[!dir]);
+
+               dwc->eps[0]->trb_enqueue = 0;
+               dwc->eps[1]->trb_enqueue = 0;
+
+               dwc3_ep0_stall_and_restart(dwc);
+       }
+
        /*
         * In the Synopsis DesignWare Cores USB3 Databook Rev. 3.30a
         * Section 4.1.2 Table 4-2, it states that during a USB reset, the SW
index f763380..55a56cf 100644 (file)
@@ -110,6 +110,8 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req,
 void dwc3_ep0_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event);
 void dwc3_ep0_out_start(struct dwc3 *dwc);
+void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep);
+void dwc3_ep0_stall_and_restart(struct dwc3 *dwc);
 int __dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
 int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value);
 int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
index eda8719..f56c30c 100644 (file)
@@ -7,7 +7,6 @@
  * Authors: Felipe Balbi <balbi@ti.com>,
  */
 
-#include <linux/acpi.h>
 #include <linux/irq.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
@@ -83,7 +82,6 @@ int dwc3_host_init(struct dwc3 *dwc)
        }
 
        xhci->dev.parent        = dwc->dev;
-       ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev));
 
        dwc->xhci = xhci;
 
index 2eaeaae..403563c 100644 (file)
@@ -2505,7 +2505,7 @@ int usb_composite_probe(struct usb_composite_driver *driver)
        gadget_driver->driver.name = driver->name;
        gadget_driver->max_speed = driver->max_speed;
 
-       return usb_gadget_probe_driver(gadget_driver);
+       return usb_gadget_register_driver(gadget_driver);
 }
 EXPORT_SYMBOL_GPL(usb_composite_probe);
 
index 84b73cb..3a6b492 100644 (file)
@@ -284,7 +284,7 @@ static ssize_t gadget_dev_desc_UDC_store(struct config_item *item,
                        goto err;
                }
                gi->composite.gadget_driver.udc_name = name;
-               ret = usb_gadget_probe_driver(&gi->composite.gadget_driver);
+               ret = usb_gadget_register_driver(&gi->composite.gadget_driver);
                if (ret) {
                        gi->composite.gadget_driver.udc_name = NULL;
                        goto err;
index 349945e..411eb48 100644 (file)
@@ -333,6 +333,8 @@ static void acm_complete_set_line_coding(struct usb_ep *ep,
        }
 }
 
+static int acm_send_break(struct gserial *port, int duration);
+
 static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
 {
        struct f_acm            *acm = func_to_acm(f);
@@ -391,6 +393,14 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl)
                acm->port_handshake_bits = w_value;
                break;
 
+       case ((USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE) << 8)
+                       | USB_CDC_REQ_SEND_BREAK:
+               if (w_index != acm->ctrl_id)
+                       goto invalid;
+
+               acm_send_break(&acm->port, w_value);
+               break;
+
        default:
 invalid:
                dev_vdbg(&cdev->gadget->dev,
index 71bb5e4..0334c8a 100644 (file)
@@ -24,7 +24,6 @@
 #include <media/v4l2-dev.h>
 #include <media/v4l2-event.h>
 
-#include "u_uvc.h"
 #include "uvc.h"
 #include "uvc_configfs.h"
 #include "uvc_v4l2.h"
@@ -44,7 +43,7 @@ MODULE_PARM_DESC(trace, "Trace level bitmask");
 #define UVC_STRING_STREAMING_IDX               1
 
 static struct usb_string uvc_en_us_strings[] = {
-       [UVC_STRING_CONTROL_IDX].s = "UVC Camera",
+       /* [UVC_STRING_CONTROL_IDX].s = DYNAMIC, */
        [UVC_STRING_STREAMING_IDX].s = "Video Streaming",
        {  }
 };
@@ -676,6 +675,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
        uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
        uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
 
+       uvc_en_us_strings[UVC_STRING_CONTROL_IDX].s = opts->function_name;
        us = usb_gstrings_attach(cdev, uvc_function_strings,
                                 ARRAY_SIZE(uvc_en_us_strings));
        if (IS_ERR(us)) {
@@ -866,6 +866,7 @@ static struct usb_function_instance *uvc_alloc_inst(void)
 
        opts->streaming_interval = 1;
        opts->streaming_maxpacket = 1024;
+       snprintf(opts->function_name, sizeof(opts->function_name), "UVC Camera");
 
        ret = uvcg_attach_configfs(opts);
        if (ret < 0) {
index 2bb5698..c1f62e9 100644 (file)
@@ -1179,8 +1179,8 @@ int g_audio_setup(struct g_audio *g_audio, const char *pcm_name,
        if (c_chmask) {
                struct uac_rtd_params *prm = &uac->c_prm;
 
-    spin_lock_init(&prm->lock);
-    uac->c_prm.uac = uac;
+               spin_lock_init(&prm->lock);
+               uac->c_prm.uac = uac;
                prm->max_psize = g_audio->out_ep_maxpsize;
                prm->srate = params->c_srates[0];
 
index 9a01a7d..24b8681 100644 (file)
@@ -27,6 +27,7 @@ struct f_uvc_opts {
 
        unsigned int                                    control_interface;
        unsigned int                                    streaming_interface;
+       char                                            function_name[32];
 
        /*
         * Control descriptors array pointers for full-/high-speed and
index c3607a3..9eec104 100644 (file)
@@ -79,6 +79,7 @@ struct uvc_request {
        struct uvc_video *video;
        struct sg_table sgt;
        u8 header[UVCG_REQUEST_HEADER_LEN];
+       struct uvc_buffer *last_buf;
 };
 
 struct uvc_video {
index 77d6403..e5a6b6e 100644 (file)
  * Author: Andrzej Pietrasiewicz <andrzejtp2010@gmail.com>
  */
 
-#include <linux/sort.h>
-
-#include "u_uvc.h"
 #include "uvc_configfs.h"
 
+#include <linux/sort.h>
+
 /* -----------------------------------------------------------------------------
  * Global Utility Structures and Macros
  */
 
-#define UVCG_STREAMING_CONTROL_SIZE    1
-
 #define UVC_ATTR(prefix, cname, aname) \
 static struct configfs_attribute prefix##attr_##cname = { \
        .ca_name        = __stringify(aname),                           \
@@ -49,12 +46,6 @@ static int uvcg_config_compare_u32(const void *l, const void *r)
        return li < ri ? -1 : li == ri ? 0 : 1;
 }
 
-static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct f_uvc_opts,
-                           func_inst.group);
-}
-
 struct uvcg_config_group_type {
        struct config_item_type type;
        const char *name;
@@ -125,19 +116,6 @@ static void uvcg_config_remove_children(struct config_group *group)
  * control/header
  */
 
-DECLARE_UVC_HEADER_DESCRIPTOR(1);
-
-struct uvcg_control_header {
-       struct config_item              item;
-       struct UVC_HEADER_DESCRIPTOR(1) desc;
-       unsigned                        linked;
-};
-
-static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
-{
-       return container_of(item, struct uvcg_control_header, item);
-}
-
 #define UVCG_CTRL_HDR_ATTR(cname, aname, bits, limit)                  \
 static ssize_t uvcg_control_header_##cname##_show(                     \
        struct config_item *item, char *page)                           \
@@ -769,24 +747,6 @@ static const char * const uvcg_format_names[] = {
        "mjpeg",
 };
 
-enum uvcg_format_type {
-       UVCG_UNCOMPRESSED = 0,
-       UVCG_MJPEG,
-};
-
-struct uvcg_format {
-       struct config_group     group;
-       enum uvcg_format_type   type;
-       unsigned                linked;
-       unsigned                num_frames;
-       __u8                    bmaControls[UVCG_STREAMING_CONTROL_SIZE];
-};
-
-static struct uvcg_format *to_uvcg_format(struct config_item *item)
-{
-       return container_of(to_config_group(item), struct uvcg_format, group);
-}
-
 static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page)
 {
        struct f_uvc_opts *opts;
@@ -845,29 +805,11 @@ end:
        return ret;
 }
 
-struct uvcg_format_ptr {
-       struct uvcg_format      *fmt;
-       struct list_head        entry;
-};
-
 /* -----------------------------------------------------------------------------
  * streaming/header/<NAME>
  * streaming/header
  */
 
-struct uvcg_streaming_header {
-       struct config_item                              item;
-       struct uvc_input_header_descriptor              desc;
-       unsigned                                        linked;
-       struct list_head                                formats;
-       unsigned                                        num_fmt;
-};
-
-static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
-{
-       return container_of(item, struct uvcg_streaming_header, item);
-}
-
 static void uvcg_format_set_indices(struct config_group *fmt);
 
 static int uvcg_streaming_header_allow_link(struct config_item *src,
@@ -1059,31 +1001,6 @@ static const struct uvcg_config_group_type uvcg_streaming_header_grp_type = {
  * streaming/<mode>/<format>/<NAME>
  */
 
-struct uvcg_frame {
-       struct config_item      item;
-       enum uvcg_format_type   fmt_type;
-       struct {
-               u8      b_length;
-               u8      b_descriptor_type;
-               u8      b_descriptor_subtype;
-               u8      b_frame_index;
-               u8      bm_capabilities;
-               u16     w_width;
-               u16     w_height;
-               u32     dw_min_bit_rate;
-               u32     dw_max_bit_rate;
-               u32     dw_max_video_frame_buffer_size;
-               u32     dw_default_frame_interval;
-               u8      b_frame_interval_type;
-       } __attribute__((packed)) frame;
-       u32 *dw_frame_interval;
-};
-
-static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
-{
-       return container_of(item, struct uvcg_frame, item);
-}
-
 #define UVCG_FRAME_ATTR(cname, aname, bits) \
 static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\
 {                                                                      \
@@ -1345,6 +1262,7 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
        struct uvcg_format *fmt;
        struct f_uvc_opts *opts;
        struct config_item *opts_item;
+       struct uvcg_frame_ptr *frame_ptr;
 
        h = kzalloc(sizeof(*h), GFP_KERNEL);
        if (!h)
@@ -1375,6 +1293,16 @@ static struct config_item *uvcg_frame_make(struct config_group *group,
                kfree(h);
                return ERR_PTR(-EINVAL);
        }
+
+       frame_ptr = kzalloc(sizeof(*frame_ptr), GFP_KERNEL);
+       if (!frame_ptr) {
+               mutex_unlock(&opts->lock);
+               kfree(h);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       frame_ptr->frm = h;
+       list_add_tail(&frame_ptr->entry, &fmt->frames);
        ++fmt->num_frames;
        mutex_unlock(&opts->lock);
 
@@ -1388,13 +1316,23 @@ static void uvcg_frame_drop(struct config_group *group, struct config_item *item
        struct uvcg_format *fmt;
        struct f_uvc_opts *opts;
        struct config_item *opts_item;
+       struct uvcg_frame *target_frm = NULL;
+       struct uvcg_frame_ptr *frame_ptr, *tmp;
 
        opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
        opts = to_f_uvc_opts(opts_item);
 
        mutex_lock(&opts->lock);
+       target_frm = container_of(item, struct uvcg_frame, item);
        fmt = to_uvcg_format(&group->cg_item);
-       --fmt->num_frames;
+
+       list_for_each_entry_safe(frame_ptr, tmp, &fmt->frames, entry)
+               if (frame_ptr->frm == target_frm) {
+                       list_del(&frame_ptr->entry);
+                       kfree(frame_ptr);
+                       --fmt->num_frames;
+                       break;
+               }
        mutex_unlock(&opts->lock);
 
        config_item_put(item);
@@ -1420,18 +1358,6 @@ static void uvcg_format_set_indices(struct config_group *fmt)
  * streaming/uncompressed/<NAME>
  */
 
-struct uvcg_uncompressed {
-       struct uvcg_format              fmt;
-       struct uvc_format_uncompressed  desc;
-};
-
-static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item)
-{
-       return container_of(
-               container_of(to_config_group(item), struct uvcg_format, group),
-               struct uvcg_uncompressed, fmt);
-}
-
 static struct configfs_group_operations uvcg_uncompressed_group_ops = {
        .make_item              = uvcg_frame_make,
        .drop_item              = uvcg_frame_drop,
@@ -1565,6 +1491,12 @@ uvcg_uncompressed_##cname##_store(struct config_item *item,              \
        if (ret)                                                        \
                goto end;                                               \
                                                                        \
+       /* index values in uvc are never 0 */                           \
+       if (!num) {                                                     \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
        u->desc.aname = num;                                            \
        ret = len;                                                      \
 end:                                                                   \
@@ -1645,6 +1577,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group,
        h->desc.bmInterfaceFlags        = 0;
        h->desc.bCopyProtect            = 0;
 
+       INIT_LIST_HEAD(&h->fmt.frames);
        h->fmt.type = UVCG_UNCOMPRESSED;
        config_group_init_type_name(&h->fmt.group, name,
                                    &uvcg_uncompressed_type);
@@ -1669,18 +1602,6 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = {
  * streaming/mjpeg/<NAME>
  */
 
-struct uvcg_mjpeg {
-       struct uvcg_format              fmt;
-       struct uvc_format_mjpeg         desc;
-};
-
-static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
-{
-       return container_of(
-               container_of(to_config_group(item), struct uvcg_format, group),
-               struct uvcg_mjpeg, fmt);
-}
-
 static struct configfs_group_operations uvcg_mjpeg_group_ops = {
        .make_item              = uvcg_frame_make,
        .drop_item              = uvcg_frame_drop,
@@ -1758,6 +1679,12 @@ uvcg_mjpeg_##cname##_store(struct config_item *item,                     \
        if (ret)                                                        \
                goto end;                                               \
                                                                        \
+       /* index values in uvc are never 0 */                           \
+       if (!num) {                                                     \
+               ret = -EINVAL;                                          \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
        u->desc.aname = num;                                            \
        ret = len;                                                      \
 end:                                                                   \
@@ -1831,6 +1758,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group,
        h->desc.bmInterfaceFlags        = 0;
        h->desc.bCopyProtect            = 0;
 
+       INIT_LIST_HEAD(&h->fmt.frames);
        h->fmt.type = UVCG_MJPEG;
        config_group_init_type_name(&h->fmt.group, name,
                                    &uvcg_mjpeg_type);
@@ -2425,10 +2353,51 @@ UVCG_OPTS_ATTR(streaming_maxburst, streaming_maxburst, 15);
 
 #undef UVCG_OPTS_ATTR
 
+#define UVCG_OPTS_STRING_ATTR(cname, aname)                            \
+static ssize_t f_uvc_opts_string_##cname##_show(struct config_item *item,\
+                                        char *page)                    \
+{                                                                      \
+       struct f_uvc_opts *opts = to_f_uvc_opts(item);                  \
+       int result;                                                     \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       result = snprintf(page, sizeof(opts->aname), "%s", opts->aname);\
+       mutex_unlock(&opts->lock);                                      \
+                                                                       \
+       return result;                                                  \
+}                                                                      \
+                                                                       \
+static ssize_t f_uvc_opts_string_##cname##_store(struct config_item *item,\
+                                         const char *page, size_t len) \
+{                                                                      \
+       struct f_uvc_opts *opts = to_f_uvc_opts(item);                  \
+       int ret = 0;                                                    \
+                                                                       \
+       mutex_lock(&opts->lock);                                        \
+       if (opts->refcnt) {                                             \
+               ret = -EBUSY;                                           \
+               goto end;                                               \
+       }                                                               \
+                                                                       \
+       ret = snprintf(opts->aname, min(sizeof(opts->aname), len),      \
+                       "%s", page);                                    \
+                                                                       \
+end:                                                                   \
+       mutex_unlock(&opts->lock);                                      \
+       return ret;                                                     \
+}                                                                      \
+                                                                       \
+UVC_ATTR(f_uvc_opts_string_, cname, aname)
+
+UVCG_OPTS_STRING_ATTR(function_name, function_name);
+
+#undef UVCG_OPTS_STRING_ATTR
+
 static struct configfs_attribute *uvc_attrs[] = {
        &f_uvc_opts_attr_streaming_interval,
        &f_uvc_opts_attr_streaming_maxpacket,
        &f_uvc_opts_attr_streaming_maxburst,
+       &f_uvc_opts_string_attr_function_name,
        NULL,
 };
 
index 7e1d7ca..ad2ec8c 100644 (file)
 #ifndef UVC_CONFIGFS_H
 #define UVC_CONFIGFS_H
 
-struct f_uvc_opts;
+#include <linux/configfs.h>
+
+#include "u_uvc.h"
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_uvc_opts,
+                           func_inst.group);
+}
+
+#define UVCG_STREAMING_CONTROL_SIZE    1
+
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+struct uvcg_control_header {
+       struct config_item              item;
+       struct UVC_HEADER_DESCRIPTOR(1) desc;
+       unsigned                        linked;
+};
+
+static inline struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
+{
+       return container_of(item, struct uvcg_control_header, item);
+}
+
+enum uvcg_format_type {
+       UVCG_UNCOMPRESSED = 0,
+       UVCG_MJPEG,
+};
+
+struct uvcg_format {
+       struct config_group     group;
+       enum uvcg_format_type   type;
+       unsigned                linked;
+       struct list_head        frames;
+       unsigned                num_frames;
+       __u8                    bmaControls[UVCG_STREAMING_CONTROL_SIZE];
+};
+
+struct uvcg_format_ptr {
+       struct uvcg_format      *fmt;
+       struct list_head        entry;
+};
+
+static inline struct uvcg_format *to_uvcg_format(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct uvcg_format, group);
+}
+
+struct uvcg_streaming_header {
+       struct config_item                              item;
+       struct uvc_input_header_descriptor              desc;
+       unsigned                                        linked;
+       struct list_head                                formats;
+       unsigned                                        num_fmt;
+};
+
+static inline struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
+{
+       return container_of(item, struct uvcg_streaming_header, item);
+}
+
+struct uvcg_frame_ptr {
+       struct uvcg_frame       *frm;
+       struct list_head        entry;
+};
+
+struct uvcg_frame {
+       struct config_item      item;
+       enum uvcg_format_type   fmt_type;
+       struct {
+               u8      b_length;
+               u8      b_descriptor_type;
+               u8      b_descriptor_subtype;
+               u8      b_frame_index;
+               u8      bm_capabilities;
+               u16     w_width;
+               u16     w_height;
+               u32     dw_min_bit_rate;
+               u32     dw_max_bit_rate;
+               u32     dw_max_video_frame_buffer_size;
+               u32     dw_default_frame_interval;
+               u8      b_frame_interval_type;
+       } __attribute__((packed)) frame;
+       u32 *dw_frame_interval;
+};
+
+static inline struct uvcg_frame *to_uvcg_frame(struct config_item *item)
+{
+       return container_of(item, struct uvcg_frame, item);
+}
+
+/* -----------------------------------------------------------------------------
+ * streaming/uncompressed/<NAME>
+ */
+
+struct uvcg_uncompressed {
+       struct uvcg_format              fmt;
+       struct uvc_format_uncompressed  desc;
+};
+
+static inline struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item)
+{
+       return container_of(to_uvcg_format(item), struct uvcg_uncompressed, fmt);
+}
+
+/* -----------------------------------------------------------------------------
+ * streaming/mjpeg/<NAME>
+ */
+
+struct uvcg_mjpeg {
+       struct uvcg_format              fmt;
+       struct uvc_format_mjpeg         desc;
+};
+
+static inline struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
+{
+       return container_of(to_uvcg_format(item), struct uvcg_mjpeg, fmt);
+}
 
 int uvcg_attach_configfs(struct f_uvc_opts *opts);
 
index 2cda982..d25edc3 100644 (file)
@@ -185,18 +185,7 @@ int uvcg_query_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
 
 int uvcg_queue_buffer(struct uvc_video_queue *queue, struct v4l2_buffer *buf)
 {
-       unsigned long flags;
-       int ret;
-
-       ret = vb2_qbuf(&queue->queue, NULL, buf);
-       if (ret < 0)
-               return ret;
-
-       spin_lock_irqsave(&queue->irqlock, flags);
-       ret = (queue->flags & UVC_QUEUE_PAUSED) != 0;
-       queue->flags &= ~UVC_QUEUE_PAUSED;
-       spin_unlock_irqrestore(&queue->irqlock, flags);
-       return ret;
+       return vb2_qbuf(&queue->queue, NULL, buf);
 }
 
 /*
@@ -328,33 +317,22 @@ int uvcg_queue_enable(struct uvc_video_queue *queue, int enable)
 }
 
 /* called with &queue_irqlock held.. */
-struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+void uvcg_complete_buffer(struct uvc_video_queue *queue,
                                          struct uvc_buffer *buf)
 {
-       struct uvc_buffer *nextbuf;
-
        if ((queue->flags & UVC_QUEUE_DROP_INCOMPLETE) &&
             buf->length != buf->bytesused) {
                buf->state = UVC_BUF_STATE_QUEUED;
                vb2_set_plane_payload(&buf->buf.vb2_buf, 0, 0);
-               return buf;
+               return;
        }
 
-       list_del(&buf->queue);
-       if (!list_empty(&queue->irqqueue))
-               nextbuf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
-                                          queue);
-       else
-               nextbuf = NULL;
-
        buf->buf.field = V4L2_FIELD_NONE;
        buf->buf.sequence = queue->sequence++;
        buf->buf.vb2_buf.timestamp = ktime_get_ns();
 
        vb2_set_plane_payload(&buf->buf.vb2_buf, 0, buf->bytesused);
        vb2_buffer_done(&buf->buf.vb2_buf, VB2_BUF_STATE_DONE);
-
-       return nextbuf;
 }
 
 struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
@@ -364,8 +342,6 @@ struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue)
        if (!list_empty(&queue->irqqueue))
                buf = list_first_entry(&queue->irqqueue, struct uvc_buffer,
                                       queue);
-       else
-               queue->flags |= UVC_QUEUE_PAUSED;
 
        return buf;
 }
index 05360a0..41f87b9 100644 (file)
@@ -43,7 +43,6 @@ struct uvc_buffer {
 
 #define UVC_QUEUE_DISCONNECTED         (1 << 0)
 #define UVC_QUEUE_DROP_INCOMPLETE      (1 << 1)
-#define UVC_QUEUE_PAUSED               (1 << 2)
 
 struct uvc_video_queue {
        struct vb2_queue queue;
@@ -93,7 +92,7 @@ void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect);
 
 int uvcg_queue_enable(struct uvc_video_queue *queue, int enable);
 
-struct uvc_buffer *uvcg_queue_next_buffer(struct uvc_video_queue *queue,
+void uvcg_complete_buffer(struct uvc_video_queue *queue,
                                          struct uvc_buffer *buf);
 
 struct uvc_buffer *uvcg_queue_head(struct uvc_video_queue *queue);
index 7f59a0c..a9bb455 100644 (file)
@@ -112,7 +112,8 @@ uvc_video_encode_bulk(struct usb_request *req, struct uvc_video *video,
        if (buf->bytesused == video->queue.buf_used) {
                video->queue.buf_used = 0;
                buf->state = UVC_BUF_STATE_DONE;
-               uvcg_queue_next_buffer(&video->queue, buf);
+               list_del(&buf->queue);
+               uvcg_complete_buffer(&video->queue, buf);
                video->fid ^= UVC_STREAM_FID;
 
                video->payload_size = 0;
@@ -154,7 +155,7 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
        sg = sg_next(sg);
 
        for_each_sg(sg, iter, ureq->sgt.nents - 1, i) {
-               if (!len || !buf->sg)
+               if (!len || !buf->sg || !sg_dma_len(buf->sg))
                        break;
 
                sg_left = sg_dma_len(buf->sg) - buf->offset;
@@ -183,8 +184,9 @@ uvc_video_encode_isoc_sg(struct usb_request *req, struct uvc_video *video,
                video->queue.buf_used = 0;
                buf->state = UVC_BUF_STATE_DONE;
                buf->offset = 0;
-               uvcg_queue_next_buffer(&video->queue, buf);
+               list_del(&buf->queue);
                video->fid ^= UVC_STREAM_FID;
+               ureq->last_buf = buf;
        }
 }
 
@@ -210,7 +212,8 @@ uvc_video_encode_isoc(struct usb_request *req, struct uvc_video *video,
        if (buf->bytesused == video->queue.buf_used) {
                video->queue.buf_used = 0;
                buf->state = UVC_BUF_STATE_DONE;
-               uvcg_queue_next_buffer(&video->queue, buf);
+               list_del(&buf->queue);
+               uvcg_complete_buffer(&video->queue, buf);
                video->fid ^= UVC_STREAM_FID;
        }
 }
@@ -264,6 +267,11 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
                uvcg_queue_cancel(queue, 0);
        }
 
+       if (ureq->last_buf) {
+               uvcg_complete_buffer(&video->queue, ureq->last_buf);
+               ureq->last_buf = NULL;
+       }
+
        spin_lock_irqsave(&video->req_lock, flags);
        list_add_tail(&req->list, &video->req_free);
        spin_unlock_irqrestore(&video->req_lock, flags);
@@ -332,6 +340,7 @@ uvc_video_alloc_requests(struct uvc_video *video)
                video->ureq[i].req->complete = uvc_video_complete;
                video->ureq[i].req->context = &video->ureq[i];
                video->ureq[i].video = video;
+               video->ureq[i].last_buf = NULL;
 
                list_add_tail(&video->ureq[i].req->list, &video->req_free);
                /* req_size/PAGE_SIZE + 1 for overruns and + 1 for header */
index 6bcbad3..b62e452 100644 (file)
@@ -422,7 +422,7 @@ static struct usb_gadget_driver dbgp_driver = {
 
 static int __init dbgp_init(void)
 {
-       return usb_gadget_probe_driver(&dbgp_driver);
+       return usb_gadget_register_driver(&dbgp_driver);
 }
 
 static void __exit dbgp_exit(void)
index 0c01e74..7999059 100644 (file)
@@ -1873,7 +1873,7 @@ dev_config (struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
        else
                gadgetfs_driver.max_speed = USB_SPEED_FULL;
 
-       value = usb_gadget_probe_driver(&gadgetfs_driver);
+       value = usb_gadget_register_driver(&gadgetfs_driver);
        if (value != 0) {
                spin_lock_irq(&dev->lock);
                goto fail;
index 8d40a1f..b3be8db 100644 (file)
@@ -510,12 +510,12 @@ static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
        }
        spin_unlock_irqrestore(&dev->lock, flags);
 
-       ret = usb_gadget_probe_driver(&dev->driver);
+       ret = usb_gadget_register_driver(&dev->driver);
 
        spin_lock_irqsave(&dev->lock, flags);
        if (ret) {
                dev_err(dev->dev,
-                       "fail, usb_gadget_probe_driver returned %d\n", ret);
+                       "fail, usb_gadget_register_driver returned %d\n", ret);
                dev->state = STATE_DEV_FAILED;
                goto out_unlock;
        }
index 85b1940..7886497 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/device.h>
 #include <linux/list.h>
+#include <linux/idr.h>
 #include <linux/err.h>
 #include <linux/dma-mapping.h>
 #include <linux/sched/task_stack.h>
 
 #include "trace.h"
 
+static DEFINE_IDA(gadget_id_numbers);
+
+static struct bus_type gadget_bus_type;
+
 /**
  * struct usb_udc - describes one usb device controller
  * @driver: the gadget driver pointer. For use by the class code
@@ -47,11 +52,9 @@ struct usb_udc {
 
 static struct class *udc_class;
 static LIST_HEAD(udc_list);
-static LIST_HEAD(gadget_driver_pending_list);
-static DEFINE_MUTEX(udc_lock);
 
-static int udc_bind_to_driver(struct usb_udc *udc,
-               struct usb_gadget_driver *driver);
+/* Protects udc_list, udc->driver, driver->is_bound, and related calls */
+static DEFINE_MUTEX(udc_lock);
 
 /* ------------------------------------------------------------------------- */
 
@@ -1238,38 +1241,16 @@ static void usb_udc_nop_release(struct device *dev)
        dev_vdbg(dev, "%s\n", __func__);
 }
 
-/* should be called with udc_lock held */
-static int check_pending_gadget_drivers(struct usb_udc *udc)
-{
-       struct usb_gadget_driver *driver;
-       int ret = 0;
-
-       list_for_each_entry(driver, &gadget_driver_pending_list, pending)
-               if (!driver->udc_name || strcmp(driver->udc_name,
-                                               dev_name(&udc->dev)) == 0) {
-                       ret = udc_bind_to_driver(udc, driver);
-                       if (ret != -EPROBE_DEFER)
-                               list_del_init(&driver->pending);
-                       break;
-               }
-
-       return ret;
-}
-
 /**
  * usb_initialize_gadget - initialize a gadget and its embedded struct device
  * @parent: the parent device to this udc. Usually the controller driver's
  * device.
  * @gadget: the gadget to be initialized.
  * @release: a gadget release function.
- *
- * Returns zero on success, negative errno otherwise.
- * Calls the gadget release function in the latter case.
  */
 void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
                void (*release)(struct device *dev))
 {
-       dev_set_name(&gadget->dev, "gadget");
        INIT_WORK(&gadget->work, usb_gadget_state_work);
        gadget->dev.parent = parent;
 
@@ -1279,6 +1260,7 @@ void usb_initialize_gadget(struct device *parent, struct usb_gadget *gadget,
                gadget->dev.release = usb_udc_nop_release;
 
        device_initialize(&gadget->dev);
+       gadget->dev.bus = &gadget_bus_type;
 }
 EXPORT_SYMBOL_GPL(usb_initialize_gadget);
 
@@ -1308,10 +1290,6 @@ int usb_add_gadget(struct usb_gadget *gadget)
        if (ret)
                goto err_put_udc;
 
-       ret = device_add(&gadget->dev);
-       if (ret)
-               goto err_put_udc;
-
        udc->gadget = gadget;
        gadget->udc = udc;
 
@@ -1319,6 +1297,7 @@ int usb_add_gadget(struct usb_gadget *gadget)
 
        mutex_lock(&udc_lock);
        list_add_tail(&udc->list, &udc_list);
+       mutex_unlock(&udc_lock);
 
        ret = device_add(&udc->dev);
        if (ret)
@@ -1327,25 +1306,30 @@ int usb_add_gadget(struct usb_gadget *gadget)
        usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
        udc->vbus = true;
 
-       /* pick up one of pending gadget drivers */
-       ret = check_pending_gadget_drivers(udc);
-       if (ret)
+       ret = ida_alloc(&gadget_id_numbers, GFP_KERNEL);
+       if (ret < 0)
                goto err_del_udc;
+       gadget->id_number = ret;
+       dev_set_name(&gadget->dev, "gadget.%d", ret);
 
-       mutex_unlock(&udc_lock);
+       ret = device_add(&gadget->dev);
+       if (ret)
+               goto err_free_id;
 
        return 0;
 
+ err_free_id:
+       ida_free(&gadget_id_numbers, gadget->id_number);
+
  err_del_udc:
        flush_work(&gadget->work);
        device_del(&udc->dev);
 
  err_unlist_udc:
+       mutex_lock(&udc_lock);
        list_del(&udc->list);
        mutex_unlock(&udc_lock);
 
-       device_del(&gadget->dev);
-
  err_put_udc:
        put_device(&udc->dev);
 
@@ -1421,30 +1405,11 @@ int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget)
 }
 EXPORT_SYMBOL_GPL(usb_add_gadget_udc);
 
-static void usb_gadget_remove_driver(struct usb_udc *udc)
-{
-       dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n",
-                       udc->driver->function);
-
-       kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
-
-       usb_gadget_disconnect(udc->gadget);
-       usb_gadget_disable_async_callbacks(udc);
-       if (udc->gadget->irq)
-               synchronize_irq(udc->gadget->irq);
-       udc->driver->unbind(udc->gadget);
-       usb_gadget_udc_stop(udc);
-
-       udc->driver = NULL;
-       udc->gadget->dev.driver = NULL;
-}
-
 /**
- * usb_del_gadget - deletes @udc from udc_list
- * @gadget: the gadget to be removed.
+ * usb_del_gadget - deletes a gadget and unregisters its udc
+ * @gadget: the gadget to be deleted.
  *
- * This will call usb_gadget_unregister_driver() if
- * the @udc is still busy.
+ * This will unbind @gadget, if it is bound.
  * It will not do a final usb_put_gadget().
  */
 void usb_del_gadget(struct usb_gadget *gadget)
@@ -1458,25 +1423,19 @@ void usb_del_gadget(struct usb_gadget *gadget)
 
        mutex_lock(&udc_lock);
        list_del(&udc->list);
-
-       if (udc->driver) {
-               struct usb_gadget_driver *driver = udc->driver;
-
-               usb_gadget_remove_driver(udc);
-               list_add(&driver->pending, &gadget_driver_pending_list);
-       }
        mutex_unlock(&udc_lock);
 
        kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE);
        flush_work(&gadget->work);
-       device_unregister(&udc->dev);
        device_del(&gadget->dev);
+       ida_free(&gadget_id_numbers, gadget->id_number);
+       device_unregister(&udc->dev);
 }
 EXPORT_SYMBOL_GPL(usb_del_gadget);
 
 /**
- * usb_del_gadget_udc - deletes @udc from udc_list
- * @gadget: the gadget to be removed.
+ * usb_del_gadget_udc - unregisters a gadget
+ * @gadget: the gadget to be unregistered.
  *
  * Calls usb_del_gadget() and does a final usb_put_gadget().
  */
@@ -1489,123 +1448,147 @@ EXPORT_SYMBOL_GPL(usb_del_gadget_udc);
 
 /* ------------------------------------------------------------------------- */
 
-static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver)
+static int gadget_match_driver(struct device *dev, struct device_driver *drv)
 {
-       int ret;
+       struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+       struct usb_udc *udc = gadget->udc;
+       struct usb_gadget_driver *driver = container_of(drv,
+                       struct usb_gadget_driver, driver);
 
-       dev_dbg(&udc->dev, "registering UDC driver [%s]\n",
-                       driver->function);
+       /* If the driver specifies a udc_name, it must match the UDC's name */
+       if (driver->udc_name &&
+                       strcmp(driver->udc_name, dev_name(&udc->dev)) != 0)
+               return 0;
+
+       /* If the driver is already bound to a gadget, it doesn't match */
+       if (driver->is_bound)
+               return 0;
+
+       /* Otherwise any gadget driver matches any UDC */
+       return 1;
+}
 
+static int gadget_bind_driver(struct device *dev)
+{
+       struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+       struct usb_udc *udc = gadget->udc;
+       struct usb_gadget_driver *driver = container_of(dev->driver,
+                       struct usb_gadget_driver, driver);
+       int ret = 0;
+
+       mutex_lock(&udc_lock);
+       if (driver->is_bound) {
+               mutex_unlock(&udc_lock);
+               return -ENXIO;          /* Driver binds to only one gadget */
+       }
+       driver->is_bound = true;
        udc->driver = driver;
-       udc->gadget->dev.driver = &driver->driver;
+       mutex_unlock(&udc_lock);
+
+       dev_dbg(&udc->dev, "binding gadget driver [%s]\n", driver->function);
 
        usb_gadget_udc_set_speed(udc, driver->max_speed);
 
+       mutex_lock(&udc_lock);
        ret = driver->bind(udc->gadget, driver);
        if (ret)
-               goto err1;
+               goto err_bind;
+
        ret = usb_gadget_udc_start(udc);
-       if (ret) {
-               driver->unbind(udc->gadget);
-               goto err1;
-       }
+       if (ret)
+               goto err_start;
        usb_gadget_enable_async_callbacks(udc);
        usb_udc_connect_control(udc);
+       mutex_unlock(&udc_lock);
 
        kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
        return 0;
-err1:
+
+ err_start:
+       driver->unbind(udc->gadget);
+
+ err_bind:
        if (ret != -EISNAM)
                dev_err(&udc->dev, "failed to start %s: %d\n",
-                       udc->driver->function, ret);
+                       driver->function, ret);
+
        udc->driver = NULL;
-       udc->gadget->dev.driver = NULL;
+       driver->is_bound = false;
+       mutex_unlock(&udc_lock);
+
        return ret;
 }
 
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver)
+static void gadget_unbind_driver(struct device *dev)
 {
-       struct usb_udc          *udc = NULL, *iter;
-       int                     ret = -ENODEV;
+       struct usb_gadget *gadget = dev_to_usb_gadget(dev);
+       struct usb_udc *udc = gadget->udc;
+       struct usb_gadget_driver *driver = udc->driver;
+
+       dev_dbg(&udc->dev, "unbinding gadget driver [%s]\n", driver->function);
+
+       kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
+
+       mutex_lock(&udc_lock);
+       usb_gadget_disconnect(gadget);
+       usb_gadget_disable_async_callbacks(udc);
+       if (gadget->irq)
+               synchronize_irq(gadget->irq);
+       udc->driver->unbind(gadget);
+       usb_gadget_udc_stop(udc);
+
+       driver->is_bound = false;
+       udc->driver = NULL;
+       mutex_unlock(&udc_lock);
+}
+
+/* ------------------------------------------------------------------------- */
+
+int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver,
+               struct module *owner, const char *mod_name)
+{
+       int ret;
 
        if (!driver || !driver->bind || !driver->setup)
                return -EINVAL;
 
+       driver->driver.bus = &gadget_bus_type;
+       driver->driver.owner = owner;
+       driver->driver.mod_name = mod_name;
+       ret = driver_register(&driver->driver);
+       if (ret) {
+               pr_warn("%s: driver registration failed: %d\n",
+                               driver->function, ret);
+               return ret;
+       }
+
        mutex_lock(&udc_lock);
-       if (driver->udc_name) {
-               list_for_each_entry(iter, &udc_list, list) {
-                       ret = strcmp(driver->udc_name, dev_name(&iter->dev));
-                       if (ret)
-                               continue;
-                       udc = iter;
-                       break;
-               }
-               if (ret)
-                       ret = -ENODEV;
-               else if (udc->driver)
+       if (!driver->is_bound) {
+               if (driver->match_existing_only) {
+                       pr_warn("%s: couldn't find an available UDC or it's busy\n",
+                                       driver->function);
                        ret = -EBUSY;
-               else
-                       goto found;
-       } else {
-               list_for_each_entry(iter, &udc_list, list) {
-                       /* For now we take the first one */
-                       if (iter->driver)
-                               continue;
-                       udc = iter;
-                       goto found;
+               } else {
+                       pr_info("%s: couldn't find an available UDC\n",
+                                       driver->function);
+                       ret = 0;
                }
        }
-
-       if (!driver->match_existing_only) {
-               list_add_tail(&driver->pending, &gadget_driver_pending_list);
-               pr_info("couldn't find an available UDC - added [%s] to list of pending drivers\n",
-                       driver->function);
-               ret = 0;
-       }
-
        mutex_unlock(&udc_lock);
+
        if (ret)
-               pr_warn("couldn't find an available UDC or it's busy: %d\n", ret);
-       return ret;
-found:
-       ret = udc_bind_to_driver(udc, driver);
-       mutex_unlock(&udc_lock);
+               driver_unregister(&driver->driver);
        return ret;
 }
-EXPORT_SYMBOL_GPL(usb_gadget_probe_driver);
+EXPORT_SYMBOL_GPL(usb_gadget_register_driver_owner);
 
 int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
 {
-       struct usb_udc          *udc = NULL;
-       int                     ret = -ENODEV;
-
        if (!driver || !driver->unbind)
                return -EINVAL;
 
-       mutex_lock(&udc_lock);
-       list_for_each_entry(udc, &udc_list, list) {
-               if (udc->driver == driver) {
-                       usb_gadget_remove_driver(udc);
-                       usb_gadget_set_state(udc->gadget,
-                                            USB_STATE_NOTATTACHED);
-
-                       /* Maybe there is someone waiting for this UDC? */
-                       check_pending_gadget_drivers(udc);
-                       /*
-                        * For now we ignore bind errors as probably it's
-                        * not a valid reason to fail other's gadget unbind
-                        */
-                       ret = 0;
-                       break;
-               }
-       }
-
-       if (ret) {
-               list_del(&driver->pending);
-               ret = 0;
-       }
-       mutex_unlock(&udc_lock);
-       return ret;
+       driver_unregister(&driver->driver);
+       return 0;
 }
 EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver);
 
@@ -1757,8 +1740,17 @@ static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env)
        return 0;
 }
 
+static struct bus_type gadget_bus_type = {
+       .name = "gadget",
+       .probe = gadget_bind_driver,
+       .remove = gadget_unbind_driver,
+       .match = gadget_match_driver,
+};
+
 static int __init usb_udc_init(void)
 {
+       int rc;
+
        udc_class = class_create(THIS_MODULE, "udc");
        if (IS_ERR(udc_class)) {
                pr_err("failed to create udc class --> %ld\n",
@@ -1767,12 +1759,17 @@ static int __init usb_udc_init(void)
        }
 
        udc_class->dev_uevent = usb_udc_uevent;
-       return 0;
+
+       rc = bus_register(&gadget_bus_type);
+       if (rc)
+               class_destroy(udc_class);
+       return rc;
 }
 subsys_initcall(usb_udc_init);
 
 static void __exit usb_udc_exit(void)
 {
+       bus_unregister(&gadget_bus_type);
        class_destroy(udc_class);
 }
 module_exit(usb_udc_exit);
index 6a88846..c97cd4b 100644 (file)
@@ -71,7 +71,7 @@ static ushort dma_ep = 1;
 module_param(dma_ep, ushort, 0644);
 
 /*
- * dma_mode: net2272 dma mode setting (see LOCCTL1 definiton):
+ * dma_mode: net2272 dma mode setting (see LOCCTL1 definition):
  *     mode 0 == Slow DREQ mode
  *     mode 1 == Fast DREQ mode
  *     mode 2 == Burst mode
@@ -97,7 +97,7 @@ module_param(fifo_mode, ushort, 0644);
 /*
  * enable_suspend: When enabled, the driver will respond to
  * USB suspend requests by powering down the NET2272.  Otherwise,
- * USB suspend requests will be ignored.  This is acceptible for
+ * USB suspend requests will be ignored.  This is acceptable for
  * self-powered devices.  For bus powered devices set this to 1.
  */
 static ushort enable_suspend = 0;
@@ -288,7 +288,7 @@ static void net2272_ep_reset(struct net2272_ep *ep)
                          | (1 << LOCAL_OUT_ZLP)
                          | (1 << BUFFER_FLUSH));
 
-       /* fifo size is handled seperately */
+       /* fifo size is handled separately */
 }
 
 static int net2272_disable(struct usb_ep *_ep)
index 051d024..d6a6863 100644 (file)
@@ -932,19 +932,11 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
 static inline void
 queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
 {
-       struct net2280_dma      *end;
-       dma_addr_t              tmp;
-
        /* swap new dummy for old, link; fill and maybe activate */
-       end = ep->dummy;
-       ep->dummy = req->td;
-       req->td = end;
-
-       tmp = ep->td_dma;
-       ep->td_dma = req->td_dma;
-       req->td_dma = tmp;
+       swap(ep->dummy, req->td);
+       swap(ep->td_dma, req->td_dma);
 
-       end->dmadesc = cpu_to_le32 (ep->td_dma);
+       req->td->dmadesc = cpu_to_le32 (ep->td_dma);
 
        fill_dma_desc(ep, req, valid);
 }
index 2d9815d..54c3363 100644 (file)
@@ -1467,7 +1467,7 @@ static void ep0_irq(struct omap_udc *udc, u16 irq_src)
                        if (!udc->ep0_in) {
                                stat = 0;
                                /* read next OUT packet of request, maybe
-                                * reactiviting the fifo; stall on errors.
+                                * reactivating the fifo; stall on errors.
                                 */
                                stat = read_fifo(ep0, req);
                                if (!req || stat < 0) {
@@ -2606,6 +2606,8 @@ static void omap_udc_release(struct device *dev)
        if (udc->dc_clk) {
                if (udc->clk_requested)
                        omap_udc_enable_clock(0);
+               clk_unprepare(udc->hhc_clk);
+               clk_unprepare(udc->dc_clk);
                clk_put(udc->hhc_clk);
                clk_put(udc->dc_clk);
        }
@@ -2770,8 +2772,8 @@ static int omap_udc_probe(struct platform_device *pdev)
                hhc_clk = clk_get(&pdev->dev, "usb_hhc_ck");
                BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
                /* can't use omap_udc_enable_clock yet */
-               clk_enable(dc_clk);
-               clk_enable(hhc_clk);
+               clk_prepare_enable(dc_clk);
+               clk_prepare_enable(hhc_clk);
                udelay(100);
        }
 
@@ -2780,8 +2782,8 @@ static int omap_udc_probe(struct platform_device *pdev)
                hhc_clk = clk_get(&pdev->dev, "l3_ocpi_ck");
                BUG_ON(IS_ERR(dc_clk) || IS_ERR(hhc_clk));
                /* can't use omap_udc_enable_clock yet */
-               clk_enable(dc_clk);
-               clk_enable(hhc_clk);
+               clk_prepare_enable(dc_clk);
+               clk_prepare_enable(hhc_clk);
                udelay(100);
        }
 
@@ -2929,8 +2931,8 @@ cleanup0:
                usb_put_phy(xceiv);
 
        if (cpu_is_omap16xx() || cpu_is_omap7xx()) {
-               clk_disable(hhc_clk);
-               clk_disable(dc_clk);
+               clk_disable_unprepare(hhc_clk);
+               clk_disable_unprepare(dc_clk);
                clk_put(hhc_clk);
                clk_put(dc_clk);
        }
index 0a6bc18..31bf79c 100644 (file)
@@ -326,7 +326,7 @@ struct udc_usb_ep {
  * @addr: usb endpoint number
  * @config: configuration in which this endpoint is active
  * @interface: interface in which this endpoint is active
- * @alternate: altsetting in which this endpoitn is active
+ * @alternate: altsetting in which this endpoint is active
  * @fifo_size: max packet size in the endpoint fifo
  * @type: endpoint type (bulk, iso, int, ...)
  * @udccsr_value: save register of UDCCSR0 for suspend/resume
index bf803e0..4b7eb77 100644 (file)
@@ -126,7 +126,7 @@ struct s3c_hsudc_req {
 /**
  * struct s3c_hsudc - Driver's abstraction of the device controller.
  * @gadget: Instance of usb_gadget which is referenced by gadget driver.
- * @driver: Reference to currenty active gadget driver.
+ * @driver: Reference to currently active gadget driver.
  * @dev: The device reference used by probe function.
  * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
  * @regs: Remapped base address of controller's register space.
@@ -633,7 +633,7 @@ static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
 }
 
 /** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
- * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occurred.
  *
  * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
  * when a stall handshake is sent to host or data is sent/received on
index d9c406b..6d31ccf 100644 (file)
@@ -1434,7 +1434,7 @@ __tegra_xudc_ep_dequeue(struct tegra_xudc_ep *ep,
                return 0;
        }
 
-       /* Halt DMA for this endpiont. */
+       /* Halt DMA for this endpoint. */
        if (ep_ctx_read_state(ep->context) == EP_STATE_RUNNING) {
                ep_pause(xudc, ep->index);
                ep_wait_for_inactive(xudc, ep->index);
@@ -3423,7 +3423,7 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
        }
 
        /*
-        * Compliacne suite appears to be violating polling LFPS tBurst max
+        * Compliance suite appears to be violating polling LFPS tBurst max
         * of 1.4us.  Send 1.45us instead.
         */
        val = xudc_readl(xudc, SSPX_CORE_CNT32);
index 428c755..4827e3c 100644 (file)
@@ -632,7 +632,7 @@ top:
                dev_dbg(udc->dev, "read %s, %d bytes%s req %p %d/%d\n",
                        ep->ep_usb.name, count, is_short ? "/S" : "", req,
                        req->usb_req.actual, req->usb_req.length);
-               bufferspace -= count;
+
                /* Completion */
                if ((req->usb_req.actual == req->usb_req.length) || is_short) {
                        if (udc->dma_enabled && req->usb_req.length)
index 7f4a03e..8c45bc1 100644 (file)
@@ -61,11 +61,6 @@ static inline void ehci_write(void __iomem *base, u32 reg, u32 val)
        __raw_writel(val, base + reg);
 }
 
-static inline u32 ehci_read(void __iomem *base, u32 reg)
-{
-       return __raw_readl(base + reg);
-}
-
 /* configure so an HC device and id are always provided */
 /* always called with process context; sleeping is OK */
 
index 1115431..f343967 100644 (file)
@@ -518,6 +518,7 @@ static struct platform_driver ehci_platform_driver = {
                .pm     = pm_ptr(&ehci_platform_pm_ops),
                .of_match_table = vt8500_ehci_ids,
                .acpi_match_table = ACPI_PTR(ehci_acpi_match),
+               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
        }
 };
 
index a2a5c29..1163af6 100644 (file)
@@ -645,7 +645,7 @@ qh_urb_transaction (
                token |= (1 /* "in" */ << 8);
        /* else it's already initted to "out" pid (0 << 8) */
 
-       maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+       maxpacket = usb_maxpacket(urb->dev, urb->pipe);
 
        /*
         * buffer gets wrapped in one or more qtds;
@@ -1218,7 +1218,7 @@ static int ehci_submit_single_step_set_feature(
 
        token |= (1 /* "in" */ << 8);  /*This is IN stage*/
 
-       maxpacket = usb_maxpacket(urb->dev, urb->pipe, 0);
+       maxpacket = usb_maxpacket(urb->dev, urb->pipe);
 
        qtd_fill(ehci, qtd, buf, len, token, maxpacket);
 
index 67a6ee8..3d78937 100644 (file)
@@ -32,6 +32,8 @@
  * There are cases when the host controller fails to enable the port due to,
  * for example, insufficient power that can be supplied to the device from
  * the USB bus. In those cases, the messages printed here are not helpful.
+ *
+ * Return: Always return 0
  */
 static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum)
 {
@@ -46,11 +48,9 @@ static int ehci_xilinx_port_handed_over(struct usb_hcd *hcd, int portnum)
                dev_warn(hcd->self.controller,
                        "Maybe your device is not a high speed device?\n");
                dev_warn(hcd->self.controller,
-                       "The USB host controller does not support full speed "
-                       "nor low speed devices\n");
+                       "The USB host controller does not support full speed nor low speed devices\n");
                dev_warn(hcd->self.controller,
-                       "You can reconfigure the host controller to have "
-                       "full speed support\n");
+                       "You can reconfigure the host controller to have full speed support\n");
        }
 
        return 0;
@@ -112,6 +112,8 @@ static const struct hc_driver ehci_xilinx_of_hc_driver = {
  * host controller. Because the Xilinx USB host controller can be configured
  * as HS only or HS/FS only, it checks the configuration in the device tree
  * entry, and sets an appropriate value for hcd->has_tt.
+ *
+ * Return: zero on success, negative error code otherwise
  */
 static int ehci_hcd_xilinx_of_probe(struct platform_device *op)
 {
@@ -196,6 +198,8 @@ err_irq:
  *
  * Remove the hcd structure, and release resources that has been requested
  * during probe.
+ *
+ * Return: Always return 0
  */
 static int ehci_hcd_xilinx_of_remove(struct platform_device *op)
 {
index a8e1048..2ba09c3 100644 (file)
@@ -408,8 +408,7 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
                        size++;
                else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
                         && (urb->transfer_buffer_length
-                            % usb_maxpacket(urb->dev, pipe,
-                                            usb_pipeout(pipe))) != 0)
+                            % usb_maxpacket(urb->dev, pipe)) != 0)
                        size++;
                break;
        case PIPE_ISOCHRONOUS:
index c3fd375..f8c111e 100644 (file)
@@ -2596,7 +2596,7 @@ static struct list_head *qh_urb_transaction(struct fotg210_hcd *fotg210,
                token |= (1 /* "in" */ << 8);
        /* else it's already initted to "out" pid (0 << 8) */
 
-       maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+       maxpacket = usb_maxpacket(urb->dev, urb->pipe);
 
        /*
         * buffer gets wrapped in one or more qtds;
index 8835f6b..4f564d7 100644 (file)
@@ -726,7 +726,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
                INIT_LIST_HEAD(&ep->schedule);
                ep->udev = udev;
                ep->epnum = epnum;
-               ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+               ep->maxpacket = usb_maxpacket(udev, urb->pipe);
                usb_settoggle(udev, epnum, is_out, 0);
 
                if (type == PIPE_CONTROL) {
@@ -757,8 +757,7 @@ static int isp116x_urb_enqueue(struct usb_hcd *hcd,
                        ep->load = usb_calc_bus_time(udev->speed,
                                                     !is_out,
                                                     (type == PIPE_ISOCHRONOUS),
-                                                    usb_maxpacket(udev, pipe,
-                                                                  is_out)) /
+                                                    usb_maxpacket(udev, pipe)) /
                            1000;
                }
                hep->hcpriv = ep;
@@ -1541,10 +1540,12 @@ static int isp116x_remove(struct platform_device *pdev)
 
        iounmap(isp116x->data_reg);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       release_mem_region(res->start, 2);
+       if (res)
+               release_mem_region(res->start, 2);
        iounmap(isp116x->addr_reg);
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       release_mem_region(res->start, 2);
+       if (res)
+               release_mem_region(res->start, 2);
 
        usb_put_hcd(hcd);
        return 0;
index d8610ce..0e14d1d 100644 (file)
@@ -1279,7 +1279,7 @@ static int isp1362_urb_enqueue(struct usb_hcd *hcd,
                ep->udev = usb_get_dev(udev);
                ep->hep = hep;
                ep->epnum = epnum;
-               ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+               ep->maxpacket = usb_maxpacket(udev, urb->pipe);
                ep->ptd_offset = -EINVAL;
                ep->ptd_index = -EINVAL;
                usb_settoggle(udev, epnum, is_out, 0);
@@ -1299,8 +1299,8 @@ static int isp1362_urb_enqueue(struct usb_hcd *hcd,
                        ep->interval = urb->interval;
                        ep->branch = PERIODIC_SIZE;
                        ep->load = usb_calc_bus_time(udev->speed, !is_out,
-                                                    (type == PIPE_ISOCHRONOUS),
-                                                    usb_maxpacket(udev, pipe, is_out)) / 1000;
+                                                    type == PIPE_ISOCHRONOUS,
+                                                    usb_maxpacket(udev, pipe)) / 1000;
                        break;
                }
                hep->hcpriv = ep;
index 99a5523..502a3ac 100644 (file)
@@ -546,7 +546,7 @@ max3421_transfer_out(struct usb_hcd *hcd, struct urb *urb, int fast_retransmit)
                return MAX3421_HXFR_BULK_OUT(epnum);
        }
 
-       max_packet = usb_maxpacket(urb->dev, urb->pipe, 1);
+       max_packet = usb_maxpacket(urb->dev, urb->pipe);
 
        if (max_packet > MAX3421_FIFO_SIZE) {
                /*
@@ -952,7 +952,7 @@ max3421_transfer_in_done(struct usb_hcd *hcd, struct urb *urb)
         * USB 2.0 Section 5.3.2 Pipes: packets must be full size
         * except for last one.
         */
-       max_packet = usb_maxpacket(urb->dev, urb->pipe, 0);
+       max_packet = usb_maxpacket(urb->dev, urb->pipe);
        if (max_packet > MAX3421_FIFO_SIZE) {
                /*
                 * We do not support isochronous transfers at this
@@ -998,7 +998,7 @@ max3421_transfer_out_done(struct usb_hcd *hcd, struct urb *urb)
                 * max_packet as an indicator that the end of the
                 * packet has been reached).
                 */
-               u32 max_packet = usb_maxpacket(urb->dev, urb->pipe, 1);
+               u32 max_packet = usb_maxpacket(urb->dev, urb->pipe);
 
                if (max3421_hcd->curr_len == max_packet)
                        return 0;
index 666b1c6..c4c821c 100644 (file)
@@ -181,8 +181,7 @@ static int ohci_urb_enqueue (
                                size++;
                        else if ((urb->transfer_flags & URB_ZERO_PACKET) != 0
                                && (urb->transfer_buffer_length
-                                       % usb_maxpacket (urb->dev, pipe,
-                                               usb_pipeout (pipe))) == 0)
+                                       % usb_maxpacket(urb->dev, pipe)) == 0)
                                size++;
                        break;
                case PIPE_ISOCHRONOUS: /* number of packets from URB */
index 45dcf82..2ab2e08 100644 (file)
@@ -281,6 +281,10 @@ static int ohci_hcd_omap_probe(struct platform_device *pdev)
                goto err_put_hcd;
        }
 
+       retval = clk_prepare(priv->usb_host_ck);
+       if (retval)
+               goto err_put_host_ck;
+
        if (!cpu_is_omap15xx())
                priv->usb_dc_ck = clk_get(&pdev->dev, "usb_dc_ck");
        else
@@ -288,13 +292,17 @@ static int ohci_hcd_omap_probe(struct platform_device *pdev)
 
        if (IS_ERR(priv->usb_dc_ck)) {
                retval = PTR_ERR(priv->usb_dc_ck);
-               goto err_put_host_ck;
+               goto err_unprepare_host_ck;
        }
 
+       retval = clk_prepare(priv->usb_dc_ck);
+       if (retval)
+               goto err_put_dc_ck;
+
        if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
                dev_dbg(&pdev->dev, "request_mem_region failed\n");
                retval = -EBUSY;
-               goto err_put_dc_ck;
+               goto err_unprepare_dc_ck;
        }
 
        hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len);
@@ -319,8 +327,12 @@ err3:
        iounmap(hcd->regs);
 err2:
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+err_unprepare_dc_ck:
+       clk_unprepare(priv->usb_dc_ck);
 err_put_dc_ck:
        clk_put(priv->usb_dc_ck);
+err_unprepare_host_ck:
+       clk_unprepare(priv->usb_host_ck);
 err_put_host_ck:
        clk_put(priv->usb_host_ck);
 err_put_hcd:
@@ -355,7 +367,9 @@ static int ohci_hcd_omap_remove(struct platform_device *pdev)
        }
        iounmap(hcd->regs);
        release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
+       clk_unprepare(priv->usb_dc_ck);
        clk_put(priv->usb_dc_ck);
+       clk_unprepare(priv->usb_host_ck);
        clk_put(priv->usb_host_ck);
        usb_put_hcd(hcd);
        return 0;
index 4a8456f..47dfbfe 100644 (file)
@@ -334,6 +334,7 @@ static struct platform_driver ohci_platform_driver = {
                .name   = "ohci-platform",
                .pm     = &ohci_platform_pm_ops,
                .of_match_table = ohci_platform_ids,
+               .probe_type = PROBE_PREFER_ASYNCHRONOUS,
        }
 };
 
index 45f7cce..1960b8d 100644 (file)
@@ -19,9 +19,6 @@
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 
-#include <asm/prom.h>
-
-
 static int
 ohci_ppc_of_start(struct usb_hcd *hcd)
 {
index b741670..3a44131 100644 (file)
@@ -1685,7 +1685,7 @@ static struct list_head *qh_urb_transaction(struct oxu_hcd *oxu,
                token |= (1 /* "in" */ << 8);
        /* else it's already initted to "out" pid (0 << 8) */
 
-       maxpacket = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+       maxpacket = usb_maxpacket(urb->dev, urb->pipe);
 
        /*
         * buffer gets wrapped in one or more qtds;
@@ -1796,7 +1796,7 @@ static struct ehci_qh *qh_make(struct oxu_hcd *oxu,
 
        is_input = usb_pipein(urb->pipe);
        type = usb_pipetype(urb->pipe);
-       maxp = usb_maxpacket(urb->dev, urb->pipe, !is_input);
+       maxp = usb_maxpacket(urb->dev, urb->pipe);
 
        /* Compute interrupt scheduling parameters just once, and save.
         * - allowing for high bandwidth, how many nsec/uframe are used?
@@ -3909,8 +3909,10 @@ static int oxu_bus_suspend(struct usb_hcd *hcd)
                }
        }
 
+       spin_unlock_irq(&oxu->lock);
        /* turn off now-idle HC */
        del_timer_sync(&oxu->watchdog);
+       spin_lock_irq(&oxu->lock);
        ehci_halt(oxu);
        hcd->state = HC_STATE_SUSPENDED;
 
@@ -4223,13 +4225,9 @@ static int oxu_drv_probe(struct platform_device *pdev)
        /*
         * Get the platform resources
         */
-       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!res) {
-               dev_err(&pdev->dev,
-                       "no IRQ! Check %s setup!\n", dev_name(&pdev->dev));
-               return -ENODEV;
-       }
-       irq = res->start;
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
        dev_dbg(&pdev->dev, "IRQ resource %d\n", irq);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
index 63719cd..abb88dd 100644 (file)
@@ -1867,8 +1867,7 @@ static struct r8a66597_td *r8a66597_make_td(struct r8a66597 *r8a66597,
        td->pipe = hep->hcpriv;
        td->urb = urb;
        td->address = get_urb_to_r8a66597_addr(r8a66597, urb);
-       td->maxpacket = usb_maxpacket(urb->dev, urb->pipe,
-                                     !usb_pipein(urb->pipe));
+       td->maxpacket = usb_maxpacket(urb->dev, urb->pipe);
        if (usb_pipecontrol(urb->pipe))
                td->type = USB_PID_SETUP;
        else if (usb_pipein(urb->pipe))
index 8562373..d206bd9 100644 (file)
@@ -842,7 +842,7 @@ static int sl811h_urb_enqueue(
                INIT_LIST_HEAD(&ep->schedule);
                ep->udev = udev;
                ep->epnum = epnum;
-               ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out);
+               ep->maxpacket = usb_maxpacket(udev, urb->pipe);
                ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE;
                usb_settoggle(udev, epnum, is_out, 0);
 
@@ -878,8 +878,8 @@ static int sl811h_urb_enqueue(
                        if (type == PIPE_ISOCHRONOUS)
                                ep->defctrl |= SL11H_HCTLMASK_ISOCH;
                        ep->load = usb_calc_bus_time(udev->speed, !is_out,
-                               (type == PIPE_ISOCHRONOUS),
-                               usb_maxpacket(udev, pipe, is_out))
+                                                    type == PIPE_ISOCHRONOUS,
+                                                    usb_maxpacket(udev, pipe))
                                        / 1000;
                        break;
                }
index f65f1ba..c54f2bc 100644 (file)
@@ -707,6 +707,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
                                u16 test_mode, u16 wIndex, unsigned long *flags)
        __must_hold(&xhci->lock)
 {
+       struct usb_hcd *usb3_hcd = xhci_get_usb3_hcd(xhci);
        int i, retval;
 
        /* Disable all Device Slots */
@@ -727,7 +728,7 @@ static int xhci_enter_test_mode(struct xhci_hcd *xhci,
        xhci_dbg(xhci, "Disable all port (PP = 0)\n");
        /* Power off USB3 ports*/
        for (i = 0; i < xhci->usb3_rhub.num_ports; i++)
-               xhci_set_port_power(xhci, xhci->shared_hcd, i, false, flags);
+               xhci_set_port_power(xhci, usb3_hcd, i, false, flags);
        /* Power off USB2 ports*/
        for (i = 0; i < xhci->usb2_rhub.num_ports; i++)
                xhci_set_port_power(xhci, xhci->main_hcd, i, false, flags);
index bbb27ee..8c19e15 100644 (file)
@@ -782,14 +782,6 @@ void xhci_free_stream_info(struct xhci_hcd *xhci,
 
 /***************** Device context manipulation *************************/
 
-static void xhci_init_endpoint_timer(struct xhci_hcd *xhci,
-               struct xhci_virt_ep *ep)
-{
-       timer_setup(&ep->stop_cmd_timer, xhci_stop_endpoint_command_watchdog,
-                   0);
-       ep->xhci = xhci;
-}
-
 static void xhci_free_tt_info(struct xhci_hcd *xhci,
                struct xhci_virt_device *virt_dev,
                int slot_id)
@@ -994,11 +986,11 @@ int xhci_alloc_virt_device(struct xhci_hcd *xhci, int slot_id,
        xhci_dbg(xhci, "Slot %d input ctx = 0x%llx (dma)\n", slot_id,
                        (unsigned long long)dev->in_ctx->dma);
 
-       /* Initialize the cancellation list and watchdog timers for each ep */
+       /* Initialize the cancellation and bandwidth list for each ep */
        for (i = 0; i < 31; i++) {
                dev->eps[i].ep_index = i;
                dev->eps[i].vdev = dev;
-               xhci_init_endpoint_timer(xhci, &dev->eps[i]);
+               dev->eps[i].xhci = xhci;
                INIT_LIST_HEAD(&dev->eps[i].cancelled_td_list);
                INIT_LIST_HEAD(&dev->eps[i].bw_endpoint_list);
        }
@@ -1072,7 +1064,7 @@ static u32 xhci_find_real_port_number(struct xhci_hcd *xhci,
        struct usb_hcd *hcd;
 
        if (udev->speed >= USB_SPEED_SUPER)
-               hcd = xhci->shared_hcd;
+               hcd = xhci_get_usb3_hcd(xhci);
        else
                hcd = xhci->main_hcd;
 
@@ -2362,10 +2354,11 @@ static int xhci_setup_port_arrays(struct xhci_hcd *xhci, gfp_t flags)
                xhci->usb2_rhub.num_ports = USB_MAXCHILDREN;
        }
 
-       /*
-        * Note we could have all USB 3.0 ports, or all USB 2.0 ports.
-        * Not sure how the USB core will handle a hub with no ports...
-        */
+       if (!xhci->usb2_rhub.num_ports)
+               xhci_info(xhci, "USB2 root hub has no ports\n");
+
+       if (!xhci->usb3_rhub.num_ports)
+               xhci_info(xhci, "USB3 root hub has no ports\n");
 
        xhci_create_rhub_port_array(xhci, &xhci->usb2_rhub, flags);
        xhci_create_rhub_port_array(xhci, &xhci->usb3_rhub, flags);
index d7e0e6e..fac9492 100644 (file)
@@ -59,6 +59,7 @@
 #define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI            0x9a13
 #define PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI           0x1138
 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI            0x461e
+#define PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_XHCI          0x464e
 #define PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI        0x51ed
 
 #define PCI_DEVICE_ID_AMD_RENOIR_XHCI                  0x1639
@@ -129,8 +130,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                                pdev->revision == 0x0) {
                        xhci->quirks |= XHCI_RESET_EP_QUIRK;
                        xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
-                               "QUIRK: Fresco Logic xHC needs configure"
-                               " endpoint cmd after reset endpoint");
+                               "XHCI_RESET_EP_QUIRK for this evaluation HW is deprecated");
                }
                if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK &&
                                pdev->revision == 0x4) {
@@ -268,6 +268,7 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
             pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI ||
             pdev->device == PCI_DEVICE_ID_INTEL_MAPLE_RIDGE_XHCI ||
             pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_XHCI ||
+            pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_N_XHCI ||
             pdev->device == PCI_DEVICE_ID_INTEL_ALDER_LAKE_PCH_XHCI))
                xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
 
index 649ffd8..0448558 100644 (file)
@@ -180,7 +180,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
        struct device           *sysdev, *tmpdev;
        struct xhci_hcd         *xhci;
        struct resource         *res;
-       struct usb_hcd          *hcd;
+       struct usb_hcd          *hcd, *usb3_hcd;
        int                     ret;
        int                     irq;
        struct xhci_plat_priv   *priv = NULL;
@@ -245,6 +245,8 @@ static int xhci_plat_probe(struct platform_device *pdev)
 
        xhci = hcd_to_xhci(hcd);
 
+       xhci->allow_single_roothub = 1;
+
        /*
         * Not all platforms have clks so it is not an error if the
         * clock do not exist.
@@ -283,12 +285,6 @@ static int xhci_plat_probe(struct platform_device *pdev)
        device_set_wakeup_capable(&pdev->dev, true);
 
        xhci->main_hcd = hcd;
-       xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
-                       dev_name(&pdev->dev), hcd);
-       if (!xhci->shared_hcd) {
-               ret = -ENOMEM;
-               goto disable_clk;
-       }
 
        /* imod_interval is the interrupt moderation value in nanoseconds. */
        xhci->imod_interval = 40000;
@@ -313,16 +309,16 @@ static int xhci_plat_probe(struct platform_device *pdev)
        if (IS_ERR(hcd->usb_phy)) {
                ret = PTR_ERR(hcd->usb_phy);
                if (ret == -EPROBE_DEFER)
-                       goto put_usb3_hcd;
+                       goto disable_clk;
                hcd->usb_phy = NULL;
        } else {
                ret = usb_phy_init(hcd->usb_phy);
                if (ret)
-                       goto put_usb3_hcd;
+                       goto disable_clk;
        }
 
        hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
-       xhci->shared_hcd->tpl_support = hcd->tpl_support;
+
        if (priv && (priv->quirks & XHCI_SKIP_PHY_INIT))
                hcd->skip_phy_initialization = 1;
 
@@ -333,12 +329,26 @@ static int xhci_plat_probe(struct platform_device *pdev)
        if (ret)
                goto disable_usb_phy;
 
-       if (HCC_MAX_PSA(xhci->hcc_params) >= 4)
-               xhci->shared_hcd->can_do_streams = 1;
+       if (!xhci_has_one_roothub(xhci)) {
+               xhci->shared_hcd = __usb_create_hcd(driver, sysdev, &pdev->dev,
+                                                   dev_name(&pdev->dev), hcd);
+               if (!xhci->shared_hcd) {
+                       ret = -ENOMEM;
+                       goto dealloc_usb2_hcd;
+               }
 
-       ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
-       if (ret)
-               goto dealloc_usb2_hcd;
+               xhci->shared_hcd->tpl_support = hcd->tpl_support;
+       }
+
+       usb3_hcd = xhci_get_usb3_hcd(xhci);
+       if (usb3_hcd && HCC_MAX_PSA(xhci->hcc_params) >= 4)
+               usb3_hcd->can_do_streams = 1;
+
+       if (xhci->shared_hcd) {
+               ret = usb_add_hcd(xhci->shared_hcd, irq, IRQF_SHARED);
+               if (ret)
+                       goto put_usb3_hcd;
+       }
 
        device_enable_async_suspend(&pdev->dev);
        pm_runtime_put_noidle(&pdev->dev);
@@ -352,15 +362,15 @@ static int xhci_plat_probe(struct platform_device *pdev)
        return 0;
 
 
+put_usb3_hcd:
+       usb_put_hcd(xhci->shared_hcd);
+
 dealloc_usb2_hcd:
        usb_remove_hcd(hcd);
 
 disable_usb_phy:
        usb_phy_shutdown(hcd->usb_phy);
 
-put_usb3_hcd:
-       usb_put_hcd(xhci->shared_hcd);
-
 disable_clk:
        clk_disable_unprepare(xhci->clk);
 
index f970799..46d0b9a 100644 (file)
@@ -740,14 +740,6 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
        }
 }
 
-static void xhci_stop_watchdog_timer_in_irq(struct xhci_hcd *xhci,
-               struct xhci_virt_ep *ep)
-{
-       ep->ep_state &= ~EP_STOP_CMD_PENDING;
-       /* Can't del_timer_sync in interrupt */
-       del_timer(&ep->stop_cmd_timer);
-}
-
 /*
  * Must be called with xhci->lock held in interrupt context,
  * releases and re-acquires xhci->lock
@@ -1122,18 +1114,17 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
                                                          reset_type);
                        if (err)
                                break;
-                       xhci_stop_watchdog_timer_in_irq(xhci, ep);
+                       ep->ep_state &= ~EP_STOP_CMD_PENDING;
                        return;
                case EP_STATE_RUNNING:
                        /* Race, HW handled stop ep cmd before ep was running */
                        xhci_dbg(xhci, "Stop ep completion ctx error, ep is running\n");
 
                        command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
-                       if (!command)
-                               xhci_stop_watchdog_timer_in_irq(xhci, ep);
-
-                       mod_timer(&ep->stop_cmd_timer,
-                                 jiffies + XHCI_STOP_EP_CMD_TIMEOUT * HZ);
+                       if (!command) {
+                               ep->ep_state &= ~EP_STOP_CMD_PENDING;
+                               return;
+                       }
                        xhci_queue_stop_endpoint(xhci, command, slot_id, ep_index, 0);
                        xhci_ring_cmd_db(xhci);
 
@@ -1142,9 +1133,10 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id,
                        break;
                }
        }
+
        /* will queue a set TR deq if stopped on a cancelled, uncleared TD */
        xhci_invalidate_cancelled_tds(ep);
-       xhci_stop_watchdog_timer_in_irq(xhci, ep);
+       ep->ep_state &= ~EP_STOP_CMD_PENDING;
 
        /* Otherwise ring the doorbell(s) to restart queued transfers */
        xhci_giveback_invalidated_tds(ep);
@@ -1248,61 +1240,6 @@ void xhci_hc_died(struct xhci_hcd *xhci)
                usb_hc_died(xhci_to_hcd(xhci));
 }
 
-/* Watchdog timer function for when a stop endpoint command fails to complete.
- * In this case, we assume the host controller is broken or dying or dead.  The
- * host may still be completing some other events, so we have to be careful to
- * let the event ring handler and the URB dequeueing/enqueueing functions know
- * through xhci->state.
- *
- * The timer may also fire if the host takes a very long time to respond to the
- * command, and the stop endpoint command completion handler cannot delete the
- * timer before the timer function is called.  Another endpoint cancellation may
- * sneak in before the timer function can grab the lock, and that may queue
- * another stop endpoint command and add the timer back.  So we cannot use a
- * simple flag to say whether there is a pending stop endpoint command for a
- * particular endpoint.
- *
- * Instead we use a combination of that flag and checking if a new timer is
- * pending.
- */
-void xhci_stop_endpoint_command_watchdog(struct timer_list *t)
-{
-       struct xhci_virt_ep *ep = from_timer(ep, t, stop_cmd_timer);
-       struct xhci_hcd *xhci = ep->xhci;
-       unsigned long flags;
-       u32 usbsts;
-       char str[XHCI_MSG_MAX];
-
-       spin_lock_irqsave(&xhci->lock, flags);
-
-       /* bail out if cmd completed but raced with stop ep watchdog timer.*/
-       if (!(ep->ep_state & EP_STOP_CMD_PENDING) ||
-           timer_pending(&ep->stop_cmd_timer)) {
-               spin_unlock_irqrestore(&xhci->lock, flags);
-               xhci_dbg(xhci, "Stop EP timer raced with cmd completion, exit");
-               return;
-       }
-       usbsts = readl(&xhci->op_regs->status);
-
-       xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n");
-       xhci_warn(xhci, "USBSTS:%s\n", xhci_decode_usbsts(str, usbsts));
-
-       ep->ep_state &= ~EP_STOP_CMD_PENDING;
-
-       xhci_halt(xhci);
-
-       /*
-        * handle a stop endpoint cmd timeout as if host died (-ENODEV).
-        * In the future we could distinguish between -ENODEV and -ETIMEDOUT
-        * and try to recover a -ETIMEDOUT with a host controller reset
-        */
-       xhci_hc_died(xhci);
-
-       spin_unlock_irqrestore(&xhci->lock, flags);
-       xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
-                       "xHCI host controller is dead.");
-}
-
 static void update_ring_for_set_deq_completion(struct xhci_hcd *xhci,
                struct xhci_virt_device *dev,
                struct xhci_ring *ep_ring,
@@ -1489,8 +1426,6 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id,
        /* Cleanup cancelled TDs as ep is stopped. May queue a Set TR Deq cmd */
        xhci_invalidate_cancelled_tds(ep);
 
-       if (xhci->quirks & XHCI_RESET_EP_QUIRK)
-               xhci_dbg(xhci, "Note: Removed workaround to queue config ep for this hw");
        /* Clear our internal halted state */
        ep->ep_state &= ~EP_HALTED;
 
@@ -1534,17 +1469,13 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
        struct xhci_input_control_ctx *ctrl_ctx;
        struct xhci_ep_ctx *ep_ctx;
        unsigned int ep_index;
-       unsigned int ep_state;
-       u32 add_flags, drop_flags;
+       u32 add_flags;
 
        /*
-        * 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.
+        * Configure endpoint commands can come from the USB core configuration
+        * or alt setting changes, or when streams were being configured.
         */
+
        virt_dev = xhci->devs[slot_id];
        if (!virt_dev)
                return;
@@ -1555,34 +1486,13 @@ static void xhci_handle_cmd_config_ep(struct xhci_hcd *xhci, int slot_id,
        }
 
        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;
 
        ep_ctx = xhci_get_ep_ctx(xhci, virt_dev->out_ctx, ep_index);
        trace_xhci_handle_cmd_config_ep(ep_ctx);
 
-       /* 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))
-                       return;
-               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;
-       }
        return;
 }
 
@@ -1650,9 +1560,12 @@ void xhci_cleanup_command_queue(struct xhci_hcd *xhci)
 
 void xhci_handle_command_timeout(struct work_struct *work)
 {
-       struct xhci_hcd *xhci;
-       unsigned long flags;
-       u64 hw_ring_state;
+       struct xhci_hcd *xhci;
+       unsigned long   flags;
+       char            str[XHCI_MSG_MAX];
+       u64             hw_ring_state;
+       u32             cmd_field3;
+       u32             usbsts;
 
        xhci = container_of(to_delayed_work(work), struct xhci_hcd, cmd_timer);
 
@@ -1666,6 +1579,27 @@ void xhci_handle_command_timeout(struct work_struct *work)
                spin_unlock_irqrestore(&xhci->lock, flags);
                return;
        }
+
+       cmd_field3 = le32_to_cpu(xhci->current_cmd->command_trb->generic.field[3]);
+       usbsts = readl(&xhci->op_regs->status);
+       xhci_dbg(xhci, "Command timeout, USBSTS:%s\n", xhci_decode_usbsts(str, usbsts));
+
+       /* Bail out and tear down xhci if a stop endpoint command failed */
+       if (TRB_FIELD_TO_TYPE(cmd_field3) == TRB_STOP_RING) {
+               struct xhci_virt_ep     *ep;
+
+               xhci_warn(xhci, "xHCI host not responding to stop endpoint command\n");
+
+               ep = xhci_get_virt_ep(xhci, TRB_TO_SLOT_ID(cmd_field3),
+                                     TRB_TO_EP_INDEX(cmd_field3));
+               if (ep)
+                       ep->ep_state &= ~EP_STOP_CMD_PENDING;
+
+               xhci_halt(xhci);
+               xhci_hc_died(xhci);
+               goto time_out_completed;
+       }
+
        /* mark this command to be cancelled */
        xhci->current_cmd->status = COMP_COMMAND_ABORTED;
 
index 25b87e9..f0ab631 100644 (file)
@@ -486,6 +486,10 @@ static void compliance_mode_recovery(struct timer_list *t)
 
        xhci = from_timer(xhci, t, comp_mode_recovery_timer);
        rhub = &xhci->usb3_rhub;
+       hcd = rhub->hcd;
+
+       if (!hcd)
+               return;
 
        for (i = 0; i < rhub->num_ports; i++) {
                temp = readl(rhub->ports[i]->addr);
@@ -499,7 +503,6 @@ static void compliance_mode_recovery(struct timer_list *t)
                                        i + 1);
                        xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
                                        "Attempting compliance mode recovery");
-                       hcd = xhci->shared_hcd;
 
                        if (hcd->state == HC_STATE_SUSPENDED)
                                usb_hcd_resume_root_hub(hcd);
@@ -612,14 +615,11 @@ static int xhci_run_finished(struct xhci_hcd *xhci)
                xhci_halt(xhci);
                return -ENODEV;
        }
-       xhci->shared_hcd->state = HC_STATE_RUNNING;
        xhci->cmd_ring_state = CMD_RING_STATE_RUNNING;
 
        if (xhci->quirks & XHCI_NEC_HOST)
                xhci_ring_cmd_db(xhci);
 
-       xhci_dbg_trace(xhci, trace_xhci_dbg_init,
-                       "Finished xhci_run for USB3 roothub");
        return 0;
 }
 
@@ -694,12 +694,17 @@ int xhci_run(struct usb_hcd *hcd)
                        xhci_free_command(xhci, command);
        }
        xhci_dbg_trace(xhci, trace_xhci_dbg_init,
-                       "Finished xhci_run for USB2 roothub");
+                       "Finished %s for main hcd", __func__);
 
        xhci_create_dbc_dev(xhci);
 
        xhci_debugfs_init(xhci);
 
+       if (xhci_has_one_roothub(xhci))
+               return xhci_run_finished(xhci);
+
+       set_bit(HCD_FLAG_DEFER_RH_REGISTER, &hcd->flags);
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(xhci_run);
@@ -992,7 +997,7 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
                return 0;
 
        if (hcd->state != HC_STATE_SUSPENDED ||
-                       xhci->shared_hcd->state != HC_STATE_SUSPENDED)
+           (xhci->shared_hcd && xhci->shared_hcd->state != HC_STATE_SUSPENDED))
                return -EINVAL;
 
        /* Clear root port wake on bits if wakeup not allowed. */
@@ -1009,15 +1014,18 @@ int xhci_suspend(struct xhci_hcd *xhci, bool do_wakeup)
                 __func__, hcd->self.busnum);
        clear_bit(HCD_FLAG_POLL_RH, &hcd->flags);
        del_timer_sync(&hcd->rh_timer);
-       clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
-       del_timer_sync(&xhci->shared_hcd->rh_timer);
+       if (xhci->shared_hcd) {
+               clear_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
+               del_timer_sync(&xhci->shared_hcd->rh_timer);
+       }
 
        if (xhci->quirks & XHCI_SUSPEND_DELAY)
                usleep_range(1000, 1500);
 
        spin_lock_irq(&xhci->lock);
        clear_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
+       if (xhci->shared_hcd)
+               clear_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
        /* step 1: stop endpoint */
        /* skipped assuming that port suspend has done */
 
@@ -1117,7 +1125,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
                msleep(100);
 
        set_bit(HCD_FLAG_HW_ACCESSIBLE, &hcd->flags);
-       set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
+       if (xhci->shared_hcd)
+               set_bit(HCD_FLAG_HW_ACCESSIBLE, &xhci->shared_hcd->flags);
 
        spin_lock_irq(&xhci->lock);
 
@@ -1177,7 +1186,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 
                /* Let the USB core know _both_ roothubs lost power. */
                usb_root_hub_lost_power(xhci->main_hcd->self.root_hub);
-               usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub);
+               if (xhci->shared_hcd)
+                       usb_root_hub_lost_power(xhci->shared_hcd->self.root_hub);
 
                xhci_dbg(xhci, "Stop HCD\n");
                xhci_halt(xhci);
@@ -1217,12 +1227,13 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
 
                xhci_dbg(xhci, "Start the primary HCD\n");
                retval = xhci_run(hcd->primary_hcd);
-               if (!retval) {
+               if (!retval && secondary_hcd) {
                        xhci_dbg(xhci, "Start the secondary HCD\n");
                        retval = xhci_run(secondary_hcd);
                }
                hcd->state = HC_STATE_SUSPENDED;
-               xhci->shared_hcd->state = HC_STATE_SUSPENDED;
+               if (xhci->shared_hcd)
+                       xhci->shared_hcd->state = HC_STATE_SUSPENDED;
                goto done;
        }
 
@@ -1260,7 +1271,8 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
                }
 
                if (pending_portevent) {
-                       usb_hcd_resume_root_hub(xhci->shared_hcd);
+                       if (xhci->shared_hcd)
+                               usb_hcd_resume_root_hub(xhci->shared_hcd);
                        usb_hcd_resume_root_hub(hcd);
                }
        }
@@ -1279,8 +1291,10 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
        /* Re-enable port polling. */
        xhci_dbg(xhci, "%s: starting usb%d port polling.\n",
                 __func__, hcd->self.busnum);
-       set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
-       usb_hcd_poll_rh_status(xhci->shared_hcd);
+       if (xhci->shared_hcd) {
+               set_bit(HCD_FLAG_POLL_RH, &xhci->shared_hcd->flags);
+               usb_hcd_poll_rh_status(xhci->shared_hcd);
+       }
        set_bit(HCD_FLAG_POLL_RH, &hcd->flags);
        usb_hcd_poll_rh_status(hcd);
 
@@ -1860,9 +1874,6 @@ static int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
                        goto done;
                }
                ep->ep_state |= EP_STOP_CMD_PENDING;
-               ep->stop_cmd_timer.expires = jiffies +
-                       XHCI_STOP_EP_CMD_TIMEOUT * HZ;
-               add_timer(&ep->stop_cmd_timer);
                xhci_queue_stop_endpoint(xhci, command, urb->dev->slot_id,
                                         ep_index, 0);
                xhci_ring_cmd_db(xhci);
@@ -3972,10 +3983,8 @@ static void xhci_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
        trace_xhci_free_dev(slot_ctx);
 
        /* Stop any wayward timer functions (which may grab the lock) */
-       for (i = 0; i < 31; i++) {
+       for (i = 0; i < 31; i++)
                virt_dev->eps[i].ep_state &= ~EP_STOP_CMD_PENDING;
-               del_timer_sync(&virt_dev->eps[i].stop_cmd_timer);
-       }
        virt_dev->udev = NULL;
        xhci_disable_slot(xhci, udev->slot_id);
        xhci_free_virt_device(xhci, udev->slot_id);
@@ -4879,9 +4888,6 @@ static int xhci_check_intel_tier_policy(struct usb_device *udev,
        struct usb_device *parent;
        unsigned int num_hubs;
 
-       if (state == USB3_LPM_U2)
-               return 0;
-
        /* Don't enable U1 if the device is on a 2nd tier hub or lower. */
        for (parent = udev->parent, num_hubs = 0; parent->parent;
                        parent = parent->parent)
@@ -4890,7 +4896,7 @@ static int xhci_check_intel_tier_policy(struct usb_device *udev,
        if (num_hubs < 2)
                return 0;
 
-       dev_dbg(&udev->dev, "Disabling U1 link state for device"
+       dev_dbg(&udev->dev, "Disabling U1/U2 link state for device"
                        " below second-tier hub.\n");
        dev_dbg(&udev->dev, "Plug device into first-tier hub "
                        "to decrease power consumption.\n");
@@ -4931,9 +4937,6 @@ static u16 xhci_calculate_lpm_timeout(struct usb_hcd *hcd,
                return timeout;
        }
 
-       if (xhci_check_tier_policy(xhci, udev, state) < 0)
-               return timeout;
-
        /* Gather some information about the currently installed configuration
         * and alternate interface settings.
         */
@@ -5040,6 +5043,9 @@ static int xhci_enable_usb3_lpm_timeout(struct usb_hcd *hcd,
                        !xhci->devs[udev->slot_id])
                return USB3_LPM_DISABLED;
 
+       if (xhci_check_tier_policy(xhci, udev, state) < 0)
+               return USB3_LPM_DISABLED;
+
        hub_encoded_timeout = xhci_calculate_lpm_timeout(hcd, udev, state);
        mel = calculate_max_exit_latency(udev, state, hub_encoded_timeout);
        if (mel < 0) {
@@ -5207,6 +5213,57 @@ static int xhci_get_frame(struct usb_hcd *hcd)
        return readl(&xhci->run_regs->microframe_index) >> 3;
 }
 
+static void xhci_hcd_init_usb2_data(struct xhci_hcd *xhci, struct usb_hcd *hcd)
+{
+       xhci->usb2_rhub.hcd = hcd;
+       hcd->speed = HCD_USB2;
+       hcd->self.root_hub->speed = USB_SPEED_HIGH;
+       /*
+        * USB 2.0 roothub under xHCI has an integrated TT,
+        * (rate matching hub) as opposed to having an OHCI/UHCI
+        * companion controller.
+        */
+       hcd->has_tt = 1;
+}
+
+static void xhci_hcd_init_usb3_data(struct xhci_hcd *xhci, struct usb_hcd *hcd)
+{
+       unsigned int minor_rev;
+
+       /*
+        * Early xHCI 1.1 spec did not mention USB 3.1 capable hosts
+        * should return 0x31 for sbrn, or that the minor revision
+        * is a two digit BCD containig minor and sub-minor numbers.
+        * This was later clarified in xHCI 1.2.
+        *
+        * Some USB 3.1 capable hosts therefore have sbrn 0x30, and
+        * minor revision set to 0x1 instead of 0x10.
+        */
+       if (xhci->usb3_rhub.min_rev == 0x1)
+               minor_rev = 1;
+       else
+               minor_rev = xhci->usb3_rhub.min_rev / 0x10;
+
+       switch (minor_rev) {
+       case 2:
+               hcd->speed = HCD_USB32;
+               hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
+               hcd->self.root_hub->rx_lanes = 2;
+               hcd->self.root_hub->tx_lanes = 2;
+               hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x2;
+               break;
+       case 1:
+               hcd->speed = HCD_USB31;
+               hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
+               hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x1;
+               break;
+       }
+       xhci_info(xhci, "Host supports USB 3.%x %sSuperSpeed\n",
+                 minor_rev, minor_rev ? "Enhanced " : "");
+
+       xhci->usb3_rhub.hcd = hcd;
+}
+
 int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 {
        struct xhci_hcd         *xhci;
@@ -5215,7 +5272,6 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
         * quirks
         */
        struct device           *dev = hcd->self.sysdev;
-       unsigned int            minor_rev;
        int                     retval;
 
        /* Accept arbitrarily long scatter-gather lists */
@@ -5229,61 +5285,13 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
 
        xhci = hcd_to_xhci(hcd);
 
-       if (usb_hcd_is_primary_hcd(hcd)) {
-               xhci->main_hcd = hcd;
-               xhci->usb2_rhub.hcd = hcd;
-               /* Mark the first roothub as being USB 2.0.
-                * The xHCI driver will register the USB 3.0 roothub.
-                */
-               hcd->speed = HCD_USB2;
-               hcd->self.root_hub->speed = USB_SPEED_HIGH;
-               /*
-                * USB 2.0 roothub under xHCI has an integrated TT,
-                * (rate matching hub) as opposed to having an OHCI/UHCI
-                * companion controller.
-                */
-               hcd->has_tt = 1;
-       } else {
-               /*
-                * Early xHCI 1.1 spec did not mention USB 3.1 capable hosts
-                * should return 0x31 for sbrn, or that the minor revision
-                * is a two digit BCD containig minor and sub-minor numbers.
-                * This was later clarified in xHCI 1.2.
-                *
-                * Some USB 3.1 capable hosts therefore have sbrn 0x30, and
-                * minor revision set to 0x1 instead of 0x10.
-                */
-               if (xhci->usb3_rhub.min_rev == 0x1)
-                       minor_rev = 1;
-               else
-                       minor_rev = xhci->usb3_rhub.min_rev / 0x10;
-
-               switch (minor_rev) {
-               case 2:
-                       hcd->speed = HCD_USB32;
-                       hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
-                       hcd->self.root_hub->rx_lanes = 2;
-                       hcd->self.root_hub->tx_lanes = 2;
-                       hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x2;
-                       break;
-               case 1:
-                       hcd->speed = HCD_USB31;
-                       hcd->self.root_hub->speed = USB_SPEED_SUPER_PLUS;
-                       hcd->self.root_hub->ssp_rate = USB_SSP_GEN_2x1;
-                       break;
-               }
-               xhci_info(xhci, "Host supports USB 3.%x %sSuperSpeed\n",
-                         minor_rev,
-                         minor_rev ? "Enhanced " : "");
-
-               xhci->usb3_rhub.hcd = hcd;
-               /* xHCI private pointer was set in xhci_pci_probe for the second
-                * registered roothub.
-                */
+       if (!usb_hcd_is_primary_hcd(hcd)) {
+               xhci_hcd_init_usb3_data(xhci, hcd);
                return 0;
        }
 
        mutex_init(&xhci->mutex);
+       xhci->main_hcd = hcd;
        xhci->cap_regs = hcd->regs;
        xhci->op_regs = hcd->regs +
                HC_LENGTH(readl(&xhci->cap_regs->hc_capbase));
@@ -5358,6 +5366,11 @@ int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks)
                return retval;
        xhci_dbg(xhci, "Called HCD init\n");
 
+       if (xhci_hcd_is_usb3(hcd))
+               xhci_hcd_init_usb3_data(xhci, hcd);
+       else
+               xhci_hcd_init_usb2_data(xhci, hcd);
+
        xhci_info(xhci, "hcc params 0x%08x hci version 0x%x quirks 0x%016llx\n",
                  xhci->hcc_params, xhci->hci_version, xhci->quirks);
 
index 473a33c..0bd76c9 100644 (file)
@@ -948,8 +948,6 @@ struct xhci_virt_ep {
 #define EP_CLEARING_TT         (1 << 8)
        /* ----  Related to URB cancellation ---- */
        struct list_head        cancelled_td_list;
-       /* Watchdog timer for stop endpoint command to cancel URBs */
-       struct timer_list       stop_cmd_timer;
        struct xhci_hcd         *xhci;
        /* Dequeue pointer and dequeue segment for a submitted Set TR Dequeue
         * command.  We'll need to update the ring's dequeue segment and dequeue
@@ -1848,7 +1846,7 @@ struct xhci_hcd {
 #define XHCI_STATE_REMOVING    (1 << 2)
        unsigned long long      quirks;
 #define        XHCI_LINK_TRB_QUIRK     BIT_ULL(0)
-#define XHCI_RESET_EP_QUIRK    BIT_ULL(1)
+#define XHCI_RESET_EP_QUIRK    BIT_ULL(1) /* Deprecated */
 #define XHCI_NEC_HOST          BIT_ULL(2)
 #define XHCI_AMD_PLL_FIX       BIT_ULL(3)
 #define XHCI_SPURIOUS_SUCCESS  BIT_ULL(4)
@@ -1911,6 +1909,8 @@ struct xhci_hcd {
        unsigned                hw_lpm_support:1;
        /* Broken Suspend flag for SNPS Suspend resume issue */
        unsigned                broken_suspend:1;
+       /* Indicates that omitting hcd is supported if root hub has no ports */
+       unsigned                allow_single_roothub:1;
        /* cached usb2 extened protocol capabilites */
        u32                     *ext_caps;
        unsigned int            num_ext_caps;
@@ -1966,6 +1966,30 @@ static inline struct usb_hcd *xhci_to_hcd(struct xhci_hcd *xhci)
        return xhci->main_hcd;
 }
 
+static inline struct usb_hcd *xhci_get_usb3_hcd(struct xhci_hcd *xhci)
+{
+       if (xhci->shared_hcd)
+               return xhci->shared_hcd;
+
+       if (!xhci->usb2_rhub.num_ports)
+               return xhci->main_hcd;
+
+       return NULL;
+}
+
+static inline bool xhci_hcd_is_usb3(struct usb_hcd *hcd)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(hcd);
+
+       return hcd == xhci_get_usb3_hcd(xhci);
+}
+
+static inline bool xhci_has_one_roothub(struct xhci_hcd *xhci)
+{
+       return xhci->allow_single_roothub &&
+              (!xhci->usb2_rhub.num_ports || !xhci->usb3_rhub.num_ports);
+}
+
 #define xhci_dbg(xhci, fmt, args...) \
        dev_dbg(xhci_to_hcd(xhci)->self.controller , fmt , ## args)
 #define xhci_err(xhci, fmt, args...) \
index d1d9a7d..af88f4f 100644 (file)
@@ -251,6 +251,8 @@ static const struct reg_field isp1760_hc_reg_fields[] = {
        [HW_DM_PULLDOWN]        = REG_FIELD(ISP176x_HC_OTG_CTRL, 2, 2),
        [HW_DP_PULLDOWN]        = REG_FIELD(ISP176x_HC_OTG_CTRL, 1, 1),
        [HW_DP_PULLUP]          = REG_FIELD(ISP176x_HC_OTG_CTRL, 0, 0),
+       /* Make sure the array is sized properly during compilation */
+       [HC_FIELD_MAX]          = {},
 };
 
 static const struct reg_field isp1763_hc_reg_fields[] = {
@@ -321,6 +323,8 @@ static const struct reg_field isp1763_hc_reg_fields[] = {
        [HW_DM_PULLDOWN_CLEAR]  = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 2, 2),
        [HW_DP_PULLDOWN_CLEAR]  = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 1, 1),
        [HW_DP_PULLUP_CLEAR]    = REG_FIELD(ISP1763_HC_OTG_CTRL_CLEAR, 0, 0),
+       /* Make sure the array is sized properly during compilation */
+       [HC_FIELD_MAX]          = {},
 };
 
 static const struct regmap_range isp1763_hc_volatile_ranges[] = {
@@ -405,6 +409,8 @@ static const struct reg_field isp1761_dc_reg_fields[] = {
        [DC_CHIP_ID_HIGH]       = REG_FIELD(ISP176x_DC_CHIPID, 16, 31),
        [DC_CHIP_ID_LOW]        = REG_FIELD(ISP176x_DC_CHIPID, 0, 15),
        [DC_SCRATCH]            = REG_FIELD(ISP176x_DC_SCRATCH, 0, 15),
+       /* Make sure the array is sized properly during compilation */
+       [DC_FIELD_MAX]          = {},
 };
 
 static const struct regmap_range isp1763_dc_volatile_ranges[] = {
@@ -458,6 +464,8 @@ static const struct reg_field isp1763_dc_reg_fields[] = {
        [DC_CHIP_ID_HIGH]       = REG_FIELD(ISP1763_DC_CHIPID_HIGH, 0, 15),
        [DC_CHIP_ID_LOW]        = REG_FIELD(ISP1763_DC_CHIPID_LOW, 0, 15),
        [DC_SCRATCH]            = REG_FIELD(ISP1763_DC_SCRATCH, 0, 15),
+       /* Make sure the array is sized properly during compilation */
+       [DC_FIELD_MAX]          = {},
 };
 
 static const struct regmap_config isp1763_dc_regmap_conf = {
index 893becb..76862ba 100644 (file)
@@ -825,8 +825,7 @@ static void create_ptd_atl(struct isp1760_qh *qh,
        memset(ptd, 0, sizeof(*ptd));
 
        /* according to 3.6.2, max packet len can not be > 0x400 */
-       maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe,
-                                               usb_pipeout(qtd->urb->pipe));
+       maxpacket = usb_maxpacket(qtd->urb->dev, qtd->urb->pipe);
        multi =  1 + ((maxpacket >> 11) & 0x3);
        maxpacket &= 0x7ff;
 
@@ -1808,8 +1807,7 @@ static void packetize_urb(struct usb_hcd *hcd,
                        packet_type = IN_PID;
        }
 
-       maxpacketsize = usb_maxpacket(urb->dev, urb->pipe,
-                                     usb_pipeout(urb->pipe));
+       maxpacketsize = usb_maxpacket(urb->dev, urb->pipe);
 
        /*
         * buffer gets wrapped in one or more qtds;
index 6c38c62..b2f9804 100644 (file)
@@ -1449,8 +1449,7 @@ wait:if (ftdi->disconnected > 0) {
                        command->length = 0x8007;
                        command->address = (toggle_bits << 6) | (ep_number << 2)
                                | (address << 0);
-                       command->width = usb_maxpacket(urb->dev, urb->pipe,
-                                                      usb_pipeout(urb->pipe));
+                       command->width = usb_maxpacket(urb->dev, urb->pipe);
                        command->follows = 8;
                        command->value = 0;
                        command->buffer = urb->setup_packet;
@@ -1514,8 +1513,7 @@ wait:if (ftdi->disconnected > 0) {
                                                            1);
                        command->address = (toggle_bits << 6) | (ep_number << 2)
                                | (address << 0);
-                       command->width = usb_maxpacket(urb->dev, urb->pipe,
-                                                      usb_pipeout(urb->pipe));
+                       command->width = usb_maxpacket(urb->dev, urb->pipe);
                        command->follows = 0;
                        command->value = 0;
                        command->buffer = NULL;
@@ -1571,8 +1569,7 @@ wait:if (ftdi->disconnected > 0) {
                        command->length = 0x0000;
                        command->address = (toggle_bits << 6) | (ep_number << 2)
                                | (address << 0);
-                       command->width = usb_maxpacket(urb->dev, urb->pipe,
-                                                      usb_pipeout(urb->pipe));
+                       command->width = usb_maxpacket(urb->dev, urb->pipe);
                        command->follows = 0;
                        command->value = 0;
                        command->buffer = NULL;
@@ -1634,8 +1631,7 @@ wait:if (ftdi->disconnected > 0) {
                        command->header = 0x81 | (ed << 5);
                        command->address = (toggle_bits << 6) | (ep_number << 2)
                                | (address << 0);
-                       command->width = usb_maxpacket(urb->dev, urb->pipe,
-                                                      usb_pipeout(urb->pipe));
+                       command->width = usb_maxpacket(urb->dev, urb->pipe);
                        command->follows = min_t(u32, 1024,
                                                 urb->transfer_buffer_length -
                                                 urb->actual_length);
@@ -1715,8 +1711,7 @@ wait:if (ftdi->disconnected > 0) {
                                                            1);
                        command->address = (toggle_bits << 6) | (ep_number << 2)
                                | (address << 0);
-                       command->width = usb_maxpacket(urb->dev, urb->pipe,
-                                                      usb_pipeout(urb->pipe));
+                       command->width = usb_maxpacket(urb->dev, urb->pipe);
                        command->follows = 0;
                        command->value = 0;
                        command->buffer = NULL;
index f868613..25ec566 100644 (file)
@@ -437,7 +437,7 @@ static int lvs_rh_probe(struct usb_interface *intf,
        INIT_WORK(&lvs->rh_work, lvs_rh_work);
 
        pipe = usb_rcvintpipe(hdev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(hdev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(hdev, pipe);
        usb_fill_int_urb(lvs->urb, hdev, pipe, &lvs->buffer[0], maxp,
                        lvs_rh_irq, lvs, endpoint->bInterval);
 
index 1aeb34d..cad9913 100644 (file)
@@ -36,6 +36,8 @@
 #define DMA_INTR_STATUS_MSK    GENMASK(7, 0)
 #define DMA_INTR_UNMASK_SET_MSK        GENMASK(31, 24)
 
+#define MTK_MUSB_CLKS_NUM      3
+
 struct mtk_glue {
        struct device *dev;
        struct musb *musb;
@@ -44,9 +46,7 @@ struct mtk_glue {
        struct phy *phy;
        struct usb_phy *xceiv;
        enum phy_mode phy_mode;
-       struct clk *main;
-       struct clk *mcu;
-       struct clk *univpll;
+       struct clk_bulk_data clks[MTK_MUSB_CLKS_NUM];
        enum usb_role role;
        struct usb_role_switch *role_sw;
 };
@@ -55,64 +55,11 @@ static int mtk_musb_clks_get(struct mtk_glue *glue)
 {
        struct device *dev = glue->dev;
 
-       glue->main = devm_clk_get(dev, "main");
-       if (IS_ERR(glue->main)) {
-               dev_err(dev, "fail to get main clock\n");
-               return PTR_ERR(glue->main);
-       }
-
-       glue->mcu = devm_clk_get(dev, "mcu");
-       if (IS_ERR(glue->mcu)) {
-               dev_err(dev, "fail to get mcu clock\n");
-               return PTR_ERR(glue->mcu);
-       }
-
-       glue->univpll = devm_clk_get(dev, "univpll");
-       if (IS_ERR(glue->univpll)) {
-               dev_err(dev, "fail to get univpll clock\n");
-               return PTR_ERR(glue->univpll);
-       }
-
-       return 0;
-}
+       glue->clks[0].id = "main";
+       glue->clks[1].id = "mcu";
+       glue->clks[2].id = "univpll";
 
-static int mtk_musb_clks_enable(struct mtk_glue *glue)
-{
-       int ret;
-
-       ret = clk_prepare_enable(glue->main);
-       if (ret) {
-               dev_err(glue->dev, "failed to enable main clock\n");
-               goto err_main_clk;
-       }
-
-       ret = clk_prepare_enable(glue->mcu);
-       if (ret) {
-               dev_err(glue->dev, "failed to enable mcu clock\n");
-               goto err_mcu_clk;
-       }
-
-       ret = clk_prepare_enable(glue->univpll);
-       if (ret) {
-               dev_err(glue->dev, "failed to enable univpll clock\n");
-               goto err_univpll_clk;
-       }
-
-       return 0;
-
-err_univpll_clk:
-       clk_disable_unprepare(glue->mcu);
-err_mcu_clk:
-       clk_disable_unprepare(glue->main);
-err_main_clk:
-       return ret;
-}
-
-static void mtk_musb_clks_disable(struct mtk_glue *glue)
-{
-       clk_disable_unprepare(glue->univpll);
-       clk_disable_unprepare(glue->mcu);
-       clk_disable_unprepare(glue->main);
+       return devm_clk_bulk_get(dev, MTK_MUSB_CLKS_NUM, glue->clks);
 }
 
 static int mtk_otg_switch_set(struct mtk_glue *glue, enum usb_role role)
@@ -390,7 +337,7 @@ static int mtk_musb_exit(struct musb *musb)
        mtk_otg_switch_exit(glue);
        phy_power_off(glue->phy);
        phy_exit(glue->phy);
-       mtk_musb_clks_disable(glue);
+       clk_bulk_disable_unprepare(MTK_MUSB_CLKS_NUM, glue->clks);
 
        pm_runtime_put_sync(dev);
        pm_runtime_disable(dev);
@@ -528,7 +475,7 @@ static int mtk_musb_probe(struct platform_device *pdev)
        pm_runtime_enable(dev);
        pm_runtime_get_sync(dev);
 
-       ret = mtk_musb_clks_enable(glue);
+       ret = clk_bulk_prepare_enable(MTK_MUSB_CLKS_NUM, glue->clks);
        if (ret)
                goto err_enable_clk;
 
@@ -551,7 +498,7 @@ static int mtk_musb_probe(struct platform_device *pdev)
        return 0;
 
 err_device_register:
-       mtk_musb_clks_disable(glue);
+       clk_bulk_disable_unprepare(MTK_MUSB_CLKS_NUM, glue->clks);
 err_enable_clk:
        pm_runtime_put_sync(dev);
        pm_runtime_disable(dev);
index d2b7e61..f571a65 100644 (file)
@@ -362,6 +362,7 @@ static int omap2430_probe(struct platform_device *pdev)
        control_node = of_parse_phandle(np, "ctrl-module", 0);
        if (control_node) {
                control_pdev = of_find_device_by_node(control_node);
+               of_node_put(control_node);
                if (!control_pdev) {
                        dev_err(&pdev->dev, "Failed to get control device\n");
                        ret = -EINVAL;
index 20b857e..747be69 100644 (file)
@@ -1104,7 +1104,7 @@ static int init_alauda(struct us_data *us)
 
        us->extra = kzalloc(sizeof(struct alauda_info), GFP_NOIO);
        if (!us->extra)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -ENOMEM;
 
        info = (struct alauda_info *) us->extra;
        us->extra_destructor = alauda_info_destructor;
@@ -1113,7 +1113,7 @@ static int init_alauda(struct us_data *us)
                altsetting->endpoint[0].desc.bEndpointAddress
                & USB_ENDPOINT_NUMBER_MASK);
 
-       return USB_STOR_TRANSPORT_GOOD;
+       return 0;
 }
 
 static int alauda_transport(struct scsi_cmnd *srb, struct us_data *us)
index 05429f1..4e0eef1 100644 (file)
@@ -1449,7 +1449,7 @@ static void isd200_free_info_ptrs(void *info_)
  * Allocates (if necessary) and initializes the driver structure.
  *
  * RETURNS:
- *    ISD status code
+ *    error status code
  */
 static int isd200_init_info(struct us_data *us)
 {
@@ -1457,7 +1457,7 @@ static int isd200_init_info(struct us_data *us)
 
        info = kzalloc(sizeof(struct isd200_info), GFP_KERNEL);
        if (!info)
-               return ISD200_ERROR;
+               return -ENOMEM;
 
        info->id = kzalloc(ATA_ID_WORDS * 2, GFP_KERNEL);
        info->RegsBuf = kmalloc(sizeof(info->ATARegs), GFP_KERNEL);
@@ -1466,13 +1466,13 @@ static int isd200_init_info(struct us_data *us)
        if (!info->id || !info->RegsBuf || !info->srb.sense_buffer) {
                isd200_free_info_ptrs(info);
                kfree(info);
-               return ISD200_ERROR;
+               return -ENOMEM;
        }
 
        us->extra = info;
        us->extra_destructor = isd200_free_info_ptrs;
 
-       return ISD200_GOOD;
+       return 0;
 }
 
 /**************************************************************************
index 05cec81..38ddfed 100644 (file)
@@ -174,24 +174,25 @@ static void rio_karma_destructor(void *extra)
 
 static int rio_karma_init(struct us_data *us)
 {
-       int ret = 0;
        struct karma_data *data = kzalloc(sizeof(struct karma_data), GFP_NOIO);
 
        if (!data)
-               goto out;
+               return -ENOMEM;
 
        data->recv = kmalloc(RIO_RECV_LEN, GFP_NOIO);
        if (!data->recv) {
                kfree(data);
-               goto out;
+               return -ENOMEM;
        }
 
        us->extra = data;
        us->extra_destructor = rio_karma_destructor;
-       ret = rio_karma_send_command(RIO_ENTER_STORAGE, us);
-       data->in_storage = (ret == 0);
-out:
-       return ret;
+       if (rio_karma_send_command(RIO_ENTER_STORAGE, us))
+               return -EIO;
+
+       data->in_storage = 1;
+
+       return 0;
 }
 
 static struct scsi_host_template karma_host_template;
index a989fe9..1db2eef 100644 (file)
@@ -180,7 +180,7 @@ static int onetouch_connect_input(struct us_data *ss)
                return -ENODEV;
 
        pipe = usb_rcvintpipe(udev, endpoint->bEndpointAddress);
-       maxp = usb_maxpacket(udev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(udev, pipe);
        maxp = min(maxp, ONETOUCH_PKT_LEN);
 
        onetouch = kzalloc(sizeof(struct usb_onetouch), GFP_KERNEL);
index 54aa139..f0d0ca3 100644 (file)
@@ -1456,7 +1456,7 @@ static int init_usbat(struct us_data *us, int devicetype)
 
        us->extra = kzalloc(sizeof(struct usbat_info), GFP_NOIO);
        if (!us->extra)
-               return 1;
+               return -ENOMEM;
 
        info = (struct usbat_info *) (us->extra);
 
@@ -1465,7 +1465,7 @@ static int init_usbat(struct us_data *us, int devicetype)
                                 USBAT_UIO_OE1 | USBAT_UIO_OE0,
                                 USBAT_UIO_EPAD | USBAT_UIO_1);
        if (rc != USB_STOR_XFER_GOOD)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 1\n");
 
@@ -1473,42 +1473,42 @@ static int init_usbat(struct us_data *us, int devicetype)
 
        rc = usbat_read_user_io(us, status);
        if (rc != USB_STOR_TRANSPORT_GOOD)
-               return rc;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 2\n");
 
        rc = usbat_read_user_io(us, status);
        if (rc != USB_STOR_XFER_GOOD)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        rc = usbat_read_user_io(us, status);
        if (rc != USB_STOR_XFER_GOOD)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 3\n");
 
        rc = usbat_select_and_test_registers(us);
        if (rc != USB_STOR_TRANSPORT_GOOD)
-               return rc;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 4\n");
 
        rc = usbat_read_user_io(us, status);
        if (rc != USB_STOR_XFER_GOOD)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 5\n");
 
        /* Enable peripheral control signals and card detect */
        rc = usbat_device_enable_cdt(us);
        if (rc != USB_STOR_TRANSPORT_GOOD)
-               return rc;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 6\n");
 
        rc = usbat_read_user_io(us, status);
        if (rc != USB_STOR_XFER_GOOD)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 7\n");
 
@@ -1516,19 +1516,19 @@ static int init_usbat(struct us_data *us, int devicetype)
 
        rc = usbat_read_user_io(us, status);
        if (rc != USB_STOR_XFER_GOOD)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 8\n");
 
        rc = usbat_select_and_test_registers(us);
        if (rc != USB_STOR_TRANSPORT_GOOD)
-               return rc;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 9\n");
 
        /* At this point, we need to detect which device we are using */
        if (usbat_set_transport(us, info, devicetype))
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 10\n");
 
@@ -1539,11 +1539,11 @@ static int init_usbat(struct us_data *us, int devicetype)
        rc = usbat_set_shuttle_features(us, (USBAT_FEAT_ETEN | USBAT_FEAT_ET2 | USBAT_FEAT_ET1),
                                                                        0x00, 0x88, 0x08, subcountH, subcountL);
        if (rc != USB_STOR_XFER_GOOD)
-               return USB_STOR_TRANSPORT_ERROR;
+               return -EIO;
 
        usb_stor_dbg(us, "INIT 11\n");
 
-       return USB_STOR_TRANSPORT_GOOD;
+       return 0;
 }
 
 /*
index 1928b39..64d96d2 100644 (file)
@@ -363,7 +363,7 @@ static int usb_stor_intr_transfer(struct us_data *us, void *buf,
        usb_stor_dbg(us, "xfer %u bytes\n", length);
 
        /* calculate the max packet size */
-       maxp = usb_maxpacket(us->pusb_dev, pipe, usb_pipeout(pipe));
+       maxp = usb_maxpacket(us->pusb_dev, pipe);
        if (maxp > length)
                maxp = length;
 
index 78e0e78..26ea2fd 100644 (file)
@@ -24,7 +24,7 @@ typec_altmode_set_mux(struct altmode *alt, unsigned long conf, void *data)
        state.mode = conf;
        state.data = data;
 
-       return alt->mux->set(alt->mux, &state);
+       return typec_mux_set(alt->mux, &state);
 }
 
 static int typec_altmode_set_state(struct typec_altmode *adev,
index c8340de..fd55c2c 100644 (file)
 #include "class.h"
 #include "mux.h"
 
+#define TYPEC_MUX_MAX_DEVS     3
+
+struct typec_switch {
+       struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
+       unsigned int num_sw_devs;
+};
+
 static int switch_fwnode_match(struct device *dev, const void *fwnode)
 {
-       if (!is_typec_switch(dev))
+       if (!is_typec_switch_dev(dev))
                return 0;
 
        return dev_fwnode(dev) == fwnode;
@@ -49,7 +56,7 @@ static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id,
        dev = class_find_device(&typec_mux_class, NULL, fwnode,
                                switch_fwnode_match);
 
-       return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
+       return dev ? to_typec_switch_dev(dev) : ERR_PTR(-EPROBE_DEFER);
 }
 
 /**
@@ -63,14 +70,50 @@ static void *typec_switch_match(struct fwnode_handle *fwnode, const char *id,
  */
 struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
 {
+       struct typec_switch_dev *sw_devs[TYPEC_MUX_MAX_DEVS];
        struct typec_switch *sw;
+       int count;
+       int err;
+       int i;
+
+       sw = kzalloc(sizeof(*sw), GFP_KERNEL);
+       if (!sw)
+               return ERR_PTR(-ENOMEM);
+
+       count = fwnode_connection_find_matches(fwnode, "orientation-switch", NULL,
+                                              typec_switch_match,
+                                              (void **)sw_devs,
+                                              ARRAY_SIZE(sw_devs));
+       if (count <= 0) {
+               kfree(sw);
+               return NULL;
+       }
 
-       sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL,
-                                         typec_switch_match);
-       if (!IS_ERR_OR_NULL(sw))
-               WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
+       for (i = 0; i < count; i++) {
+               if (IS_ERR(sw_devs[i])) {
+                       err = PTR_ERR(sw_devs[i]);
+                       goto put_sw_devs;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               WARN_ON(!try_module_get(sw_devs[i]->dev.parent->driver->owner));
+               sw->sw_devs[i] = sw_devs[i];
+       }
+
+       sw->num_sw_devs = count;
 
        return sw;
+
+put_sw_devs:
+       for (i = 0; i < count; i++) {
+               if (!IS_ERR(sw_devs[i]))
+                       put_device(&sw_devs[i]->dev);
+       }
+
+       kfree(sw);
+
+       return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
 
@@ -82,16 +125,25 @@ EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
  */
 void typec_switch_put(struct typec_switch *sw)
 {
-       if (!IS_ERR_OR_NULL(sw)) {
-               module_put(sw->dev.parent->driver->owner);
-               put_device(&sw->dev);
+       struct typec_switch_dev *sw_dev;
+       unsigned int i;
+
+       if (IS_ERR_OR_NULL(sw))
+               return;
+
+       for (i = 0; i < sw->num_sw_devs; i++) {
+               sw_dev = sw->sw_devs[i];
+
+               module_put(sw_dev->dev.parent->driver->owner);
+               put_device(&sw_dev->dev);
        }
+       kfree(sw);
 }
 EXPORT_SYMBOL_GPL(typec_switch_put);
 
 static void typec_switch_release(struct device *dev)
 {
-       kfree(to_typec_switch(dev));
+       kfree(to_typec_switch_dev(dev));
 }
 
 const struct device_type typec_switch_dev_type = {
@@ -109,82 +161,102 @@ const struct device_type typec_switch_dev_type = {
  * connector to the USB controllers. USB Type-C plugs can be inserted
  * right-side-up or upside-down.
  */
-struct typec_switch *
+struct typec_switch_dev *
 typec_switch_register(struct device *parent,
                      const struct typec_switch_desc *desc)
 {
-       struct typec_switch *sw;
+       struct typec_switch_dev *sw_dev;
        int ret;
 
        if (!desc || !desc->set)
                return ERR_PTR(-EINVAL);
 
-       sw = kzalloc(sizeof(*sw), GFP_KERNEL);
-       if (!sw)
+       sw_dev = kzalloc(sizeof(*sw_dev), GFP_KERNEL);
+       if (!sw_dev)
                return ERR_PTR(-ENOMEM);
 
-       sw->set = desc->set;
+       sw_dev->set = desc->set;
 
-       device_initialize(&sw->dev);
-       sw->dev.parent = parent;
-       sw->dev.fwnode = desc->fwnode;
-       sw->dev.class = &typec_mux_class;
-       sw->dev.type = &typec_switch_dev_type;
-       sw->dev.driver_data = desc->drvdata;
-       dev_set_name(&sw->dev, "%s-switch",
-                    desc->name ? desc->name : dev_name(parent));
+       device_initialize(&sw_dev->dev);
+       sw_dev->dev.parent = parent;
+       sw_dev->dev.fwnode = desc->fwnode;
+       sw_dev->dev.class = &typec_mux_class;
+       sw_dev->dev.type = &typec_switch_dev_type;
+       sw_dev->dev.driver_data = desc->drvdata;
+       ret = dev_set_name(&sw_dev->dev, "%s-switch", desc->name ? desc->name : dev_name(parent));
+       if (ret) {
+               put_device(&sw_dev->dev);
+               return ERR_PTR(ret);
+       }
 
-       ret = device_add(&sw->dev);
+       ret = device_add(&sw_dev->dev);
        if (ret) {
                dev_err(parent, "failed to register switch (%d)\n", ret);
-               put_device(&sw->dev);
+               put_device(&sw_dev->dev);
                return ERR_PTR(ret);
        }
 
-       return sw;
+       return sw_dev;
 }
 EXPORT_SYMBOL_GPL(typec_switch_register);
 
 int typec_switch_set(struct typec_switch *sw,
                     enum typec_orientation orientation)
 {
+       struct typec_switch_dev *sw_dev;
+       unsigned int i;
+       int ret;
+
        if (IS_ERR_OR_NULL(sw))
                return 0;
 
-       return sw->set(sw, orientation);
+       for (i = 0; i < sw->num_sw_devs; i++) {
+               sw_dev = sw->sw_devs[i];
+
+               ret = sw_dev->set(sw_dev, orientation);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(typec_switch_set);
 
 /**
  * typec_switch_unregister - Unregister USB Type-C orientation switch
- * @sw: USB Type-C orientation switch
+ * @sw_dev: USB Type-C orientation switch
  *
  * Unregister switch that was registered with typec_switch_register().
  */
-void typec_switch_unregister(struct typec_switch *sw)
+void typec_switch_unregister(struct typec_switch_dev *sw_dev)
 {
-       if (!IS_ERR_OR_NULL(sw))
-               device_unregister(&sw->dev);
+       if (!IS_ERR_OR_NULL(sw_dev))
+               device_unregister(&sw_dev->dev);
 }
 EXPORT_SYMBOL_GPL(typec_switch_unregister);
 
-void typec_switch_set_drvdata(struct typec_switch *sw, void *data)
+void typec_switch_set_drvdata(struct typec_switch_dev *sw_dev, void *data)
 {
-       dev_set_drvdata(&sw->dev, data);
+       dev_set_drvdata(&sw_dev->dev, data);
 }
 EXPORT_SYMBOL_GPL(typec_switch_set_drvdata);
 
-void *typec_switch_get_drvdata(struct typec_switch *sw)
+void *typec_switch_get_drvdata(struct typec_switch_dev *sw_dev)
 {
-       return dev_get_drvdata(&sw->dev);
+       return dev_get_drvdata(&sw_dev->dev);
 }
 EXPORT_SYMBOL_GPL(typec_switch_get_drvdata);
 
 /* ------------------------------------------------------------------------- */
 
+struct typec_mux {
+       struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
+       unsigned int num_mux_devs;
+};
+
 static int mux_fwnode_match(struct device *dev, const void *fwnode)
 {
-       if (!is_typec_mux(dev))
+       if (!is_typec_mux_dev(dev))
                return 0;
 
        return dev_fwnode(dev) == fwnode;
@@ -246,7 +318,7 @@ find_mux:
        dev = class_find_device(&typec_mux_class, NULL, fwnode,
                                mux_fwnode_match);
 
-       return dev ? to_typec_mux(dev) : ERR_PTR(-EPROBE_DEFER);
+       return dev ? to_typec_mux_dev(dev) : ERR_PTR(-EPROBE_DEFER);
 }
 
 /**
@@ -262,14 +334,50 @@ find_mux:
 struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
                                       const struct typec_altmode_desc *desc)
 {
+       struct typec_mux_dev *mux_devs[TYPEC_MUX_MAX_DEVS];
        struct typec_mux *mux;
+       int count;
+       int err;
+       int i;
+
+       mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+       if (!mux)
+               return ERR_PTR(-ENOMEM);
+
+       count = fwnode_connection_find_matches(fwnode, "mode-switch",
+                                              (void *)desc, typec_mux_match,
+                                              (void **)mux_devs,
+                                              ARRAY_SIZE(mux_devs));
+       if (count <= 0) {
+               kfree(mux);
+               return NULL;
+       }
 
-       mux = fwnode_connection_find_match(fwnode, "mode-switch", (void *)desc,
-                                          typec_mux_match);
-       if (!IS_ERR_OR_NULL(mux))
-               WARN_ON(!try_module_get(mux->dev.parent->driver->owner));
+       for (i = 0; i < count; i++) {
+               if (IS_ERR(mux_devs[i])) {
+                       err = PTR_ERR(mux_devs[i]);
+                       goto put_mux_devs;
+               }
+       }
+
+       for (i = 0; i < count; i++) {
+               WARN_ON(!try_module_get(mux_devs[i]->dev.parent->driver->owner));
+               mux->mux_devs[i] = mux_devs[i];
+       }
+
+       mux->num_mux_devs = count;
 
        return mux;
+
+put_mux_devs:
+       for (i = 0; i < count; i++) {
+               if (!IS_ERR(mux_devs[i]))
+                       put_device(&mux_devs[i]->dev);
+       }
+
+       kfree(mux);
+
+       return ERR_PTR(err);
 }
 EXPORT_SYMBOL_GPL(fwnode_typec_mux_get);
 
@@ -281,25 +389,45 @@ EXPORT_SYMBOL_GPL(fwnode_typec_mux_get);
  */
 void typec_mux_put(struct typec_mux *mux)
 {
-       if (!IS_ERR_OR_NULL(mux)) {
-               module_put(mux->dev.parent->driver->owner);
-               put_device(&mux->dev);
+       struct typec_mux_dev *mux_dev;
+       unsigned int i;
+
+       if (IS_ERR_OR_NULL(mux))
+               return;
+
+       for (i = 0; i < mux->num_mux_devs; i++) {
+               mux_dev = mux->mux_devs[i];
+               module_put(mux_dev->dev.parent->driver->owner);
+               put_device(&mux_dev->dev);
        }
+       kfree(mux);
 }
 EXPORT_SYMBOL_GPL(typec_mux_put);
 
 int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
 {
+       struct typec_mux_dev *mux_dev;
+       unsigned int i;
+       int ret;
+
        if (IS_ERR_OR_NULL(mux))
                return 0;
 
-       return mux->set(mux, state);
+       for (i = 0; i < mux->num_mux_devs; i++) {
+               mux_dev = mux->mux_devs[i];
+
+               ret = mux_dev->set(mux_dev, state);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(typec_mux_set);
 
 static void typec_mux_release(struct device *dev)
 {
-       kfree(to_typec_mux(dev));
+       kfree(to_typec_mux_dev(dev));
 }
 
 const struct device_type typec_mux_dev_type = {
@@ -317,63 +445,66 @@ const struct device_type typec_mux_dev_type = {
  * the pins on the connector need to be reconfigured. This function registers
  * multiplexer switches routing the pins on the connector.
  */
-struct typec_mux *
+struct typec_mux_dev *
 typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
 {
-       struct typec_mux *mux;
+       struct typec_mux_dev *mux_dev;
        int ret;
 
        if (!desc || !desc->set)
                return ERR_PTR(-EINVAL);
 
-       mux = kzalloc(sizeof(*mux), GFP_KERNEL);
-       if (!mux)
+       mux_dev = kzalloc(sizeof(*mux_dev), GFP_KERNEL);
+       if (!mux_dev)
                return ERR_PTR(-ENOMEM);
 
-       mux->set = desc->set;
+       mux_dev->set = desc->set;
 
-       device_initialize(&mux->dev);
-       mux->dev.parent = parent;
-       mux->dev.fwnode = desc->fwnode;
-       mux->dev.class = &typec_mux_class;
-       mux->dev.type = &typec_mux_dev_type;
-       mux->dev.driver_data = desc->drvdata;
-       dev_set_name(&mux->dev, "%s-mux",
-                    desc->name ? desc->name : dev_name(parent));
+       device_initialize(&mux_dev->dev);
+       mux_dev->dev.parent = parent;
+       mux_dev->dev.fwnode = desc->fwnode;
+       mux_dev->dev.class = &typec_mux_class;
+       mux_dev->dev.type = &typec_mux_dev_type;
+       mux_dev->dev.driver_data = desc->drvdata;
+       ret = dev_set_name(&mux_dev->dev, "%s-mux", desc->name ? desc->name : dev_name(parent));
+       if (ret) {
+               put_device(&mux_dev->dev);
+               return ERR_PTR(ret);
+       }
 
-       ret = device_add(&mux->dev);
+       ret = device_add(&mux_dev->dev);
        if (ret) {
                dev_err(parent, "failed to register mux (%d)\n", ret);
-               put_device(&mux->dev);
+               put_device(&mux_dev->dev);
                return ERR_PTR(ret);
        }
 
-       return mux;
+       return mux_dev;
 }
 EXPORT_SYMBOL_GPL(typec_mux_register);
 
 /**
  * typec_mux_unregister - Unregister Multiplexer Switch
- * @mux: USB Type-C Connector Multiplexer/DeMultiplexer
+ * @mux_dev: USB Type-C Connector Multiplexer/DeMultiplexer
  *
  * Unregister mux that was registered with typec_mux_register().
  */
-void typec_mux_unregister(struct typec_mux *mux)
+void typec_mux_unregister(struct typec_mux_dev *mux_dev)
 {
-       if (!IS_ERR_OR_NULL(mux))
-               device_unregister(&mux->dev);
+       if (!IS_ERR_OR_NULL(mux_dev))
+               device_unregister(&mux_dev->dev);
 }
 EXPORT_SYMBOL_GPL(typec_mux_unregister);
 
-void typec_mux_set_drvdata(struct typec_mux *mux, void *data)
+void typec_mux_set_drvdata(struct typec_mux_dev *mux_dev, void *data)
 {
-       dev_set_drvdata(&mux->dev, data);
+       dev_set_drvdata(&mux_dev->dev, data);
 }
 EXPORT_SYMBOL_GPL(typec_mux_set_drvdata);
 
-void *typec_mux_get_drvdata(struct typec_mux *mux)
+void *typec_mux_get_drvdata(struct typec_mux_dev *mux_dev)
 {
-       return dev_get_drvdata(&mux->dev);
+       return dev_get_drvdata(&mux_dev->dev);
 }
 EXPORT_SYMBOL_GPL(typec_mux_get_drvdata);
 
index b1d6e83..58f0f28 100644 (file)
@@ -5,23 +5,23 @@
 
 #include <linux/usb/typec_mux.h>
 
-struct typec_switch {
+struct typec_switch_dev {
        struct device dev;
        typec_switch_set_fn_t set;
 };
 
-struct typec_mux {
+struct typec_mux_dev {
        struct device dev;
        typec_mux_set_fn_t set;
 };
 
-#define to_typec_switch(_dev_) container_of(_dev_, struct typec_switch, dev)
-#define to_typec_mux(_dev_) container_of(_dev_, struct typec_mux, dev)
+#define to_typec_switch_dev(_dev_) container_of(_dev_, struct typec_switch_dev, dev)
+#define to_typec_mux_dev(_dev_) container_of(_dev_, struct typec_mux_dev, dev)
 
 extern const struct device_type typec_switch_dev_type;
 extern const struct device_type typec_mux_dev_type;
 
-#define is_typec_switch(dev) ((dev)->type == &typec_switch_dev_type)
-#define is_typec_mux(dev) ((dev)->type == &typec_mux_dev_type)
+#define is_typec_switch_dev(dev) ((dev)->type == &typec_switch_dev_type)
+#define is_typec_mux_dev(dev) ((dev)->type == &typec_mux_dev_type)
 
 #endif /* __USB_TYPEC_MUX__ */
index edead55..5eb2c17 100644 (file)
@@ -2,6 +2,16 @@
 
 menu "USB Type-C Multiplexer/DeMultiplexer Switch support"
 
+config TYPEC_MUX_FSA4480
+       tristate "ON Semi FSA4480 Analog Audio Switch driver"
+       depends on I2C
+       select REGMAP_I2C
+       help
+         Driver for the ON Semiconductor FSA4480 Analog Audio Switch, which
+         provides support for muxing analog audio and sideband signals on a
+         common USB Type-C connector.
+         If compiled as a module, the module will be named fsa4480.
+
 config TYPEC_MUX_PI3USB30532
        tristate "Pericom PI3USB30532 Type-C cross switch driver"
        depends on I2C
index 280a6f5..e52a56c 100644 (file)
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0
 
+obj-$(CONFIG_TYPEC_MUX_FSA4480)                += fsa4480.o
 obj-$(CONFIG_TYPEC_MUX_PI3USB30532)    += pi3usb30532.o
 obj-$(CONFIG_TYPEC_MUX_INTEL_PMC)      += intel_pmc_mux.o
diff --git a/drivers/usb/typec/mux/fsa4480.c b/drivers/usb/typec/mux/fsa4480.c
new file mode 100644 (file)
index 0000000..6184f53
--- /dev/null
@@ -0,0 +1,218 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2021-2022 Linaro Ltd.
+ * Copyright (C) 2018-2020 The Linux Foundation
+ */
+
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_mux.h>
+
+#define FSA4480_SWITCH_ENABLE  0x04
+#define FSA4480_SWITCH_SELECT  0x05
+#define FSA4480_SWITCH_STATUS1 0x07
+#define FSA4480_SLOW_L         0x08
+#define FSA4480_SLOW_R         0x09
+#define FSA4480_SLOW_MIC       0x0a
+#define FSA4480_SLOW_SENSE     0x0b
+#define FSA4480_SLOW_GND       0x0c
+#define FSA4480_DELAY_L_R      0x0d
+#define FSA4480_DELAY_L_MIC    0x0e
+#define FSA4480_DELAY_L_SENSE  0x0f
+#define FSA4480_DELAY_L_AGND   0x10
+#define FSA4480_RESET          0x1e
+#define FSA4480_MAX_REGISTER   0x1f
+
+#define FSA4480_ENABLE_DEVICE  BIT(7)
+#define FSA4480_ENABLE_SBU     GENMASK(6, 5)
+#define FSA4480_ENABLE_USB     GENMASK(4, 3)
+
+#define FSA4480_SEL_SBU_REVERSE        GENMASK(6, 5)
+#define FSA4480_SEL_USB                GENMASK(4, 3)
+
+struct fsa4480 {
+       struct i2c_client *client;
+
+       /* used to serialize concurrent change requests */
+       struct mutex lock;
+
+       struct typec_switch_dev *sw;
+       struct typec_mux_dev *mux;
+
+       struct regmap *regmap;
+
+       u8 cur_enable;
+       u8 cur_select;
+};
+
+static const struct regmap_config fsa4480_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = FSA4480_MAX_REGISTER,
+       /* Accesses only done under fsa4480->lock */
+       .disable_locking = true,
+};
+
+static int fsa4480_switch_set(struct typec_switch_dev *sw,
+                             enum typec_orientation orientation)
+{
+       struct fsa4480 *fsa = typec_switch_get_drvdata(sw);
+       u8 new_sel;
+
+       mutex_lock(&fsa->lock);
+       new_sel = FSA4480_SEL_USB;
+       if (orientation == TYPEC_ORIENTATION_REVERSE)
+               new_sel |= FSA4480_SEL_SBU_REVERSE;
+
+       if (new_sel == fsa->cur_select)
+               goto out_unlock;
+
+       if (fsa->cur_enable & FSA4480_ENABLE_SBU) {
+               /* Disable SBU output while re-configuring the switch */
+               regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE,
+                            fsa->cur_enable & ~FSA4480_ENABLE_SBU);
+
+               /* 35us to allow the SBU switch to turn off */
+               usleep_range(35, 1000);
+       }
+
+       regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, new_sel);
+       fsa->cur_select = new_sel;
+
+       if (fsa->cur_enable & FSA4480_ENABLE_SBU) {
+               regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable);
+
+               /* 15us to allow the SBU switch to turn on again */
+               usleep_range(15, 1000);
+       }
+
+out_unlock:
+       mutex_unlock(&fsa->lock);
+
+       return 0;
+}
+
+static int fsa4480_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
+{
+       struct fsa4480 *fsa = typec_mux_get_drvdata(mux);
+       u8 new_enable;
+
+       mutex_lock(&fsa->lock);
+
+       new_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB;
+       if (state->mode >= TYPEC_DP_STATE_A)
+               new_enable |= FSA4480_ENABLE_SBU;
+
+       if (new_enable == fsa->cur_enable)
+               goto out_unlock;
+
+       regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, new_enable);
+       fsa->cur_enable = new_enable;
+
+       if (new_enable & FSA4480_ENABLE_SBU) {
+               /* 15us to allow the SBU switch to turn off */
+               usleep_range(15, 1000);
+       }
+
+out_unlock:
+       mutex_unlock(&fsa->lock);
+
+       return 0;
+}
+
+static int fsa4480_probe(struct i2c_client *client)
+{
+       struct device *dev = &client->dev;
+       struct typec_switch_desc sw_desc = { };
+       struct typec_mux_desc mux_desc = { };
+       struct fsa4480 *fsa;
+
+       fsa = devm_kzalloc(dev, sizeof(*fsa), GFP_KERNEL);
+       if (!fsa)
+               return -ENOMEM;
+
+       fsa->client = client;
+       mutex_init(&fsa->lock);
+
+       fsa->regmap = devm_regmap_init_i2c(client, &fsa4480_regmap_config);
+       if (IS_ERR(fsa->regmap))
+               return dev_err_probe(dev, PTR_ERR(fsa->regmap), "failed to initialize regmap\n");
+
+       fsa->cur_enable = FSA4480_ENABLE_DEVICE | FSA4480_ENABLE_USB;
+       fsa->cur_select = FSA4480_SEL_USB;
+
+       /* set default settings */
+       regmap_write(fsa->regmap, FSA4480_SLOW_L, 0x00);
+       regmap_write(fsa->regmap, FSA4480_SLOW_R, 0x00);
+       regmap_write(fsa->regmap, FSA4480_SLOW_MIC, 0x00);
+       regmap_write(fsa->regmap, FSA4480_SLOW_SENSE, 0x00);
+       regmap_write(fsa->regmap, FSA4480_SLOW_GND, 0x00);
+       regmap_write(fsa->regmap, FSA4480_DELAY_L_R, 0x00);
+       regmap_write(fsa->regmap, FSA4480_DELAY_L_MIC, 0x00);
+       regmap_write(fsa->regmap, FSA4480_DELAY_L_SENSE, 0x00);
+       regmap_write(fsa->regmap, FSA4480_DELAY_L_AGND, 0x09);
+       regmap_write(fsa->regmap, FSA4480_SWITCH_SELECT, fsa->cur_select);
+       regmap_write(fsa->regmap, FSA4480_SWITCH_ENABLE, fsa->cur_enable);
+
+       sw_desc.drvdata = fsa;
+       sw_desc.fwnode = dev_fwnode(dev);
+       sw_desc.set = fsa4480_switch_set;
+
+       fsa->sw = typec_switch_register(dev, &sw_desc);
+       if (IS_ERR(fsa->sw))
+               return dev_err_probe(dev, PTR_ERR(fsa->sw), "failed to register typec switch\n");
+
+       mux_desc.drvdata = fsa;
+       mux_desc.fwnode = dev_fwnode(dev);
+       mux_desc.set = fsa4480_mux_set;
+
+       fsa->mux = typec_mux_register(dev, &mux_desc);
+       if (IS_ERR(fsa->mux)) {
+               typec_switch_unregister(fsa->sw);
+               return dev_err_probe(dev, PTR_ERR(fsa->mux), "failed to register typec mux\n");
+       }
+
+       i2c_set_clientdata(client, fsa);
+       return 0;
+}
+
+static int fsa4480_remove(struct i2c_client *client)
+{
+       struct fsa4480 *fsa = i2c_get_clientdata(client);
+
+       typec_mux_unregister(fsa->mux);
+       typec_switch_unregister(fsa->sw);
+
+       return 0;
+}
+
+static const struct i2c_device_id fsa4480_table[] = {
+       { "fsa4480" },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, fsa4480_table);
+
+static const struct of_device_id fsa4480_of_table[] = {
+       { .compatible = "fcs,fsa4480" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, fsa4480_of_table);
+
+static struct i2c_driver fsa4480_driver = {
+       .driver = {
+               .name = "fsa4480",
+               .of_match_table = fsa4480_of_table,
+       },
+       .probe_new      = fsa4480_probe,
+       .remove         = fsa4480_remove,
+       .id_table       = fsa4480_table,
+};
+module_i2c_driver(fsa4480_driver);
+
+MODULE_DESCRIPTION("ON Semiconductor FSA4480 driver");
+MODULE_LICENSE("GPL v2");
index 2cdd221..47b733f 100644 (file)
@@ -121,8 +121,8 @@ struct pmc_usb_port {
        int num;
        u32 iom_status;
        struct pmc_usb *pmc;
-       struct typec_mux *typec_mux;
-       struct typec_switch *typec_sw;
+       struct typec_mux_dev *typec_mux;
+       struct typec_switch_dev *typec_sw;
        struct usb_role_switch *usb_sw;
 
        enum typec_orientation orientation;
@@ -173,7 +173,7 @@ static int hsl_orientation(struct pmc_usb_port *port)
        return port->orientation - 1;
 }
 
-static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
+static int pmc_usb_send_command(struct intel_scu_ipc_dev *ipc, u8 *msg, u32 len)
 {
        u8 response[4];
        u8 status_res;
@@ -184,7 +184,7 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
         * Status can be checked from the response message if the
         * function intel_scu_ipc_dev_command succeeds.
         */
-       ret = intel_scu_ipc_dev_command(port->pmc->ipc, PMC_USBC_CMD, 0, msg,
+       ret = intel_scu_ipc_dev_command(ipc, PMC_USBC_CMD, 0, msg,
                                        len, response, sizeof(response));
 
        if (ret)
@@ -203,6 +203,23 @@ static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
        return 0;
 }
 
+static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
+{
+       int retry_count = 3;
+       int ret;
+
+       /*
+        * If PMC is busy then retry the command once again
+        */
+       while (retry_count--) {
+               ret = pmc_usb_send_command(port->pmc->ipc, msg, len);
+               if (ret != -EBUSY)
+                       break;
+       }
+
+       return ret;
+}
+
 static int
 pmc_usb_mux_dp_hpd(struct pmc_usb_port *port, struct typec_displayport_data *dp)
 {
@@ -416,7 +433,7 @@ static int pmc_usb_connect(struct pmc_usb_port *port, enum usb_role role)
 }
 
 static int
-pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+pmc_usb_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
 {
        struct pmc_usb_port *port = typec_mux_get_drvdata(mux);
 
@@ -452,7 +469,7 @@ pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
        return -EOPNOTSUPP;
 }
 
-static int pmc_usb_set_orientation(struct typec_switch *sw,
+static int pmc_usb_set_orientation(struct typec_switch_dev *sw,
                                   enum typec_orientation orientation)
 {
        struct pmc_usb_port *port = typec_switch_get_drvdata(sw);
index 7afe275..6ce9f28 100644 (file)
@@ -23,8 +23,8 @@
 struct pi3usb30532 {
        struct i2c_client *client;
        struct mutex lock; /* protects the cached conf register */
-       struct typec_switch *sw;
-       struct typec_mux *mux;
+       struct typec_switch_dev *sw;
+       struct typec_mux_dev *mux;
        u8 conf;
 };
 
@@ -45,7 +45,7 @@ static int pi3usb30532_set_conf(struct pi3usb30532 *pi, u8 new_conf)
        return 0;
 }
 
-static int pi3usb30532_sw_set(struct typec_switch *sw,
+static int pi3usb30532_sw_set(struct typec_switch_dev *sw,
                              enum typec_orientation orientation)
 {
        struct pi3usb30532 *pi = typec_switch_get_drvdata(sw);
@@ -74,7 +74,7 @@ static int pi3usb30532_sw_set(struct typec_switch *sw,
 }
 
 static int
-pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+pi3usb30532_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state)
 {
        struct pi3usb30532 *pi = typec_mux_get_drvdata(mux);
        u8 new_conf;
index 16b4560..dfbba5a 100644 (file)
@@ -93,6 +93,8 @@ struct tps6598x {
        struct power_supply *psy;
        struct power_supply_desc psy_desc;
        enum power_supply_usb_type usb_type;
+
+       u16 pwr_status;
 };
 
 static enum power_supply_property tps6598x_psy_props[] = {
@@ -230,17 +232,12 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status)
 {
        struct typec_partner_desc desc;
        enum typec_pwr_opmode mode;
-       u16 pwr_status;
        int ret;
 
        if (tps->partner)
                return 0;
 
-       ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status);
-       if (ret < 0)
-               return ret;
-
-       mode = TPS_POWER_STATUS_PWROPMODE(pwr_status);
+       mode = TPS_POWER_STATUS_PWROPMODE(tps->pwr_status);
 
        desc.usb_pd = mode == TYPEC_PWR_MODE_PD;
        desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
@@ -455,6 +452,7 @@ static bool tps6598x_read_power_status(struct tps6598x *tps)
                dev_err(tps->dev, "failed to read power status: %d\n", ret);
                return false;
        }
+       tps->pwr_status = pwr_status;
        trace_tps6598x_power_status(pwr_status);
 
        return true;
@@ -601,15 +599,8 @@ static const struct regmap_config tps6598x_regmap_config = {
 static int tps6598x_psy_get_online(struct tps6598x *tps,
                                   union power_supply_propval *val)
 {
-       int ret;
-       u16 pwr_status;
-
-       ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status);
-       if (ret < 0)
-               return ret;
-
-       if (TPS_POWER_STATUS_CONNECTION(pwr_status) &&
-           TPS_POWER_STATUS_SOURCESINK(pwr_status)) {
+       if (TPS_POWER_STATUS_CONNECTION(tps->pwr_status) &&
+           TPS_POWER_STATUS_SOURCESINK(tps->pwr_status)) {
                val->intval = 1;
        } else {
                val->intval = 0;
@@ -622,15 +613,11 @@ static int tps6598x_psy_get_prop(struct power_supply *psy,
                                 union power_supply_propval *val)
 {
        struct tps6598x *tps = power_supply_get_drvdata(psy);
-       u16 pwr_status;
        int ret = 0;
 
        switch (psp) {
        case POWER_SUPPLY_PROP_USB_TYPE:
-               ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &pwr_status);
-               if (ret < 0)
-                       return ret;
-               if (TPS_POWER_STATUS_PWROPMODE(pwr_status) == TYPEC_PWR_MODE_PD)
+               if (TPS_POWER_STATUS_PWROPMODE(tps->pwr_status) == TYPEC_PWR_MODE_PD)
                        val->intval = POWER_SUPPLY_USB_TYPE_PD;
                else
                        val->intval = POWER_SUPPLY_USB_TYPE_C;
@@ -837,6 +824,11 @@ static int tps6598x_probe(struct i2c_client *client)
        fwnode_handle_put(fwnode);
 
        if (status & TPS_STATUS_PLUG_PRESENT) {
+               ret = tps6598x_read16(tps, TPS_REG_POWER_STATUS, &tps->pwr_status);
+               if (ret < 0) {
+                       dev_err(tps->dev, "failed to read power status: %d\n", ret);
+                       goto err_role_put;
+               }
                ret = tps6598x_connect(tps, status);
                if (ret)
                        dev_err(&client->dev, "failed to register partner\n");
index a6045ae..cbd862f 100644 (file)
@@ -1063,6 +1063,14 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
        con->num = index + 1;
        con->ucsi = ucsi;
 
+       cap->fwnode = ucsi_find_fwnode(con);
+       con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode);
+       if (IS_ERR(con->usb_role_sw)) {
+               dev_err(ucsi->dev, "con%d: failed to get usb role switch\n",
+                       con->num);
+               return PTR_ERR(con->usb_role_sw);
+       }
+
        /* Delay other interactions with the con until registration is complete */
        mutex_lock(&con->lock);
 
@@ -1098,7 +1106,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
        if (con->cap.op_mode & UCSI_CONCAP_OPMODE_DEBUG_ACCESSORY)
                *accessory = TYPEC_ACCESSORY_DEBUG;
 
-       cap->fwnode = ucsi_find_fwnode(con);
        cap->driver_data = con;
        cap->ops = &ucsi_ops;
 
@@ -1156,13 +1163,6 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
                ucsi_port_psy_changed(con);
        }
 
-       con->usb_role_sw = fwnode_usb_role_switch_get(cap->fwnode);
-       if (IS_ERR(con->usb_role_sw)) {
-               dev_err(ucsi->dev, "con%d: failed to get usb role switch\n",
-                       con->num);
-               con->usb_role_sw = NULL;
-       }
-
        /* Only notify USB controller if partner supports USB data */
        if (!(UCSI_CONSTAT_PARTNER_FLAGS(con->status.flags) & UCSI_CONSTAT_PARTNER_FLAG_USB))
                u_role = USB_ROLE_NONE;
@@ -1196,6 +1196,32 @@ out_unlock:
        return ret;
 }
 
+static void ucsi_unregister_connectors(struct ucsi *ucsi)
+{
+       struct ucsi_connector *con;
+       int i;
+
+       if (!ucsi->connector)
+               return;
+
+       for (i = 0; i < ucsi->cap.num_connectors; i++) {
+               con = &ucsi->connector[i];
+
+               if (!con->wq)
+                       break;
+
+               cancel_work_sync(&con->work);
+               ucsi_unregister_partner(con);
+               ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
+               ucsi_unregister_port_psy(con);
+               destroy_workqueue(con->wq);
+               typec_unregister_port(con->port);
+       }
+
+       kfree(ucsi->connector);
+       ucsi->connector = NULL;
+}
+
 /**
  * ucsi_init - Initialize UCSI interface
  * @ucsi: UCSI to be initialized
@@ -1204,7 +1230,6 @@ out_unlock:
  */
 static int ucsi_init(struct ucsi *ucsi)
 {
-       struct ucsi_connector *con;
        u64 command;
        int ret;
        int i;
@@ -1235,7 +1260,7 @@ static int ucsi_init(struct ucsi *ucsi)
        }
 
        /* Allocate the connectors. Released in ucsi_unregister() */
-       ucsi->connector = kcalloc(ucsi->cap.num_connectors + 1,
+       ucsi->connector = kcalloc(ucsi->cap.num_connectors,
                                  sizeof(*ucsi->connector), GFP_KERNEL);
        if (!ucsi->connector) {
                ret = -ENOMEM;
@@ -1259,15 +1284,7 @@ static int ucsi_init(struct ucsi *ucsi)
        return 0;
 
 err_unregister:
-       for (con = ucsi->connector; con->port; con++) {
-               ucsi_unregister_partner(con);
-               ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
-               ucsi_unregister_port_psy(con);
-               if (con->wq)
-                       destroy_workqueue(con->wq);
-               typec_unregister_port(con->port);
-               con->port = NULL;
-       }
+       ucsi_unregister_connectors(ucsi);
 
 err_reset:
        memset(&ucsi->cap, 0, sizeof(ucsi->cap));
@@ -1278,12 +1295,20 @@ err:
 
 static void ucsi_init_work(struct work_struct *work)
 {
-       struct ucsi *ucsi = container_of(work, struct ucsi, work);
+       struct ucsi *ucsi = container_of(work, struct ucsi, work.work);
        int ret;
 
        ret = ucsi_init(ucsi);
        if (ret)
                dev_err(ucsi->dev, "PPM init failed (%d)\n", ret);
+
+       if (ret == -EPROBE_DEFER) {
+               if (ucsi->work_count++ > UCSI_ROLE_SWITCH_WAIT_COUNT)
+                       return;
+
+               queue_delayed_work(system_long_wq, &ucsi->work,
+                                  UCSI_ROLE_SWITCH_INTERVAL);
+       }
 }
 
 /**
@@ -1323,7 +1348,7 @@ struct ucsi *ucsi_create(struct device *dev, const struct ucsi_operations *ops)
        if (!ucsi)
                return ERR_PTR(-ENOMEM);
 
-       INIT_WORK(&ucsi->work, ucsi_init_work);
+       INIT_DELAYED_WORK(&ucsi->work, ucsi_init_work);
        mutex_init(&ucsi->ppm_lock);
        ucsi->dev = dev;
        ucsi->ops = ops;
@@ -1358,7 +1383,7 @@ int ucsi_register(struct ucsi *ucsi)
        if (!ucsi->version)
                return -ENODEV;
 
-       queue_work(system_long_wq, &ucsi->work);
+       queue_delayed_work(system_long_wq, &ucsi->work, 0);
 
        return 0;
 }
@@ -1373,26 +1398,14 @@ EXPORT_SYMBOL_GPL(ucsi_register);
 void ucsi_unregister(struct ucsi *ucsi)
 {
        u64 cmd = UCSI_SET_NOTIFICATION_ENABLE;
-       int i;
 
        /* Make sure that we are not in the middle of driver initialization */
-       cancel_work_sync(&ucsi->work);
+       cancel_delayed_work_sync(&ucsi->work);
 
        /* Disable notifications */
        ucsi->ops->async_write(ucsi, UCSI_CONTROL, &cmd, sizeof(cmd));
 
-       for (i = 0; i < ucsi->cap.num_connectors; i++) {
-               cancel_work_sync(&ucsi->connector[i].work);
-               ucsi_unregister_partner(&ucsi->connector[i]);
-               ucsi_unregister_altmodes(&ucsi->connector[i],
-                                        UCSI_RECIPIENT_CON);
-               ucsi_unregister_port_psy(&ucsi->connector[i]);
-               if (ucsi->connector[i].wq)
-                       destroy_workqueue(ucsi->connector[i].wq);
-               typec_unregister_port(ucsi->connector[i].port);
-       }
-
-       kfree(ucsi->connector);
+       ucsi_unregister_connectors(ucsi);
 }
 EXPORT_SYMBOL_GPL(ucsi_unregister);
 
index 280f1e1..8eb391e 100644 (file)
@@ -287,7 +287,11 @@ struct ucsi {
        struct ucsi_capability cap;
        struct ucsi_connector *connector;
 
-       struct work_struct work;
+       struct delayed_work work;
+       int work_count;
+#define UCSI_ROLE_SWITCH_RETRY_PER_HZ  10
+#define UCSI_ROLE_SWITCH_INTERVAL      (HZ / UCSI_ROLE_SWITCH_RETRY_PER_HZ)
+#define UCSI_ROLE_SWITCH_WAIT_COUNT    (10 * UCSI_ROLE_SWITCH_RETRY_PER_HZ)
 
        /* PPM Communication lock */
        struct mutex ppm_lock;
index d8d3892..3c6d452 100644 (file)
@@ -393,7 +393,6 @@ static int stub_probe(struct usb_device *udev)
 
 err_port:
        dev_set_drvdata(&udev->dev, NULL);
-       usb_put_dev(udev);
 
        /* we already have busid_priv, just lock busid_lock */
        spin_lock(&busid_priv->busid_lock);
@@ -408,6 +407,7 @@ call_put_busid_priv:
        put_busid_priv(busid_priv);
 
 sdev_free:
+       usb_put_dev(udev);
        stub_device_free(sdev);
 
        return rc;
index 325c220..5dd41e8 100644 (file)
@@ -138,7 +138,9 @@ static int tweak_set_configuration_cmd(struct urb *urb)
        req = (struct usb_ctrlrequest *) urb->setup_packet;
        config = le16_to_cpu(req->wValue);
 
+       usb_lock_device(sdev->udev);
        err = usb_set_configuration(sdev->udev, config);
+       usb_unlock_device(sdev->udev);
        if (err && err != -ENODEV)
                dev_err(&sdev->udev->dev, "can't set config #%d, error %d\n",
                        config, err);
index 4cd4b32..de7ff33 100644 (file)
@@ -447,6 +447,11 @@ static inline void *device_connection_find_match(struct device *dev,
        return fwnode_connection_find_match(dev_fwnode(dev), con_id, data, match);
 }
 
+int fwnode_connection_find_matches(struct fwnode_handle *fwnode,
+                                  const char *con_id, void *data,
+                                  devcon_match_fn_t match,
+                                  void **matches, unsigned int matches_len);
+
 /* -------------------------------------------------------------------------- */
 /* Software fwnode support - when HW description is incomplete or missing */
 
index 124e13c..e13fe15 100644 (file)
@@ -198,15 +198,15 @@ void tb_unregister_property_dir(const char *key, struct tb_property_dir *dir);
  * @local_property_block_len: Length of the @local_property_block in dwords
  * @remote_properties: Properties exported by the remote domain
  * @remote_property_block_gen: Generation of @remote_properties
- * @get_uuid_work: Work used to retrieve @remote_uuid
- * @uuid_retries: Number of times left @remote_uuid is requested before
- *               giving up
- * @get_properties_work: Work used to get remote domain properties
- * @properties_retries: Number of times left to read properties
+ * @state: Next XDomain discovery state to run
+ * @state_work: Work used to run the next state
+ * @state_retries: Number of retries remain for the state
  * @properties_changed_work: Work used to notify the remote domain that
  *                          our properties have changed
  * @properties_changed_retries: Number of times left to send properties
  *                             changed notification
+ * @bonding_possible: True if lane bonding is possible on local side
+ * @target_link_width: Target link width from the remote host
  * @link: Root switch link the remote domain is connected (ICM only)
  * @depth: Depth in the chain the remote domain is connected (ICM only)
  *
@@ -244,12 +244,13 @@ struct tb_xdomain {
        u32 local_property_block_len;
        struct tb_property_dir *remote_properties;
        u32 remote_property_block_gen;
-       struct delayed_work get_uuid_work;
-       int uuid_retries;
-       struct delayed_work get_properties_work;
-       int properties_retries;
+       int state;
+       struct delayed_work state_work;
+       int state_retries;
        struct delayed_work properties_changed_work;
        int properties_changed_retries;
+       bool bonding_possible;
+       u8 target_link_width;
        u8 link;
        u8 depth;
 };
index 200b7b7..60bee86 100644 (file)
@@ -1969,21 +1969,10 @@ usb_pipe_endpoint(struct usb_device *dev, unsigned int pipe)
        return eps[usb_pipeendpoint(pipe)];
 }
 
-/*-------------------------------------------------------------------------*/
-
-static inline __u16
-usb_maxpacket(struct usb_device *udev, int pipe, int is_out)
+static inline u16 usb_maxpacket(struct usb_device *udev, int pipe)
 {
-       struct usb_host_endpoint        *ep;
-       unsigned                        epnum = usb_pipeendpoint(pipe);
+       struct usb_host_endpoint *ep = usb_pipe_endpoint(udev, pipe);
 
-       if (is_out) {
-               WARN_ON(usb_pipein(pipe));
-               ep = udev->ep_out[epnum];
-       } else {
-               WARN_ON(usb_pipeout(pipe));
-               ep = udev->ep_in[epnum];
-       }
        if (!ep)
                return 0;
 
@@ -1991,8 +1980,6 @@ usb_maxpacket(struct usb_device *udev, int pipe, int is_out)
        return usb_endpoint_maxp(&ep->desc);
 }
 
-/* ----------------------------------------------------------------------- */
-
 /* translate USB error codes to codes user space understands */
 static inline int usb_translate_errors(int error_code)
 {
index 10fe57c..3ad58b7 100644 (file)
@@ -386,6 +386,7 @@ struct usb_gadget_ops {
  * @lpm_capable: If the gadget max_speed is FULL or HIGH, this flag
  *     indicates that it supports LPM as per the LPM ECN & errata.
  * @irq: the interrupt number for device controller.
+ * @id_number: a unique ID number for ensuring that gadget names are distinct
  *
  * Gadgets have a mostly-portable "gadget driver" implementing device
  * functions, handling all usb configurations and interfaces.  Gadget
@@ -446,6 +447,7 @@ struct usb_gadget {
        unsigned                        connected:1;
        unsigned                        lpm_capable:1;
        int                             irq;
+       int                             id_number;
 };
 #define work_to_gadget(w)      (container_of((w), struct usb_gadget, work))
 
@@ -664,9 +666,9 @@ static inline int usb_gadget_check_config(struct usb_gadget *gadget)
  * @driver: Driver model state for this driver.
  * @udc_name: A name of UDC this driver should be bound to. If udc_name is NULL,
  *     this driver will be bound to any available UDC.
- * @pending: UDC core private data used for deferred probe of this driver.
- * @match_existing_only: If udc is not found, return an error and don't add this
- *      gadget driver to list of pending driver
+ * @match_existing_only: If udc is not found, return an error and fail
+ *     the driver registration
+ * @is_bound: Allow a driver to be bound to only one gadget
  *
  * Devices are disabled till a gadget driver successfully bind()s, which
  * means the driver will handle setup() requests needed to enumerate (and
@@ -729,8 +731,8 @@ struct usb_gadget_driver {
        struct device_driver    driver;
 
        char                    *udc_name;
-       struct list_head        pending;
        unsigned                match_existing_only:1;
+       bool                    is_bound:1;
 };
 
 
@@ -740,22 +742,30 @@ struct usb_gadget_driver {
 /* driver modules register and unregister, as usual.
  * these calls must be made in a context that can sleep.
  *
- * these will usually be implemented directly by the hardware-dependent
- * usb bus interface driver, which will only support a single driver.
+ * A gadget driver can be bound to only one gadget at a time.
  */
 
 /**
- * usb_gadget_probe_driver - probe a gadget driver
+ * usb_gadget_register_driver_owner - register a gadget driver
  * @driver: the driver being registered
+ * @owner: the driver module
+ * @mod_name: the driver module's build name
  * Context: can sleep
  *
  * Call this in your gadget driver's module initialization function,
- * to tell the underlying usb controller driver about your driver.
+ * to tell the underlying UDC controller driver about your driver.
  * The @bind() function will be called to bind it to a gadget before this
  * registration call returns.  It's expected that the @bind() function will
  * be in init sections.
+ *
+ * Use the macro defined below instead of calling this directly.
  */
-int usb_gadget_probe_driver(struct usb_gadget_driver *driver);
+int usb_gadget_register_driver_owner(struct usb_gadget_driver *driver,
+               struct module *owner, const char *mod_name);
+
+/* use a define to avoid include chaining to get THIS_MODULE & friends */
+#define usb_gadget_register_driver(driver) \
+       usb_gadget_register_driver_owner(driver, THIS_MODULE, KBUILD_MODNAME)
 
 /**
  * usb_gadget_unregister_driver - unregister a gadget driver
index 548a028..2c1fc92 100644 (file)
@@ -124,6 +124,7 @@ struct usb_hcd {
 #define HCD_FLAG_RH_RUNNING            5       /* root hub is running? */
 #define HCD_FLAG_DEAD                  6       /* controller has died? */
 #define HCD_FLAG_INTF_AUTHORIZED       7       /* authorize interfaces? */
+#define HCD_FLAG_DEFER_RH_REGISTER     8       /* Defer roothub registration */
 
        /* The flags can be tested using these macros; they are likely to
         * be slightly faster than test_bit().
@@ -134,6 +135,7 @@ struct usb_hcd {
 #define HCD_WAKEUP_PENDING(hcd)        ((hcd)->flags & (1U << HCD_FLAG_WAKEUP_PENDING))
 #define HCD_RH_RUNNING(hcd)    ((hcd)->flags & (1U << HCD_FLAG_RH_RUNNING))
 #define HCD_DEAD(hcd)          ((hcd)->flags & (1U << HCD_FLAG_DEAD))
+#define HCD_DEFER_RH_REGISTER(hcd) ((hcd)->flags & (1U << HCD_FLAG_DEFER_RH_REGISTER))
 
        /*
         * Specifies if interfaces are authorized by default
index a9d9957..ee57781 100644 (file)
@@ -8,11 +8,13 @@
 
 struct device;
 struct typec_mux;
+struct typec_mux_dev;
 struct typec_switch;
+struct typec_switch_dev;
 struct typec_altmode;
 struct fwnode_handle;
 
-typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw,
+typedef int (*typec_switch_set_fn_t)(struct typec_switch_dev *sw,
                                     enum typec_orientation orientation);
 
 struct typec_switch_desc {
@@ -32,13 +34,13 @@ static inline struct typec_switch *typec_switch_get(struct device *dev)
        return fwnode_typec_switch_get(dev_fwnode(dev));
 }
 
-struct typec_switch *
+struct typec_switch_dev *
 typec_switch_register(struct device *parent,
                      const struct typec_switch_desc *desc);
-void typec_switch_unregister(struct typec_switch *sw);
+void typec_switch_unregister(struct typec_switch_dev *sw);
 
-void typec_switch_set_drvdata(struct typec_switch *sw, void *data);
-void *typec_switch_get_drvdata(struct typec_switch *sw);
+void typec_switch_set_drvdata(struct typec_switch_dev *sw, void *data);
+void *typec_switch_get_drvdata(struct typec_switch_dev *sw);
 
 struct typec_mux_state {
        struct typec_altmode *alt;
@@ -46,7 +48,7 @@ struct typec_mux_state {
        void *data;
 };
 
-typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux,
+typedef int (*typec_mux_set_fn_t)(struct typec_mux_dev *mux,
                                  struct typec_mux_state *state);
 
 struct typec_mux_desc {
@@ -67,11 +69,11 @@ typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc)
        return fwnode_typec_mux_get(dev_fwnode(dev), desc);
 }
 
-struct typec_mux *
+struct typec_mux_dev *
 typec_mux_register(struct device *parent, const struct typec_mux_desc *desc);
-void typec_mux_unregister(struct typec_mux *mux);
+void typec_mux_unregister(struct typec_mux_dev *mux);
 
-void typec_mux_set_drvdata(struct typec_mux *mux, void *data);
-void *typec_mux_get_drvdata(struct typec_mux *mux);
+void typec_mux_set_drvdata(struct typec_mux_dev *mux, void *data);
+void *typec_mux_get_drvdata(struct typec_mux_dev *mux);
 
 #endif /* __USB_TYPEC_MUX */
index fdbdfb7..6a4af72 100644 (file)
@@ -552,10 +552,10 @@ int line6_init_pcm(struct usb_line6 *line6,
 
        line6pcm->max_packet_size_in =
                usb_maxpacket(line6->usbdev,
-                       usb_rcvisocpipe(line6->usbdev, ep_read), 0);
+                       usb_rcvisocpipe(line6->usbdev, ep_read));
        line6pcm->max_packet_size_out =
                usb_maxpacket(line6->usbdev,
-                       usb_sndisocpipe(line6->usbdev, ep_write), 1);
+                       usb_sndisocpipe(line6->usbdev, ep_write));
        if (!line6pcm->max_packet_size_in || !line6pcm->max_packet_size_out) {
                dev_err(line6pcm->line6->ifcdev,
                        "cannot get proper max packet size\n");
index 7c6ca2b..0ee7db5 100644 (file)
@@ -1286,7 +1286,7 @@ static int snd_usbmidi_in_endpoint_create(struct snd_usb_midi *umidi,
                pipe = usb_rcvintpipe(umidi->dev, ep_info->in_ep);
        else
                pipe = usb_rcvbulkpipe(umidi->dev, ep_info->in_ep);
-       length = usb_maxpacket(umidi->dev, pipe, 0);
+       length = usb_maxpacket(umidi->dev, pipe);
        for (i = 0; i < INPUT_URBS; ++i) {
                buffer = usb_alloc_coherent(umidi->dev, length, GFP_KERNEL,
                                            &ep->urbs[i]->transfer_dma);
@@ -1375,7 +1375,7 @@ static int snd_usbmidi_out_endpoint_create(struct snd_usb_midi *umidi,
                pipe = usb_sndbulkpipe(umidi->dev, ep_info->out_ep);
        switch (umidi->usb_id) {
        default:
-               ep->max_transfer = usb_maxpacket(umidi->dev, pipe, 1);
+               ep->max_transfer = usb_maxpacket(umidi->dev, pipe);
                break;
                /*
                 * Various chips declare a packet size larger than 4 bytes, but
index 9d0e447..a4d32e8 100644 (file)
@@ -51,7 +51,7 @@ static int init_pipe_urbs(struct usb_stream_kernel *sk,
 {
        int u, p;
        int maxpacket = use_packsize ?
-               use_packsize : usb_maxpacket(dev, pipe, usb_pipeout(pipe));
+               use_packsize : usb_maxpacket(dev, pipe);
        int transfer_length = maxpacket * sk->n_o_ps;
 
        for (u = 0; u < USB_STREAM_NURBS;
@@ -171,7 +171,7 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
        out_pipe = usb_sndisocpipe(dev, out_endpoint);
 
        max_packsize = use_packsize ?
-               use_packsize : usb_maxpacket(dev, in_pipe, 0);
+               use_packsize : usb_maxpacket(dev, in_pipe);
 
        /*
                t_period = period_frames / sample_rate
@@ -187,7 +187,7 @@ struct usb_stream *usb_stream_new(struct usb_stream_kernel *sk,
        read_size += packets * USB_STREAM_URBDEPTH *
                (max_packsize + sizeof(struct usb_stream_packet));
 
-       max_packsize = usb_maxpacket(dev, out_pipe, 1);
+       max_packsize = usb_maxpacket(dev, out_pipe);
        write_size = max_packsize * packets * USB_STREAM_URBDEPTH;
 
        if (read_size >= 256*PAGE_SIZE || write_size >= 256*PAGE_SIZE) {
index cfc1ea5..9cd5e3a 100644 (file)
@@ -421,7 +421,7 @@ static int usx2y_urbs_allocate(struct snd_usx2y_substream *subs)
 
        pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
                        usb_rcvisocpipe(dev, subs->endpoint);
-       subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
+       subs->maxpacksize = usb_maxpacket(dev, pipe);
        if (!subs->maxpacksize)
                return -EINVAL;
 
index db83522..240349b 100644 (file)
@@ -321,7 +321,7 @@ static int usx2y_usbpcm_urbs_allocate(struct snd_usx2y_substream *subs)
 
        pipe = is_playback ? usb_sndisocpipe(dev, subs->endpoint) :
                        usb_rcvisocpipe(dev, subs->endpoint);
-       subs->maxpacksize = usb_maxpacket(dev, pipe, is_playback);
+       subs->maxpacksize = usb_maxpacket(dev, pipe);
        if (!subs->maxpacksize)
                return -EINVAL;
 
index 69c3ead..474bae8 100644 (file)
@@ -482,7 +482,7 @@ usage:
        }
        if (not)
                return 0;
-       if (testdevs && testdevs->next == 0 && !device)
+       if (testdevs && !testdevs->next && !device)
                device = testdevs->name;
        for (entry = testdevs; entry; entry = entry->next) {
                int     status;