Merge 6.5-rc4 into usb-next
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 31 Jul 2023 07:36:55 +0000 (09:36 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 31 Jul 2023 07:36:55 +0000 (09:36 +0200)
We need the USB fixes in here for testing and for other patches to be
applied on top of.

Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
112 files changed:
Documentation/ABI/testing/configfs-usb-gadget-midi2 [new file with mode: 0644]
Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/ci-hdrc-usb2.yaml
Documentation/devicetree/bindings/usb/cypress,hx3.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/generic-ehci.yaml
Documentation/devicetree/bindings/usb/genesys,gl850g.yaml
Documentation/devicetree/bindings/usb/qcom,dwc3.yaml
Documentation/driver-api/usb/usb.rst
Documentation/usb/gadget-testing.rst
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/realtek/Kconfig [new file with mode: 0644]
drivers/phy/realtek/Makefile [new file with mode: 0644]
drivers/phy/realtek/phy-rtk-usb2.c [new file with mode: 0644]
drivers/phy/realtek/phy-rtk-usb3.c [new file with mode: 0644]
drivers/usb/cdns3/cdns3-gadget.c
drivers/usb/cdns3/cdns3-plat.c
drivers/usb/cdns3/cdns3-starfive.c
drivers/usb/cdns3/cdns3-ti.c
drivers/usb/cdns3/cdnsp-pci.c
drivers/usb/cdns3/core.c
drivers/usb/cdns3/core.h
drivers/usb/cdns3/drd.c
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/ci_hdrc_imx.h
drivers/usb/chipidea/ci_hdrc_tegra.c
drivers/usb/chipidea/core.c
drivers/usb/chipidea/udc.c
drivers/usb/chipidea/usbmisc_imx.c
drivers/usb/common/common.c
drivers/usb/core/file.c
drivers/usb/core/hub.c
drivers/usb/core/message.c
drivers/usb/core/of.c
drivers/usb/core/usb.c
drivers/usb/core/usb.h
drivers/usb/dwc2/gadget.c
drivers/usb/dwc2/platform.c
drivers/usb/dwc3/dwc3-am62.c
drivers/usb/dwc3/dwc3-imx8mp.c
drivers/usb/dwc3/dwc3-keystone.c
drivers/usb/gadget/Kconfig
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_midi2.c [new file with mode: 0644]
drivers/usb/gadget/function/u_midi2.h [new file with mode: 0644]
drivers/usb/gadget/udc/aspeed-vhub/core.c
drivers/usb/gadget/udc/aspeed_udc.c
drivers/usb/gadget/udc/atmel_usba_udc.c
drivers/usb/gadget/udc/fsl_qe_udc.c
drivers/usb/gadget/udc/fsl_udc_core.c
drivers/usb/gadget/udc/gr_udc.c
drivers/usb/gadget/udc/max3420_udc.c
drivers/usb/gadget/udc/pxa27x_udc.c
drivers/usb/gadget/udc/renesas_usb3.c
drivers/usb/gadget/udc/renesas_usbf.c
drivers/usb/gadget/udc/snps_udc_plat.c
drivers/usb/gadget/udc/tegra-xudc.c
drivers/usb/gadget/udc/udc-xilinx.c
drivers/usb/host/ehci-atmel.c
drivers/usb/host/ehci-exynos.c
drivers/usb/host/ehci-fsl.c
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-npcm7xx.c
drivers/usb/host/ehci-omap.c
drivers/usb/host/ehci-orion.c
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci-sh.c
drivers/usb/host/ehci-spear.c
drivers/usb/host/ehci-st.c
drivers/usb/host/fhci-hcd.c
drivers/usb/host/fsl-mph-dr-of.c
drivers/usb/host/isp1362-hcd.c
drivers/usb/host/ohci-at91.c
drivers/usb/host/ohci-da8xx.c
drivers/usb/host/ohci-exynos.c
drivers/usb/host/ohci-nxp.c
drivers/usb/host/ohci-platform.c
drivers/usb/host/ohci-ppc-of.c
drivers/usb/host/ohci-pxa27x.c
drivers/usb/host/ohci-spear.c
drivers/usb/host/ohci-st.c
drivers/usb/host/oxu210hp-hcd.c
drivers/usb/host/uhci-platform.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-rcar.c
drivers/usb/host/xhci-tegra.c
drivers/usb/misc/onboard_usb_hub.c
drivers/usb/misc/onboard_usb_hub.h
drivers/usb/misc/usb251xb.c
drivers/usb/mtu3/mtu3.h
drivers/usb/mtu3/mtu3_host.c
drivers/usb/musb/jz4740.c
drivers/usb/musb/mediatek.c
drivers/usb/musb/mpfs.c
drivers/usb/musb/musb_dsps.c
drivers/usb/musb/sunxi.c
drivers/usb/musb/tusb6010.c
drivers/usb/phy/phy-mxs-usb.c
drivers/usb/phy/phy-tegra-usb.c
drivers/usb/renesas_usbhs/common.c
drivers/usb/renesas_usbhs/rza.c
drivers/usb/renesas_usbhs/rza2.c
drivers/usb/typec/mux/intel_pmc_mux.c
drivers/usb/typec/mux/nb7vpq904m.c
drivers/usb/typec/tcpm/fusb302.c
drivers/usb/typec/tcpm/qcom/qcom_pmic_typec.c
drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_pdphy.c
drivers/usb/typec/tcpm/qcom/qcom_pmic_typec_port.c
drivers/usb/typec/ucsi/ucsi_glink.c
include/linux/usb/phy.h

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-midi2 b/Documentation/ABI/testing/configfs-usb-gadget-midi2
new file mode 100644 (file)
index 0000000..0eac3aa
--- /dev/null
@@ -0,0 +1,54 @@
+What:          /config/usb-gadget/gadget/functions/midi2.name
+Date:          Jul 2023
+KernelVersion: 6.6
+Description:
+               The attributes:
+
+               ============    ===============================================
+               process_ump     Flag to process UMP Stream messages (0 or 1)
+               static_block    Flag for static blocks (0 or 1)
+               iface_name      MIDI interface name string
+               ============    ===============================================
+
+What:          /config/usb-gadget/gadget/functions/midi2.name/ep.number
+Date:          Jul 2023
+KernelVersion: 6.6
+Description:
+               This group contains a UMP Endpoint configuration.
+               A new Endpoint starts from 0, and can be up to 3.
+
+               The attributes:
+
+               =============   ===============================================
+               protocol_caps   MIDI protocol capabilities (1, 2 or 3 for both)
+               protocol        Default MIDI protocol (1 or 2)
+               ep_name         UMP Endpoint name string
+               product_id      Product ID string
+               manufacturer    Manufacture ID (24 bit)
+               family          Device family ID (16 bit)
+               model           Device model ID (16 bit)
+               sw_revision     Software Revision (32 bit)
+               =============   ===============================================
+
+What:          /config/usb-gadget/gadget/functions/midi2.name/ep.number/block.number
+Date:          Jul 2023
+KernelVersion: 6.6
+Description:
+               This group contains a UMP Function Block configuration.
+               A new block starts from 0, and can be up to 31.
+
+               The attributes:
+
+               =================       ==============================================
+               name                    Function Block name string
+               direction               1: input, 2: output, 3: bidirectional
+               first_group             The first UMP Group number (0-15)
+               num_groups              The number of groups in this FB (1-16)
+               midi1_first_group       The first UMP Group number for MIDI 1.0 (0-15)
+               midi1_num_groups        The number of groups for MIDI 1.0 (0-16)
+               ui_hint                 0: unknown, 1: receiver, 2: sender, 3: both
+               midi_ci_verison         Supported MIDI-CI version number (8 bit)
+               is_midi1                Legacy MIDI 1.0 device (0, 1 or 2)
+               sysex8_streams          Max number of SysEx8 streams (8 bit)
+               active                  Active FB flag (0 or 1)
+               =================       ==============================================
diff --git a/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb2phy.yaml
new file mode 100644 (file)
index 0000000..9911ada
--- /dev/null
@@ -0,0 +1,175 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/realtek,usb2phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC SoCs USB 2.0 PHY
+
+maintainers:
+  - Stanley Chang <stanley_chang@realtek.com>
+
+description: |
+  Realtek USB 2.0 PHY support the digital home center (DHC) RTD series SoCs.
+  The USB 2.0 PHY driver is designed to support the XHCI controller. The SoCs
+  support multiple XHCI controllers. One PHY device node maps to one XHCI
+  controller.
+
+  RTD1295/RTD1619 SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on some
+  controllers.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1395 SoCs USB
+  The USB architecture includes two XHCI controllers.
+  The controller#0 has one USB 2.0 PHY. The controller#1 includes two USB 2.0
+  PHY.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+                               |- phy#1
+
+  RTD1319/RTD1619b SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#2.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1319d SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each xhci maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#0.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+
+  RTD1312c/RTD1315e SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+
+properties:
+  compatible:
+    enum:
+      - realtek,rtd1295-usb2phy
+      - realtek,rtd1312c-usb2phy
+      - realtek,rtd1315e-usb2phy
+      - realtek,rtd1319-usb2phy
+      - realtek,rtd1319d-usb2phy
+      - realtek,rtd1395-usb2phy
+      - realtek,rtd1395-usb2phy-2port
+      - realtek,rtd1619-usb2phy
+      - realtek,rtd1619b-usb2phy
+
+  reg:
+    items:
+      - description: PHY data registers
+      - description: PHY control registers
+
+  "#phy-cells":
+    const: 0
+
+  nvmem-cells:
+    maxItems: 2
+    description:
+      Phandles to nvmem cell that contains the trimming data.
+      If unspecified, default value is used.
+
+  nvmem-cell-names:
+    items:
+      - const: usb-dc-cal
+      - const: usb-dc-dis
+    description:
+      The following names, which correspond to each nvmem-cells.
+      usb-dc-cal is the driving level for each phy specified via efuse.
+      usb-dc-dis is the disconnection level for each phy specified via efuse.
+
+  realtek,inverse-hstx-sync-clock:
+    description:
+      For one of the phys of RTD1619b SoC, the synchronous clock of the
+      high-speed tx must be inverted.
+    type: boolean
+
+  realtek,driving-level:
+    description:
+      Control the magnitude of High speed Dp/Dm output swing (mV).
+      For a different board or port, the original magnitude maybe not meet
+      the specification. In this situation we can adjust the value to meet
+      the specification.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 8
+    minimum: 0
+    maximum: 31
+
+  realtek,driving-level-compensate:
+    description:
+      For RTD1315e SoC, the driving level can be adjusted by reading the
+      efuse table. This property provides drive compensation.
+      If the magnitude of High speed Dp/Dm output swing still not meet the
+      specification, then we can set this value to meet the specification.
+    $ref: /schemas/types.yaml#/definitions/int32
+    default: 0
+    minimum: -8
+    maximum: 8
+
+  realtek,disconnection-compensate:
+    description:
+      This adjusts the disconnection level compensation for the different
+      boards with different disconnection level.
+    $ref: /schemas/types.yaml#/definitions/int32
+    default: 0
+    minimum: -8
+    maximum: 8
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+
+allOf:
+  - if:
+      not:
+        properties:
+          compatible:
+            contains:
+              enum:
+                - realtek,rtd1619b-usb2phy
+    then:
+      properties:
+        realtek,inverse-hstx-sync-clock: false
+
+  - if:
+      not:
+        properties:
+          compatible:
+            contains:
+              enum:
+                - realtek,rtd1315e-usb2phy
+    then:
+      properties:
+        realtek,driving-level-compensate: false
+
+additionalProperties: false
+
+examples:
+  - |
+    usb-phy@13214 {
+        compatible = "realtek,rtd1619b-usb2phy";
+        reg = <0x13214 0x4>, <0x28280 0x4>;
+        #phy-cells = <0>;
+        nvmem-cells = <&otp_usb_port0_dc_cal>, <&otp_usb_port0_dc_dis>;
+        nvmem-cell-names = "usb-dc-cal", "usb-dc-dis";
+
+        realtek,inverse-hstx-sync-clock;
+        realtek,driving-level = <0xa>;
+        realtek,disconnection-compensate = <(-1)>;
+    };
diff --git a/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml b/Documentation/devicetree/bindings/phy/realtek,usb3phy.yaml
new file mode 100644 (file)
index 0000000..dfe2bb4
--- /dev/null
@@ -0,0 +1,107 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright 2023 Realtek Semiconductor Corporation
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/realtek,usb3phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Realtek DHC SoCs USB 3.0 PHY
+
+maintainers:
+  - Stanley Chang <stanley_chang@realtek.com>
+
+description: |
+  Realtek USB 3.0 PHY support the digital home center (DHC) RTD series SoCs.
+  The USB 3.0 PHY driver is designed to support the XHCI controller. The SoCs
+  support multiple XHCI controllers. One PHY device node maps to one XHCI
+  controller.
+
+  RTD1295/RTD1619 SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on some
+  controllers.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1319/RTD1619b SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each XHCI maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#2.
+  XHCI controller#0 -- usb2phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+
+  RTD1319d SoCs USB
+  The USB architecture includes three XHCI controllers.
+  Each xhci maps to one USB 2.0 PHY and map one USB 3.0 PHY on controllers#0.
+  XHCI controller#0 -- usb2phy -- phy#0
+                    |- usb3phy -- phy#0
+  XHCI controller#1 -- usb2phy -- phy#0
+  XHCI controller#2 -- usb2phy -- phy#0
+
+properties:
+  compatible:
+    enum:
+      - realtek,rtd1295-usb3phy
+      - realtek,rtd1319-usb3phy
+      - realtek,rtd1319d-usb3phy
+      - realtek,rtd1619-usb3phy
+      - realtek,rtd1619b-usb3phy
+
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+  nvmem-cells:
+    maxItems: 1
+    description: A phandle to the tx lfps swing trim data provided by
+      a nvmem device, if unspecified, default values shall be used.
+
+  nvmem-cell-names:
+    items:
+      - const: usb_u3_tx_lfps_swing_trim
+
+  realtek,amplitude-control-coarse-tuning:
+    description:
+      This adjusts the signal amplitude for normal operation and beacon LFPS.
+      This value is a parameter for coarse tuning.
+      For different boards, if the default value is inappropriate, this
+      property can be assigned to adjust.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 255
+    minimum: 0
+    maximum: 255
+
+  realtek,amplitude-control-fine-tuning:
+    description:
+      This adjusts the signal amplitude for normal operation and beacon LFPS.
+      This value is used for fine-tuning parameters.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    default: 65535
+    minimum: 0
+    maximum: 65535
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    usb-phy@13e10 {
+        compatible = "realtek,rtd1319d-usb3phy";
+        reg = <0x13e10 0x4>;
+        #phy-cells = <0>;
+
+        nvmem-cells = <&otp_usb_u3_tx_lfps_swing_trim>;
+        nvmem-cell-names = "usb_u3_tx_lfps_swing_trim";
+
+        realtek,amplitude-control-coarse-tuning = <0x77>;
+    };
index 7824028..532d646 100644 (file)
@@ -292,6 +292,18 @@ properties:
     minimum: 0x0
     maximum: 0xf
 
+  fsl,picophy-rise-fall-time-adjust:
+    description:
+      HS Transmitter Rise/Fall Time Adjustment. Adjust the rise/fall times
+      of the high-speed transmitter waveform. It has no unit. The rise/fall
+      time will be increased or decreased by a certain percentage relative
+      to design default time. (0:-10%; 1:design default; 2:+15%; 3:+20%)
+      Details can refer to TXRISETUNE0 bit of USBNC_n_PHY_CFG1.
+    $ref: /schemas/types.yaml#/definitions/uint32
+    minimum: 0
+    maximum: 3
+    default: 1
+
   usb-phy:
     description: phandle for the PHY device. Use "phys" instead.
     $ref: /schemas/types.yaml#/definitions/phandle
diff --git a/Documentation/devicetree/bindings/usb/cypress,hx3.yaml b/Documentation/devicetree/bindings/usb/cypress,hx3.yaml
new file mode 100644 (file)
index 0000000..47add0d
--- /dev/null
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/cypress,hx3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Cypress HX3 USB 3.0 hub controller family
+
+maintainers:
+  - Benjamin Bara <benjamin.bara@skidata.com>
+
+allOf:
+  - $ref: usb-device.yaml#
+
+properties:
+  compatible:
+    enum:
+      - usb4b4,6504
+      - usb4b4,6506
+
+  reg: true
+
+  reset-gpios:
+    items:
+      - description: GPIO specifier for RESETN pin.
+
+  vdd-supply:
+    description:
+      1V2 power supply (VDD_EFUSE, AVDD12, DVDD12).
+
+  vdd2-supply:
+    description:
+      3V3 power supply (AVDD33, VDD_IO).
+
+  peer-hub:
+    $ref: /schemas/types.yaml#/definitions/phandle
+    description:
+      phandle to the peer hub on the controller.
+
+required:
+  - compatible
+  - reg
+  - peer-hub
+  - vdd-supply
+  - vdd2-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/gpio/gpio.h>
+
+    usb {
+        dr_mode = "host";
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        /* 2.0 hub on port 1 */
+        hub_2_0: hub@1 {
+          compatible = "usb4b4,6504";
+          reg = <1>;
+          peer-hub = <&hub_3_0>;
+          reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
+          vdd-supply = <&reg_1v2_usb>;
+          vdd2-supply = <&reg_3v3_usb>;
+        };
+
+        /* 3.0 hub on port 2 */
+        hub_3_0: hub@2 {
+          compatible = "usb4b4,6506";
+          reg = <2>;
+          peer-hub = <&hub_2_0>;
+          reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>;
+          vdd-supply = <&reg_1v2_usb>;
+          vdd2-supply = <&reg_3v3_usb>;
+        };
+    };
index b956bb5..e5c8f4e 100644 (file)
@@ -67,6 +67,7 @@ properties:
           - const: generic-ehci
       - items:
           - enum:
+              - atmel,at91sam9g45-ehci
               - cavium,octeon-6335-ehci
               - ibm,usb-ehci-440epx
               - ibm,usb-ehci-460ex
index cc4cf92..383625c 100644 (file)
@@ -17,6 +17,7 @@ properties:
     enum:
       - usb5e3,608
       - usb5e3,610
+      - usb5e3,620
 
   reg: true
 
index ae24dac..6759105 100644 (file)
@@ -14,6 +14,7 @@ properties:
     items:
       - enum:
           - qcom,ipq4019-dwc3
+          - qcom,ipq5332-dwc3
           - qcom,ipq6018-dwc3
           - qcom,ipq8064-dwc3
           - qcom,ipq8074-dwc3
@@ -82,15 +83,6 @@ properties:
     minItems: 1
     maxItems: 9
 
-  assigned-clocks:
-    items:
-      - description: Phandle and clock specifier of MOCK_UTMI_CLK.
-      - description: Phandle and clock specifoer of MASTER_CLK.
-
-  assigned-clock-rates:
-    items:
-      - description: Must be 19.2MHz (19200000).
-      - description: Must be >= 60 MHz in HS mode, >= 125 MHz in SS mode.
   resets:
     maxItems: 1
 
@@ -246,6 +238,7 @@ allOf:
         compatible:
           contains:
             enum:
+              - qcom,ipq5332-dwc3
               - qcom,msm8994-dwc3
               - qcom,qcs404-dwc3
     then:
@@ -290,15 +283,23 @@ allOf:
     then:
       properties:
         clocks:
-          minItems: 6
+          minItems: 5
+          maxItems: 6
         clock-names:
-          items:
-            - const: cfg_noc
-            - const: core
-            - const: iface
-            - const: sleep
-            - const: mock_utmi
-            - const: bus
+          oneOf:
+            - items:
+                - const: cfg_noc
+                - const: core
+                - const: iface
+                - const: sleep
+                - const: mock_utmi
+                - const: bus
+            - items:
+                - const: cfg_noc
+                - const: core
+                - const: sleep
+                - const: mock_utmi
+                - const: bus
 
   - if:
       properties:
@@ -410,6 +411,7 @@ allOf:
         compatible:
           contains:
             enum:
+              - qcom,ipq5332-dwc3
               - qcom,sdm660-dwc3
     then:
       properties:
index 2c94ff2..0fcd75e 100644 (file)
@@ -420,6 +420,12 @@ USBDEVFS_CONNECTINFO
     know the devnum value already, it's the DDD value of the device file
     name.
 
+USBDEVFS_GET_SPEED
+    Returns the speed of the device. The speed is returned as a
+    nummerical value in accordance with enum usb_device_speed
+
+    File modification time is not updated by this request.
+
 USBDEVFS_GETDRIVER
     Returns the name of the kernel driver bound to a given interface (a
     string). Parameter is a pointer to this structure, which is
index 2fca404..394cd22 100644 (file)
@@ -27,6 +27,7 @@ provided by gadgets.
    18. UVC function
    19. PRINTER function
    20. UAC1 function (new API)
+   21. MIDI2 function
 
 
 1. ACM function
@@ -965,3 +966,156 @@ e.g.::
 
        $ arecord -f dat -t wav -D hw:CARD=UAC1Gadget,DEV=0 | \
          aplay -D default:CARD=OdroidU3
+
+
+21. MIDI2 function
+==================
+
+The function is provided by usb_f_midi2.ko module.
+It will create a virtual ALSA card containing a UMP rawmidi device
+where the UMP packet is looped back. In addition, a legacy rawmidi
+device is created. The UMP rawmidi is bound with ALSA sequencer
+clients, too.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "midi2".
+The midi2 function provides these attributes in its function directory
+as the card top-level information:
+
+       =============   =================================================
+       process_ump     Bool flag to process UMP Stream messages (0 or 1)
+       static_block    Bool flag for static blocks (0 or 1)
+       iface_name      Optional interface name string
+       =============   =================================================
+
+The directory contains a subdirectory "ep.0", and this provides the
+attributes for a UMP Endpoint (which is a pair of USB MIDI Endpoints):
+
+       =============   =================================================
+       protocol_caps   MIDI protocol capabilities;
+                       1: MIDI 1.0, 2: MIDI 2.0, or 3: both protocols
+       protocol        Default MIDI protocol (either 1 or 2)
+       ep_name         UMP Endpoint name string
+       product_id      Product ID string
+       manufacturer    Manufacture ID number (24 bit)
+       family          Device family ID number (16 bit)
+       model           Device model ID number (16 bit)
+       sw_revision     Software revision (32 bit)
+       =============   =================================================
+
+Each Endpoint subdirectory contains a subdirectory "block.0", which
+represents the Function Block for Block 0 information.
+Its attributes are:
+
+       =================       ===============================================
+       name                    Function Block name string
+       direction               Direction of this FB
+                               1: input, 2: output, or 3: bidirectional
+       first_group             The first UMP Group number (0-15)
+       num_groups              The number of groups in this FB (1-16)
+       midi1_first_group       The first UMP Group number for MIDI 1.0 (0-15)
+       midi1_num_groups        The number of groups for MIDI 1.0 (0-16)
+       ui_hint                 UI-hint of this FB
+                               0: unknown, 1: receiver, 2: sender, 3: both
+       midi_ci_verison         Supported MIDI-CI version number (8 bit)
+       is_midi1                Legacy MIDI 1.0 device (0-2)
+                               0: MIDI 2.0 device,
+                               1: MIDI 1.0 without restriction, or
+                               2: MIDI 1.0 with low speed
+       sysex8_streams          Max number of SysEx8 streams (8 bit)
+       active                  Bool flag for FB activity (0 or 1)
+       =================       ===============================================
+
+If multiple Function Blocks are required, you can add more Function
+Blocks by creating subdirectories "block.<num>" with the corresponding
+Function Block number (1, 2, ....). The FB subdirectories can be
+dynamically removed, too. Note that the Function Block numbers must be
+continuous.
+
+Similarly, if you multiple UMP Endpoints are required, you can add
+more Endpoints by creating subdirectories "ep.<num>". The number must
+be continuous.
+
+For emulating the old MIDI 2.0 device without UMP v1.1 support, pass 0
+to `process_ump` flag. Then the whole UMP v1.1 requests are ignored.
+
+Testing the MIDI2 function
+--------------------------
+
+On the device: run the gadget, and running::
+
+  $ cat /proc/asound/cards
+
+will show a new sound card containing a MIDI2 device.
+
+OTOH, on the host::
+
+  $ cat /proc/asound/cards
+
+will show a new sound card containing either MIDI1 or MIDI2 device,
+depending on the USB audio driver configuration.
+
+On both, when ALSA sequencer is enabled on the host, you can find the
+UMP MIDI client such as "MIDI 2.0 Gadget".
+
+As the driver simply loops back the data, there is no need for a real
+device just for testing.
+
+For testing a MIDI input from the gadget to the host (e.g. emulating a
+MIDI keyboard), you can send a MIDI stream like the following.
+
+On the gadget::
+
+  $ aconnect -o
+  ....
+  client 20: 'MIDI 2.0 Gadget' [type=kernel,card=1]
+      0 'MIDI 2.0        '
+      1 'Group 1 (MIDI 2.0 Gadget I/O)'
+  $ aplaymidi -p 20:1 to_host.mid
+
+On the host::
+
+  $ aconnect -i
+  ....
+  client 24: 'MIDI 2.0 Gadget' [type=kernel,card=2]
+      0 'MIDI 2.0        '
+      1 'Group 1 (MIDI 2.0 Gadget I/O)'
+  $ arecordmidi -p 24:1 from_gadget.mid
+
+If you have a UMP-capable application, you can use the UMP port to
+send/receive the raw UMP packets, too. For example, aseqdump program
+with UMP support can receive from UMP port. On the host::
+
+  $ aseqdump -u 2 -p 24:1
+  Waiting for data. Press Ctrl+C to end.
+  Source  Group    Event                  Ch  Data
+   24:1   Group  0, Program change          0, program 0, Bank select 0:0
+   24:1   Group  0, Channel pressure        0, value 0x80000000
+
+For testing a MIDI output to the gadget to the host (e.g. emulating a
+MIDI synth), it'll be just other way round.
+
+On the gadget::
+
+  $ arecordmidi -p 20:1 from_host.mid
+
+On the host::
+
+  $ aplaymidi -p 24:1 to_gadget.mid
+
+The access to MIDI 1.0 on altset 0 on the host is supported, and it's
+translated from/to UMP packets on the gadget. It's bound to only
+Function Block 0.
+
+The current operation mode can be observed in ALSA control element
+"Operation Mode" for SND_CTL_IFACE_RAWMIDI.  For example::
+
+  $ amixer -c1 contents
+  numid=1,iface=RAWMIDI,name='Operation Mode'
+    ; type=INTEGER,access=r--v----,values=1,min=0,max=2,step=0
+    : values=2
+
+where 0 = unused, 1 = MIDI 1.0 (altset 0), 2 = MIDI 2.0 (altset 1).
+The example above shows it's running in 2, i.e. MIDI 2.0.
index 8dba959..aac670b 100644 (file)
@@ -87,6 +87,7 @@ source "drivers/phy/motorola/Kconfig"
 source "drivers/phy/mscc/Kconfig"
 source "drivers/phy/qualcomm/Kconfig"
 source "drivers/phy/ralink/Kconfig"
+source "drivers/phy/realtek/Kconfig"
 source "drivers/phy/renesas/Kconfig"
 source "drivers/phy/rockchip/Kconfig"
 source "drivers/phy/samsung/Kconfig"
index 54f312c..ba7c100 100644 (file)
@@ -26,6 +26,7 @@ obj-y                                 += allwinner/   \
                                           mscc/        \
                                           qualcomm/    \
                                           ralink/      \
+                                          realtek/     \
                                           renesas/     \
                                           rockchip/    \
                                           samsung/     \
diff --git a/drivers/phy/realtek/Kconfig b/drivers/phy/realtek/Kconfig
new file mode 100644 (file)
index 0000000..650e20e
--- /dev/null
@@ -0,0 +1,27 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Phy drivers for Realtek platforms
+#
+config PHY_RTK_RTD_USB2PHY
+       tristate "Realtek RTD USB2 PHY Transceiver Driver"
+       depends on USB_SUPPORT
+       select GENERIC_PHY
+       select USB_PHY
+       select USB_COMMON
+       help
+         Enable this to support Realtek SoC USB2 phy transceiver.
+         The DHC (digital home center) RTD series SoCs used the Synopsys
+         DWC3 USB IP. This driver will do the PHY initialization
+         of the parameters.
+
+config PHY_RTK_RTD_USB3PHY
+       tristate "Realtek RTD USB3 PHY Transceiver Driver"
+       depends on USB_SUPPORT
+       select GENERIC_PHY
+       select USB_PHY
+       select USB_COMMON
+       help
+         Enable this to support Realtek SoC USB3 phy transceiver.
+         The DHC (digital home center) RTD series SoCs used the Synopsys
+         DWC3 USB IP. This driver will do the PHY initialization
+         of the parameters.
diff --git a/drivers/phy/realtek/Makefile b/drivers/phy/realtek/Makefile
new file mode 100644 (file)
index 0000000..ed7b47f
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_RTK_RTD_USB2PHY)      += phy-rtk-usb2.o
+obj-$(CONFIG_PHY_RTK_RTD_USB3PHY)      += phy-rtk-usb3.o
diff --git a/drivers/phy/realtek/phy-rtk-usb2.c b/drivers/phy/realtek/phy-rtk-usb2.c
new file mode 100644 (file)
index 0000000..ed47a1c
--- /dev/null
@@ -0,0 +1,1329 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  phy-rtk-usb2.c RTK usb2.0 PHY driver
+ *
+ * Copyright (C) 2023 Realtek Semiconductor Corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/usb.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/hcd.h>
+
+/* GUSB2PHYACCn register */
+#define PHY_NEW_REG_REQ BIT(25)
+#define PHY_VSTS_BUSY   BIT(23)
+#define PHY_VCTRL_SHIFT 8
+#define PHY_REG_DATA_MASK 0xff
+
+#define GET_LOW_NIBBLE(addr) ((addr) & 0x0f)
+#define GET_HIGH_NIBBLE(addr) (((addr) & 0xf0) >> 4)
+
+#define EFUS_USB_DC_CAL_RATE 2
+#define EFUS_USB_DC_CAL_MAX 7
+
+#define EFUS_USB_DC_DIS_RATE 1
+#define EFUS_USB_DC_DIS_MAX 7
+
+#define MAX_PHY_DATA_SIZE 20
+#define OFFEST_PHY_READ 0x20
+
+#define MAX_USB_PHY_NUM 4
+#define MAX_USB_PHY_PAGE0_DATA_SIZE 16
+#define MAX_USB_PHY_PAGE1_DATA_SIZE 16
+#define MAX_USB_PHY_PAGE2_DATA_SIZE 8
+
+#define SET_PAGE_OFFSET 0xf4
+#define SET_PAGE_0 0x9b
+#define SET_PAGE_1 0xbb
+#define SET_PAGE_2 0xdb
+
+#define PAGE_START 0xe0
+#define PAGE0_0XE4 0xe4
+#define PAGE0_0XE6 0xe6
+#define PAGE0_0XE7 0xe7
+#define PAGE1_0XE0 0xe0
+#define PAGE1_0XE2 0xe2
+
+#define SENSITIVITY_CTRL (BIT(4) | BIT(5) | BIT(6))
+#define ENABLE_AUTO_SENSITIVITY_CALIBRATION BIT(2)
+#define DEFAULT_DC_DRIVING_VALUE (0x8)
+#define DEFAULT_DC_DISCONNECTION_VALUE (0x6)
+#define HS_CLK_SELECT BIT(6)
+
+struct phy_reg {
+       void __iomem *reg_wrap_vstatus;
+       void __iomem *reg_gusb2phyacc0;
+       int vstatus_index;
+};
+
+struct phy_data {
+       u8 addr;
+       u8 data;
+};
+
+struct phy_cfg {
+       int page0_size;
+       struct phy_data page0[MAX_USB_PHY_PAGE0_DATA_SIZE];
+       int page1_size;
+       struct phy_data page1[MAX_USB_PHY_PAGE1_DATA_SIZE];
+       int page2_size;
+       struct phy_data page2[MAX_USB_PHY_PAGE2_DATA_SIZE];
+
+       int num_phy;
+
+       bool check_efuse;
+       int check_efuse_version;
+#define CHECK_EFUSE_V1 1
+#define CHECK_EFUSE_V2 2
+       int efuse_dc_driving_rate;
+       int efuse_dc_disconnect_rate;
+       int dc_driving_mask;
+       int dc_disconnect_mask;
+       bool usb_dc_disconnect_at_page0;
+       int driving_updated_for_dev_dis;
+
+       bool do_toggle;
+       bool do_toggle_driving;
+       bool use_default_parameter;
+       bool is_double_sensitivity_mode;
+};
+
+struct phy_parameter {
+       struct phy_reg phy_reg;
+
+       /* Get from efuse */
+       s8 efuse_usb_dc_cal;
+       s8 efuse_usb_dc_dis;
+
+       /* Get from dts */
+       bool inverse_hstx_sync_clock;
+       u32 driving_level;
+       s32 driving_level_compensate;
+       s32 disconnection_compensate;
+};
+
+struct rtk_phy {
+       struct usb_phy phy;
+       struct device *dev;
+
+       struct phy_cfg *phy_cfg;
+       int num_phy;
+       struct phy_parameter *phy_parameter;
+
+       struct dentry *debug_dir;
+};
+
+/* mapping 0xE0 to 0 ... 0xE7 to 7, 0xF0 to 8 ,,, 0xF7 to 15 */
+static inline int page_addr_to_array_index(u8 addr)
+{
+       return (int)((((addr) - PAGE_START) & 0x7) +
+               ((((addr) - PAGE_START) & 0x10) >> 1));
+}
+
+static inline u8 array_index_to_page_addr(int index)
+{
+       return ((((index) + PAGE_START) & 0x7) +
+               ((((index) & 0x8) << 1) + PAGE_START));
+}
+
+#define PHY_IO_TIMEOUT_USEC            (50000)
+#define PHY_IO_DELAY_US                        (100)
+
+static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+       int ret;
+       unsigned int val;
+
+       ret = read_poll_timeout(readl, val, ((val & mask) == result),
+                               PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg);
+       if (ret) {
+               pr_err("%s can't program USB phy\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static char rtk_phy_read(struct phy_reg *phy_reg, char addr)
+{
+       void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0;
+       unsigned int val;
+       int ret = 0;
+
+       addr -= OFFEST_PHY_READ;
+
+       /* polling until VBusy == 0 */
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return (char)ret;
+
+       /* VCtrl = low nibble of addr, and set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return (char)ret;
+
+       /* VCtrl = high nibble of addr, and set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return (char)ret;
+
+       val = readl(reg_gusb2phyacc0);
+
+       return (char)(val & PHY_REG_DATA_MASK);
+}
+
+static int rtk_phy_write(struct phy_reg *phy_reg, char addr, char data)
+{
+       unsigned int val;
+       void __iomem *reg_wrap_vstatus = phy_reg->reg_wrap_vstatus;
+       void __iomem *reg_gusb2phyacc0 = phy_reg->reg_gusb2phyacc0;
+       int shift_bits = phy_reg->vstatus_index * 8;
+       int ret = 0;
+
+       /* write data to VStatusOut2 (data output to phy) */
+       writel((u32)data << shift_bits, reg_wrap_vstatus);
+
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return ret;
+
+       /* VCtrl = low nibble of addr, set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_LOW_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return ret;
+
+       /* VCtrl = high nibble of addr, set PHY_NEW_REG_REQ */
+       val = PHY_NEW_REG_REQ | (GET_HIGH_NIBBLE(addr) << PHY_VCTRL_SHIFT);
+
+       writel(val, reg_gusb2phyacc0);
+       ret = utmi_wait_register(reg_gusb2phyacc0, PHY_VSTS_BUSY, 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rtk_phy_set_page(struct phy_reg *phy_reg, int page)
+{
+       switch (page) {
+       case 0:
+               return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_0);
+       case 1:
+               return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_1);
+       case 2:
+               return rtk_phy_write(phy_reg, SET_PAGE_OFFSET, SET_PAGE_2);
+       default:
+               pr_err("%s error page=%d\n", __func__, page);
+       }
+
+       return -EINVAL;
+}
+
+static u8 __updated_dc_disconnect_level_page0_0xe4(struct phy_cfg *phy_cfg,
+                                                  struct phy_parameter *phy_parameter, u8 data)
+{
+       u8 ret;
+       s32 val;
+       s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+       int offset = 4;
+
+       val = (s32)((data >> offset) & dc_disconnect_mask)
+                    + phy_parameter->efuse_usb_dc_dis
+                    + phy_parameter->disconnection_compensate;
+
+       if (val > dc_disconnect_mask)
+               val = dc_disconnect_mask;
+       else if (val < 0)
+               val = 0;
+
+       ret = (data & (~(dc_disconnect_mask << offset))) |
+                   (val & dc_disconnect_mask) << offset;
+
+       return ret;
+}
+
+/* updated disconnect level at page0 */
+static void update_dc_disconnect_level_at_page0(struct rtk_phy *rtk_phy,
+                                               struct phy_parameter *phy_parameter, bool update)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_data *phy_data_page;
+       struct phy_data *phy_data;
+       u8 addr, data;
+       int offset = 4;
+       s32 dc_disconnect_mask;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_reg = &phy_parameter->phy_reg;
+
+       /* Set page 0 */
+       phy_data_page = phy_cfg->page0;
+       rtk_phy_set_page(phy_reg, 0);
+
+       i = page_addr_to_array_index(PAGE0_0XE4);
+       phy_data = phy_data_page + i;
+       if (!phy_data->addr) {
+               phy_data->addr = PAGE0_0XE4;
+               phy_data->data = rtk_phy_read(phy_reg, PAGE0_0XE4);
+       }
+
+       addr = phy_data->addr;
+       data = phy_data->data;
+       dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+
+       if (update)
+               data = __updated_dc_disconnect_level_page0_0xe4(phy_cfg, phy_parameter, data);
+       else
+               data = (data & ~(dc_disconnect_mask << offset)) |
+                       (DEFAULT_DC_DISCONNECTION_VALUE << offset);
+
+       if (rtk_phy_write(phy_reg, addr, data))
+               dev_err(rtk_phy->dev,
+                       "%s: Error to set page1 parameter addr=0x%x value=0x%x\n",
+                       __func__, addr, data);
+}
+
+static u8 __updated_dc_disconnect_level_page1_0xe2(struct phy_cfg *phy_cfg,
+                                                  struct phy_parameter *phy_parameter, u8 data)
+{
+       u8 ret;
+       s32 val;
+       s32 dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               val = (s32)(data & dc_disconnect_mask)
+                           + phy_parameter->efuse_usb_dc_dis
+                           + phy_parameter->disconnection_compensate;
+       } else { /* for CHECK_EFUSE_V2 or no efuse */
+               if (phy_parameter->efuse_usb_dc_dis)
+                       val = (s32)(phy_parameter->efuse_usb_dc_dis +
+                                   phy_parameter->disconnection_compensate);
+               else
+                       val = (s32)((data & dc_disconnect_mask) +
+                                   phy_parameter->disconnection_compensate);
+       }
+
+       if (val > dc_disconnect_mask)
+               val = dc_disconnect_mask;
+       else if (val < 0)
+               val = 0;
+
+       ret = (data & (~dc_disconnect_mask)) | (val & dc_disconnect_mask);
+
+       return ret;
+}
+
+/* updated disconnect level at page1 */
+static void update_dc_disconnect_level_at_page1(struct rtk_phy *rtk_phy,
+                                               struct phy_parameter *phy_parameter, bool update)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_data *phy_data_page;
+       struct phy_data *phy_data;
+       struct phy_reg *phy_reg;
+       u8 addr, data;
+       s32 dc_disconnect_mask;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_reg = &phy_parameter->phy_reg;
+
+       /* Set page 1 */
+       phy_data_page = phy_cfg->page1;
+       rtk_phy_set_page(phy_reg, 1);
+
+       i = page_addr_to_array_index(PAGE1_0XE2);
+       phy_data = phy_data_page + i;
+       if (!phy_data->addr) {
+               phy_data->addr = PAGE1_0XE2;
+               phy_data->data = rtk_phy_read(phy_reg, PAGE1_0XE2);
+       }
+
+       addr = phy_data->addr;
+       data = phy_data->data;
+       dc_disconnect_mask = phy_cfg->dc_disconnect_mask;
+
+       if (update)
+               data = __updated_dc_disconnect_level_page1_0xe2(phy_cfg, phy_parameter, data);
+       else
+               data = (data & ~dc_disconnect_mask) | DEFAULT_DC_DISCONNECTION_VALUE;
+
+       if (rtk_phy_write(phy_reg, addr, data))
+               dev_err(rtk_phy->dev,
+                       "%s: Error to set page1 parameter addr=0x%x value=0x%x\n",
+                       __func__, addr, data);
+}
+
+static void update_dc_disconnect_level(struct rtk_phy *rtk_phy,
+                                      struct phy_parameter *phy_parameter, bool update)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+
+       if (phy_cfg->usb_dc_disconnect_at_page0)
+               update_dc_disconnect_level_at_page0(rtk_phy, phy_parameter, update);
+       else
+               update_dc_disconnect_level_at_page1(rtk_phy, phy_parameter, update);
+}
+
+static u8 __update_dc_driving_page0_0xe4(struct phy_cfg *phy_cfg,
+                                        struct phy_parameter *phy_parameter, u8 data)
+{
+       s32 driving_level_compensate = phy_parameter->driving_level_compensate;
+       s32 dc_driving_mask = phy_cfg->dc_driving_mask;
+       s32 val;
+       u8 ret;
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               val = (s32)(data & dc_driving_mask) + driving_level_compensate
+                           + phy_parameter->efuse_usb_dc_cal;
+       } else { /* for CHECK_EFUSE_V2 or no efuse */
+               if (phy_parameter->efuse_usb_dc_cal)
+                       val = (s32)((phy_parameter->efuse_usb_dc_cal & dc_driving_mask)
+                                   + driving_level_compensate);
+               else
+                       val = (s32)(data & dc_driving_mask);
+       }
+
+       if (val > dc_driving_mask)
+               val = dc_driving_mask;
+       else if (val < 0)
+               val = 0;
+
+       ret = (data & (~dc_driving_mask)) | (val & dc_driving_mask);
+
+       return ret;
+}
+
+static void update_dc_driving_level(struct rtk_phy *rtk_phy,
+                                   struct phy_parameter *phy_parameter)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+
+       phy_reg = &phy_parameter->phy_reg;
+       phy_cfg = rtk_phy->phy_cfg;
+       if (!phy_cfg->page0[4].addr) {
+               rtk_phy_set_page(phy_reg, 0);
+               phy_cfg->page0[4].addr = PAGE0_0XE4;
+               phy_cfg->page0[4].data = rtk_phy_read(phy_reg, PAGE0_0XE4);
+       }
+
+       if (phy_parameter->driving_level != DEFAULT_DC_DRIVING_VALUE) {
+               u32 dc_driving_mask;
+               u8 driving_level;
+               u8 data;
+
+               data = phy_cfg->page0[4].data;
+               dc_driving_mask = phy_cfg->dc_driving_mask;
+               driving_level = data & dc_driving_mask;
+
+               dev_dbg(rtk_phy->dev, "%s driving_level=%d => dts driving_level=%d\n",
+                       __func__, driving_level, phy_parameter->driving_level);
+
+               phy_cfg->page0[4].data = (data & (~dc_driving_mask)) |
+                           (phy_parameter->driving_level & dc_driving_mask);
+       }
+
+       phy_cfg->page0[4].data = __update_dc_driving_page0_0xe4(phy_cfg,
+                                                               phy_parameter,
+                                                               phy_cfg->page0[4].data);
+}
+
+static void update_hs_clk_select(struct rtk_phy *rtk_phy,
+                                struct phy_parameter *phy_parameter)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (phy_parameter->inverse_hstx_sync_clock) {
+               if (!phy_cfg->page0[6].addr) {
+                       rtk_phy_set_page(phy_reg, 0);
+                       phy_cfg->page0[6].addr = PAGE0_0XE6;
+                       phy_cfg->page0[6].data = rtk_phy_read(phy_reg, PAGE0_0XE6);
+               }
+
+               phy_cfg->page0[6].data = phy_cfg->page0[6].data | HS_CLK_SELECT;
+       }
+}
+
+static void do_rtk_phy_toggle(struct rtk_phy *rtk_phy,
+                             int index, bool connect)
+{
+       struct phy_parameter *phy_parameter;
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_data *phy_data_page;
+       u8 addr, data;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (!phy_cfg->do_toggle)
+               goto out;
+
+       if (phy_cfg->is_double_sensitivity_mode)
+               goto do_toggle_driving;
+
+       /* Set page 0 */
+       rtk_phy_set_page(phy_reg, 0);
+
+       addr = PAGE0_0XE7;
+       data = rtk_phy_read(phy_reg, addr);
+
+       if (connect)
+               rtk_phy_write(phy_reg, addr, data & (~SENSITIVITY_CTRL));
+       else
+               rtk_phy_write(phy_reg, addr, data | (SENSITIVITY_CTRL));
+
+do_toggle_driving:
+
+       if (!phy_cfg->do_toggle_driving)
+               goto do_toggle;
+
+       /* Page 0 addr 0xE4 driving capability */
+
+       /* Set page 0 */
+       phy_data_page = phy_cfg->page0;
+       rtk_phy_set_page(phy_reg, 0);
+
+       i = page_addr_to_array_index(PAGE0_0XE4);
+       addr = phy_data_page[i].addr;
+       data = phy_data_page[i].data;
+
+       if (connect) {
+               rtk_phy_write(phy_reg, addr, data);
+       } else {
+               u8 value;
+               s32 tmp;
+               s32 driving_updated =
+                           phy_cfg->driving_updated_for_dev_dis;
+               s32 dc_driving_mask = phy_cfg->dc_driving_mask;
+
+               tmp = (s32)(data & dc_driving_mask) + driving_updated;
+
+               if (tmp > dc_driving_mask)
+                       tmp = dc_driving_mask;
+               else if (tmp < 0)
+                       tmp = 0;
+
+               value = (data & (~dc_driving_mask)) | (tmp & dc_driving_mask);
+
+               rtk_phy_write(phy_reg, addr, value);
+       }
+
+do_toggle:
+       /* restore dc disconnect level before toggle */
+       update_dc_disconnect_level(rtk_phy, phy_parameter, false);
+
+       /* Set page 1 */
+       rtk_phy_set_page(phy_reg, 1);
+
+       addr = PAGE1_0XE0;
+       data = rtk_phy_read(phy_reg, addr);
+
+       rtk_phy_write(phy_reg, addr, data &
+                     (~ENABLE_AUTO_SENSITIVITY_CALIBRATION));
+       mdelay(1);
+       rtk_phy_write(phy_reg, addr, data |
+                     (ENABLE_AUTO_SENSITIVITY_CALIBRATION));
+
+       /* update dc disconnect level after toggle */
+       update_dc_disconnect_level(rtk_phy, phy_parameter, true);
+
+out:
+       return;
+}
+
+static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index)
+{
+       struct phy_parameter *phy_parameter;
+       struct phy_cfg *phy_cfg;
+       struct phy_data *phy_data_page;
+       struct phy_reg *phy_reg;
+       int i;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (phy_cfg->use_default_parameter) {
+               dev_dbg(rtk_phy->dev, "%s phy#%d use default parameter\n",
+                       __func__, index);
+               goto do_toggle;
+       }
+
+       /* Set page 0 */
+       phy_data_page = phy_cfg->page0;
+       rtk_phy_set_page(phy_reg, 0);
+
+       for (i = 0; i < phy_cfg->page0_size; i++) {
+               struct phy_data *phy_data = phy_data_page + i;
+               u8 addr = phy_data->addr;
+               u8 data = phy_data->data;
+
+               if (!addr)
+                       continue;
+
+               if (rtk_phy_write(phy_reg, addr, data)) {
+                       dev_err(rtk_phy->dev,
+                               "%s: Error to set page0 parameter addr=0x%x value=0x%x\n",
+                               __func__, addr, data);
+                       return -EINVAL;
+               }
+       }
+
+       /* Set page 1 */
+       phy_data_page = phy_cfg->page1;
+       rtk_phy_set_page(phy_reg, 1);
+
+       for (i = 0; i < phy_cfg->page1_size; i++) {
+               struct phy_data *phy_data = phy_data_page + i;
+               u8 addr = phy_data->addr;
+               u8 data = phy_data->data;
+
+               if (!addr)
+                       continue;
+
+               if (rtk_phy_write(phy_reg, addr, data)) {
+                       dev_err(rtk_phy->dev,
+                               "%s: Error to set page1 parameter addr=0x%x value=0x%x\n",
+                               __func__, addr, data);
+                       return -EINVAL;
+               }
+       }
+
+       if (phy_cfg->page2_size == 0)
+               goto do_toggle;
+
+       /* Set page 2 */
+       phy_data_page = phy_cfg->page2;
+       rtk_phy_set_page(phy_reg, 2);
+
+       for (i = 0; i < phy_cfg->page2_size; i++) {
+               struct phy_data *phy_data = phy_data_page + i;
+               u8 addr = phy_data->addr;
+               u8 data = phy_data->data;
+
+               if (!addr)
+                       continue;
+
+               if (rtk_phy_write(phy_reg, addr, data)) {
+                       dev_err(rtk_phy->dev,
+                               "%s: Error to set page2 parameter addr=0x%x value=0x%x\n",
+                               __func__, addr, data);
+                       return -EINVAL;
+               }
+       }
+
+do_toggle:
+       do_rtk_phy_toggle(rtk_phy, index, false);
+
+       return 0;
+}
+
+static int rtk_phy_init(struct phy *phy)
+{
+       struct rtk_phy *rtk_phy = phy_get_drvdata(phy);
+       unsigned long phy_init_time = jiffies;
+       int i, ret = 0;
+
+       if (!rtk_phy)
+               return -EINVAL;
+
+       for (i = 0; i < rtk_phy->num_phy; i++)
+               ret = do_rtk_phy_init(rtk_phy, i);
+
+       dev_dbg(rtk_phy->dev, "Initialized RTK USB 2.0 PHY (take %dms)\n",
+               jiffies_to_msecs(jiffies - phy_init_time));
+       return ret;
+}
+
+static int rtk_phy_exit(struct phy *phy)
+{
+       return 0;
+}
+
+static const struct phy_ops ops = {
+       .init           = rtk_phy_init,
+       .exit           = rtk_phy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static void rtk_phy_toggle(struct usb_phy *usb2_phy, bool connect, int port)
+{
+       int index = port;
+       struct rtk_phy *rtk_phy = NULL;
+
+       rtk_phy = dev_get_drvdata(usb2_phy->dev);
+
+       if (index > rtk_phy->num_phy) {
+               dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n",
+                       __func__, index, rtk_phy->num_phy);
+               return;
+       }
+
+       do_rtk_phy_toggle(rtk_phy, index, connect);
+}
+
+static int rtk_phy_notify_port_status(struct usb_phy *x, int port,
+                                     u16 portstatus, u16 portchange)
+{
+       bool connect = false;
+
+       pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n",
+                __func__, port, (int)portstatus, (int)portchange);
+       if (portstatus & USB_PORT_STAT_CONNECTION)
+               connect = true;
+
+       if (portchange & USB_PORT_STAT_C_CONNECTION)
+               rtk_phy_toggle(x, connect, port);
+
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *create_phy_debug_root(void)
+{
+       struct dentry *phy_debug_root;
+
+       phy_debug_root = debugfs_lookup("phy", usb_debug_root);
+       if (!phy_debug_root)
+               phy_debug_root = debugfs_create_dir("phy", usb_debug_root);
+
+       return phy_debug_root;
+}
+
+static int rtk_usb2_parameter_show(struct seq_file *s, void *unused)
+{
+       struct rtk_phy *rtk_phy = s->private;
+       struct phy_cfg *phy_cfg;
+       int i, index;
+
+       phy_cfg = rtk_phy->phy_cfg;
+
+       seq_puts(s, "Property:\n");
+       seq_printf(s, "  check_efuse: %s\n",
+                  phy_cfg->check_efuse ? "Enable" : "Disable");
+       seq_printf(s, "  check_efuse_version: %d\n",
+                  phy_cfg->check_efuse_version);
+       seq_printf(s, "  efuse_dc_driving_rate: %d\n",
+                  phy_cfg->efuse_dc_driving_rate);
+       seq_printf(s, "  dc_driving_mask: 0x%x\n",
+                  phy_cfg->dc_driving_mask);
+       seq_printf(s, "  efuse_dc_disconnect_rate: %d\n",
+                  phy_cfg->efuse_dc_disconnect_rate);
+       seq_printf(s, "  dc_disconnect_mask: 0x%x\n",
+                  phy_cfg->dc_disconnect_mask);
+       seq_printf(s, "  usb_dc_disconnect_at_page0: %s\n",
+                  phy_cfg->usb_dc_disconnect_at_page0 ? "true" : "false");
+       seq_printf(s, "  do_toggle: %s\n",
+                  phy_cfg->do_toggle ? "Enable" : "Disable");
+       seq_printf(s, "  do_toggle_driving: %s\n",
+                  phy_cfg->do_toggle_driving ? "Enable" : "Disable");
+       seq_printf(s, "  driving_updated_for_dev_dis: 0x%x\n",
+                  phy_cfg->driving_updated_for_dev_dis);
+       seq_printf(s, "  use_default_parameter: %s\n",
+                  phy_cfg->use_default_parameter ? "Enable" : "Disable");
+       seq_printf(s, "  is_double_sensitivity_mode: %s\n",
+                  phy_cfg->is_double_sensitivity_mode ? "Enable" : "Disable");
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               struct phy_parameter *phy_parameter;
+               struct phy_reg *phy_reg;
+               struct phy_data *phy_data_page;
+
+               phy_parameter =  &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+               phy_reg = &phy_parameter->phy_reg;
+
+               seq_printf(s, "PHY %d:\n", index);
+
+               seq_puts(s, "Page 0:\n");
+               /* Set page 0 */
+               phy_data_page = phy_cfg->page0;
+               rtk_phy_set_page(phy_reg, 0);
+
+               for (i = 0; i < phy_cfg->page0_size; i++) {
+                       struct phy_data *phy_data = phy_data_page + i;
+                       u8 addr = array_index_to_page_addr(i);
+                       u8 data = phy_data->data;
+                       u8 value = rtk_phy_read(phy_reg, addr);
+
+                       if (phy_data->addr)
+                               seq_printf(s, "  Page 0: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+                                          addr, data, value);
+                       else
+                               seq_printf(s, "  Page 0: addr=0x%x data=none ==> read value=0x%02x\n",
+                                          addr, value);
+               }
+
+               seq_puts(s, "Page 1:\n");
+               /* Set page 1 */
+               phy_data_page = phy_cfg->page1;
+               rtk_phy_set_page(phy_reg, 1);
+
+               for (i = 0; i < phy_cfg->page1_size; i++) {
+                       struct phy_data *phy_data = phy_data_page + i;
+                       u8 addr = array_index_to_page_addr(i);
+                       u8 data = phy_data->data;
+                       u8 value = rtk_phy_read(phy_reg, addr);
+
+                       if (phy_data->addr)
+                               seq_printf(s, "  Page 1: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+                                          addr, data, value);
+                       else
+                               seq_printf(s, "  Page 1: addr=0x%x data=none ==> read value=0x%02x\n",
+                                          addr, value);
+               }
+
+               if (phy_cfg->page2_size == 0)
+                       goto out;
+
+               seq_puts(s, "Page 2:\n");
+               /* Set page 2 */
+               phy_data_page = phy_cfg->page2;
+               rtk_phy_set_page(phy_reg, 2);
+
+               for (i = 0; i < phy_cfg->page2_size; i++) {
+                       struct phy_data *phy_data = phy_data_page + i;
+                       u8 addr = array_index_to_page_addr(i);
+                       u8 data = phy_data->data;
+                       u8 value = rtk_phy_read(phy_reg, addr);
+
+                       if (phy_data->addr)
+                               seq_printf(s, "  Page 2: addr=0x%x data=0x%02x ==> read value=0x%02x\n",
+                                          addr, data, value);
+                       else
+                               seq_printf(s, "  Page 2: addr=0x%x data=none ==> read value=0x%02x\n",
+                                          addr, value);
+               }
+
+out:
+               seq_puts(s, "PHY Property:\n");
+               seq_printf(s, "  efuse_usb_dc_cal: %d\n",
+                          (int)phy_parameter->efuse_usb_dc_cal);
+               seq_printf(s, "  efuse_usb_dc_dis: %d\n",
+                          (int)phy_parameter->efuse_usb_dc_dis);
+               seq_printf(s, "  inverse_hstx_sync_clock: %s\n",
+                          phy_parameter->inverse_hstx_sync_clock ? "Enable" : "Disable");
+               seq_printf(s, "  driving_level: %d\n",
+                          phy_parameter->driving_level);
+               seq_printf(s, "  driving_level_compensate: %d\n",
+                          phy_parameter->driving_level_compensate);
+               seq_printf(s, "  disconnection_compensate: %d\n",
+                          phy_parameter->disconnection_compensate);
+       }
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rtk_usb2_parameter);
+
+static inline void create_debug_files(struct rtk_phy *rtk_phy)
+{
+       struct dentry *phy_debug_root = NULL;
+
+       phy_debug_root = create_phy_debug_root();
+       if (!phy_debug_root)
+               return;
+
+       rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev),
+                                               phy_debug_root);
+       if (!rtk_phy->debug_dir)
+               return;
+
+       if (!debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy,
+                                &rtk_usb2_parameter_fops))
+               goto file_error;
+
+       return;
+
+file_error:
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+
+static inline void remove_debug_files(struct rtk_phy *rtk_phy)
+{
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#else
+static inline void create_debug_files(struct rtk_phy *rtk_phy) { }
+static inline void remove_debug_files(struct rtk_phy *rtk_phy) { }
+#endif /* CONFIG_DEBUG_FS */
+
+static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy,
+                                struct phy_parameter *phy_parameter, int index)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+       u8 value = 0;
+       struct nvmem_cell *cell;
+       struct soc_device_attribute rtk_soc_groot[] = {
+                   { .family = "Realtek Groot",},
+                   { /* empty */ } };
+
+       if (!phy_cfg->check_efuse)
+               goto out;
+
+       /* Read efuse for usb dc cal */
+       cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-cal");
+       if (IS_ERR(cell)) {
+               dev_dbg(rtk_phy->dev, "%s no usb-dc-cal: %ld\n",
+                       __func__, PTR_ERR(cell));
+       } else {
+               unsigned char *buf;
+               size_t buf_size;
+
+               buf = nvmem_cell_read(cell, &buf_size);
+               value = buf[0] & phy_cfg->dc_driving_mask;
+
+               kfree(buf);
+               nvmem_cell_put(cell);
+       }
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               int rate = phy_cfg->efuse_dc_driving_rate;
+
+               if (value <= EFUS_USB_DC_CAL_MAX)
+                       phy_parameter->efuse_usb_dc_cal = (int8_t)(value * rate);
+               else
+                       phy_parameter->efuse_usb_dc_cal = -(int8_t)
+                                   ((EFUS_USB_DC_CAL_MAX & value) * rate);
+
+               if (soc_device_match(rtk_soc_groot)) {
+                       dev_dbg(rtk_phy->dev, "For groot IC we need a workaround to adjust efuse_usb_dc_cal\n");
+
+                       /* We don't multiple dc_cal_rate=2 for positive dc cal compensate */
+                       if (value <= EFUS_USB_DC_CAL_MAX)
+                               phy_parameter->efuse_usb_dc_cal = (int8_t)(value);
+
+                       /* We set max dc cal compensate is 0x8 if otp is 0x7 */
+                       if (value == 0x7)
+                               phy_parameter->efuse_usb_dc_cal = (int8_t)(value + 1);
+               }
+       } else { /* for CHECK_EFUSE_V2 */
+               phy_parameter->efuse_usb_dc_cal = value & phy_cfg->dc_driving_mask;
+       }
+
+       /* Read efuse for usb dc disconnect level */
+       value = 0;
+       cell = nvmem_cell_get(rtk_phy->dev, "usb-dc-dis");
+       if (IS_ERR(cell)) {
+               dev_dbg(rtk_phy->dev, "%s no usb-dc-dis: %ld\n",
+                       __func__, PTR_ERR(cell));
+       } else {
+               unsigned char *buf;
+               size_t buf_size;
+
+               buf = nvmem_cell_read(cell, &buf_size);
+               value = buf[0] & phy_cfg->dc_disconnect_mask;
+
+               kfree(buf);
+               nvmem_cell_put(cell);
+       }
+
+       if (phy_cfg->check_efuse_version == CHECK_EFUSE_V1) {
+               int rate = phy_cfg->efuse_dc_disconnect_rate;
+
+               if (value <= EFUS_USB_DC_DIS_MAX)
+                       phy_parameter->efuse_usb_dc_dis = (int8_t)(value * rate);
+               else
+                       phy_parameter->efuse_usb_dc_dis = -(int8_t)
+                                   ((EFUS_USB_DC_DIS_MAX & value) * rate);
+       } else { /* for CHECK_EFUSE_V2 */
+               phy_parameter->efuse_usb_dc_dis = value & phy_cfg->dc_disconnect_mask;
+       }
+
+out:
+       return 0;
+}
+
+static int parse_phy_data(struct rtk_phy *rtk_phy)
+{
+       struct device *dev = rtk_phy->dev;
+       struct device_node *np = dev->of_node;
+       struct phy_parameter *phy_parameter;
+       int ret = 0;
+       int index;
+
+       rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) *
+                                               rtk_phy->num_phy, GFP_KERNEL);
+       if (!rtk_phy->phy_parameter)
+               return -ENOMEM;
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+
+               phy_parameter->phy_reg.reg_wrap_vstatus = of_iomap(np, 0);
+               phy_parameter->phy_reg.reg_gusb2phyacc0 = of_iomap(np, 1) + index;
+               phy_parameter->phy_reg.vstatus_index = index;
+
+               if (of_property_read_bool(np, "realtek,inverse-hstx-sync-clock"))
+                       phy_parameter->inverse_hstx_sync_clock = true;
+               else
+                       phy_parameter->inverse_hstx_sync_clock = false;
+
+               if (of_property_read_u32_index(np, "realtek,driving-level",
+                                              index, &phy_parameter->driving_level))
+                       phy_parameter->driving_level = DEFAULT_DC_DRIVING_VALUE;
+
+               if (of_property_read_u32_index(np, "realtek,driving-level-compensate",
+                                              index, &phy_parameter->driving_level_compensate))
+                       phy_parameter->driving_level_compensate = 0;
+
+               if (of_property_read_u32_index(np, "realtek,disconnection-compensate",
+                                              index, &phy_parameter->disconnection_compensate))
+                       phy_parameter->disconnection_compensate = 0;
+
+               get_phy_data_by_efuse(rtk_phy, phy_parameter, index);
+
+               update_dc_driving_level(rtk_phy, phy_parameter);
+
+               update_hs_clk_select(rtk_phy, phy_parameter);
+       }
+
+       return ret;
+}
+
+static int rtk_usb2phy_probe(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy;
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       const struct phy_cfg *phy_cfg;
+       int ret = 0;
+
+       phy_cfg = of_device_get_match_data(dev);
+       if (!phy_cfg) {
+               dev_err(dev, "phy config are not assigned!\n");
+               return -EINVAL;
+       }
+
+       rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL);
+       if (!rtk_phy)
+               return -ENOMEM;
+
+       rtk_phy->dev                    = &pdev->dev;
+       rtk_phy->phy.dev                = rtk_phy->dev;
+       rtk_phy->phy.label              = "rtk-usb2phy";
+       rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status;
+
+       rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
+
+       memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg));
+
+       rtk_phy->num_phy = phy_cfg->num_phy;
+
+       ret = parse_phy_data(rtk_phy);
+       if (ret)
+               goto err;
+
+       platform_set_drvdata(pdev, rtk_phy);
+
+       generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops);
+       if (IS_ERR(generic_phy))
+               return PTR_ERR(generic_phy);
+
+       phy_set_drvdata(generic_phy, rtk_phy);
+
+       phy_provider = devm_of_phy_provider_register(rtk_phy->dev,
+                                                    of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       ret = usb_add_phy_dev(&rtk_phy->phy);
+       if (ret)
+               goto err;
+
+       create_debug_files(rtk_phy);
+
+err:
+       return ret;
+}
+
+static void rtk_usb2phy_remove(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy = platform_get_drvdata(pdev);
+
+       remove_debug_files(rtk_phy);
+
+       usb_remove_phy(&rtk_phy->phy);
+}
+
+static const struct phy_cfg rtd1295_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0x90},
+                  [3] = {0xe3, 0x3a},
+                  [4] = {0xe4, 0x68},
+                  [6] = {0xe6, 0x91},
+                 [13] = {0xf5, 0x81},
+                 [15] = {0xf7, 0x02}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 1,
+       .check_efuse = false,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1395_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [4] = {0xe4, 0xac},
+                 [13] = {0xf5, 0x00},
+                 [15] = {0xf7, 0x02}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 1,
+       .check_efuse = false,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1395_phy_cfg_2port = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [4] = {0xe4, 0xac},
+                 [13] = {0xf5, 0x00},
+                 [15] = {0xf7, 0x02}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 2,
+       .check_efuse = false,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1619_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [4] = {0xe4, 0x68}, },
+       .page1_size = 8,
+       .page1 = { /* default parameter */ },
+       .page2_size = 0,
+       .page2 = { /* no parameter */ },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = false,
+};
+
+static const struct phy_cfg rtd1319_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0x18},
+                  [4] = {0xe4, 0x6a},
+                  [7] = {0xe7, 0x71},
+                 [13] = {0xf5, 0x15},
+                 [15] = {0xf7, 0x32}, },
+       .page1_size = 8,
+       .page1 = { [3] = {0xe3, 0x44}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [0] = {0xe0, 0x01}, },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = true,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1312c_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0x14},
+                  [4] = {0xe4, 0x67},
+                  [5] = {0xe5, 0x55}, },
+       .page1_size = 8,
+       .page1 = { [3] = {0xe3, 0x23},
+                  [6] = {0xe6, 0x58}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { /* default parameter */ },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = 1,
+       .dc_driving_mask = 0xf,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = true,
+       .do_toggle = true,
+       .do_toggle_driving = true,
+       .driving_updated_for_dev_dis = 0xf,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1619b_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0xa3},
+                  [4] = {0xe4, 0x88},
+                  [5] = {0xe5, 0x4f},
+                  [6] = {0xe6, 0x02}, },
+       .page1_size = 8,
+       .page1 = { [3] = {0xe3, 0x64}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [7] = {0xe7, 0x45}, },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE,
+       .dc_driving_mask = 0x1f,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = false,
+       .do_toggle = true,
+       .do_toggle_driving = true,
+       .driving_updated_for_dev_dis = 0x8,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1319d_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0xa3},
+                  [4] = {0xe4, 0x8e},
+                  [5] = {0xe5, 0x4f},
+                  [6] = {0xe6, 0x02}, },
+       .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE,
+       .page1 = { [14] = {0xf5, 0x1}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [7] = {0xe7, 0x44}, },
+       .check_efuse = true,
+       .num_phy = 1,
+       .check_efuse_version = CHECK_EFUSE_V1,
+       .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE,
+       .dc_driving_mask = 0x1f,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = false,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0x8,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct phy_cfg rtd1315e_phy_cfg = {
+       .page0_size = MAX_USB_PHY_PAGE0_DATA_SIZE,
+       .page0 = { [0] = {0xe0, 0xa3},
+                  [4] = {0xe4, 0x8c},
+                  [5] = {0xe5, 0x4f},
+                  [6] = {0xe6, 0x02}, },
+       .page1_size = MAX_USB_PHY_PAGE1_DATA_SIZE,
+       .page1 = { [3] = {0xe3, 0x7f},
+                 [14] = {0xf5, 0x01}, },
+       .page2_size = MAX_USB_PHY_PAGE2_DATA_SIZE,
+       .page2 = { [7] = {0xe7, 0x44}, },
+       .num_phy = 1,
+       .check_efuse = true,
+       .check_efuse_version = CHECK_EFUSE_V2,
+       .efuse_dc_driving_rate = EFUS_USB_DC_CAL_RATE,
+       .dc_driving_mask = 0x1f,
+       .efuse_dc_disconnect_rate = EFUS_USB_DC_DIS_RATE,
+       .dc_disconnect_mask = 0xf,
+       .usb_dc_disconnect_at_page0 = false,
+       .do_toggle = true,
+       .do_toggle_driving = false,
+       .driving_updated_for_dev_dis = 0x8,
+       .use_default_parameter = false,
+       .is_double_sensitivity_mode = true,
+};
+
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+       { .compatible = "realtek,rtd1295-usb2phy", .data = &rtd1295_phy_cfg },
+       { .compatible = "realtek,rtd1312c-usb2phy", .data = &rtd1312c_phy_cfg },
+       { .compatible = "realtek,rtd1315e-usb2phy", .data = &rtd1315e_phy_cfg },
+       { .compatible = "realtek,rtd1319-usb2phy", .data = &rtd1319_phy_cfg },
+       { .compatible = "realtek,rtd1319d-usb2phy", .data = &rtd1319d_phy_cfg },
+       { .compatible = "realtek,rtd1395-usb2phy", .data = &rtd1395_phy_cfg },
+       { .compatible = "realtek,rtd1395-usb2phy-2port", .data = &rtd1395_phy_cfg_2port },
+       { .compatible = "realtek,rtd1619-usb2phy", .data = &rtd1619_phy_cfg },
+       { .compatible = "realtek,rtd1619b-usb2phy", .data = &rtd1619b_phy_cfg },
+       {},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+
+static struct platform_driver rtk_usb2phy_driver = {
+       .probe          = rtk_usb2phy_probe,
+       .remove_new     = rtk_usb2phy_remove,
+       .driver         = {
+               .name   = "rtk-usb2phy",
+               .of_match_table = usbphy_rtk_dt_match,
+       },
+};
+
+module_platform_driver(rtk_usb2phy_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: rtk-usb2phy");
+MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>");
+MODULE_DESCRIPTION("Realtek usb 2.0 phy driver");
diff --git a/drivers/phy/realtek/phy-rtk-usb3.c b/drivers/phy/realtek/phy-rtk-usb3.c
new file mode 100644 (file)
index 0000000..6050f1e
--- /dev/null
@@ -0,0 +1,766 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *  phy-rtk-usb3.c RTK usb3.0 phy driver
+ *
+ * copyright (c) 2023 realtek semiconductor corporation
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/uaccess.h>
+#include <linux/debugfs.h>
+#include <linux/nvmem-consumer.h>
+#include <linux/regmap.h>
+#include <linux/sys_soc.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/phy.h>
+
+#define USB_MDIO_CTRL_PHY_BUSY BIT(7)
+#define USB_MDIO_CTRL_PHY_WRITE BIT(0)
+#define USB_MDIO_CTRL_PHY_ADDR_SHIFT 8
+#define USB_MDIO_CTRL_PHY_DATA_SHIFT 16
+
+#define MAX_USB_PHY_DATA_SIZE 0x30
+#define PHY_ADDR_0X09 0x09
+#define PHY_ADDR_0X0B 0x0b
+#define PHY_ADDR_0X0D 0x0d
+#define PHY_ADDR_0X10 0x10
+#define PHY_ADDR_0X1F 0x1f
+#define PHY_ADDR_0X20 0x20
+#define PHY_ADDR_0X21 0x21
+#define PHY_ADDR_0X30 0x30
+
+#define REG_0X09_FORCE_CALIBRATION BIT(9)
+#define REG_0X0B_RX_OFFSET_RANGE_MASK 0xc
+#define REG_0X0D_RX_DEBUG_TEST_EN BIT(6)
+#define REG_0X10_DEBUG_MODE_SETTING 0x3c0
+#define REG_0X10_DEBUG_MODE_SETTING_MASK 0x3f8
+#define REG_0X1F_RX_OFFSET_CODE_MASK 0x1e
+
+#define USB_U3_TX_LFPS_SWING_TRIM_SHIFT 4
+#define USB_U3_TX_LFPS_SWING_TRIM_MASK 0xf
+#define AMPLITUDE_CONTROL_COARSE_MASK 0xff
+#define AMPLITUDE_CONTROL_FINE_MASK 0xffff
+#define AMPLITUDE_CONTROL_COARSE_DEFAULT 0xff
+#define AMPLITUDE_CONTROL_FINE_DEFAULT 0xffff
+
+#define PHY_ADDR_MAP_ARRAY_INDEX(addr) (addr)
+#define ARRAY_INDEX_MAP_PHY_ADDR(index) (index)
+
+struct phy_reg {
+       void __iomem *reg_mdio_ctl;
+};
+
+struct phy_data {
+       u8 addr;
+       u16 data;
+};
+
+struct phy_cfg {
+       int param_size;
+       struct phy_data param[MAX_USB_PHY_DATA_SIZE];
+
+       bool check_efuse;
+       bool do_toggle;
+       bool do_toggle_once;
+       bool use_default_parameter;
+       bool check_rx_front_end_offset;
+};
+
+struct phy_parameter {
+       struct phy_reg phy_reg;
+
+       /* Get from efuse */
+       u8 efuse_usb_u3_tx_lfps_swing_trim;
+
+       /* Get from dts */
+       u32 amplitude_control_coarse;
+       u32 amplitude_control_fine;
+};
+
+struct rtk_phy {
+       struct usb_phy phy;
+       struct device *dev;
+
+       struct phy_cfg *phy_cfg;
+       int num_phy;
+       struct phy_parameter *phy_parameter;
+
+       struct dentry *debug_dir;
+};
+
+#define PHY_IO_TIMEOUT_USEC            (50000)
+#define PHY_IO_DELAY_US                        (100)
+
+static inline int utmi_wait_register(void __iomem *reg, u32 mask, u32 result)
+{
+       int ret;
+       unsigned int val;
+
+       ret = read_poll_timeout(readl, val, ((val & mask) == result),
+                               PHY_IO_DELAY_US, PHY_IO_TIMEOUT_USEC, false, reg);
+       if (ret) {
+               pr_err("%s can't program USB phy\n", __func__);
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static int rtk_phy3_wait_vbusy(struct phy_reg *phy_reg)
+{
+       return utmi_wait_register(phy_reg->reg_mdio_ctl, USB_MDIO_CTRL_PHY_BUSY, 0);
+}
+
+static u16 rtk_phy_read(struct phy_reg *phy_reg, char addr)
+{
+       unsigned int tmp;
+       u32 value;
+
+       tmp = (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT);
+
+       writel(tmp, phy_reg->reg_mdio_ctl);
+
+       rtk_phy3_wait_vbusy(phy_reg);
+
+       value = readl(phy_reg->reg_mdio_ctl);
+       value = value >> USB_MDIO_CTRL_PHY_DATA_SHIFT;
+
+       return (u16)value;
+}
+
+static int rtk_phy_write(struct phy_reg *phy_reg, char addr, u16 data)
+{
+       unsigned int val;
+
+       val = USB_MDIO_CTRL_PHY_WRITE |
+                   (addr << USB_MDIO_CTRL_PHY_ADDR_SHIFT) |
+                   (data << USB_MDIO_CTRL_PHY_DATA_SHIFT);
+
+       writel(val, phy_reg->reg_mdio_ctl);
+
+       rtk_phy3_wait_vbusy(phy_reg);
+
+       return 0;
+}
+
+static void do_rtk_usb3_phy_toggle(struct rtk_phy *rtk_phy, int index, bool connect)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_parameter *phy_parameter;
+       struct phy_data *phy_data;
+       u8 addr;
+       u16 data;
+       int i;
+
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (!phy_cfg->do_toggle)
+               return;
+
+       i = PHY_ADDR_MAP_ARRAY_INDEX(PHY_ADDR_0X09);
+       phy_data = phy_cfg->param + i;
+       addr = phy_data->addr;
+       data = phy_data->data;
+
+       if (!addr && !data) {
+               addr = PHY_ADDR_0X09;
+               data = rtk_phy_read(phy_reg, addr);
+               phy_data->addr = addr;
+               phy_data->data = data;
+       }
+
+       rtk_phy_write(phy_reg, addr, data & (~REG_0X09_FORCE_CALIBRATION));
+       mdelay(1);
+       rtk_phy_write(phy_reg, addr, data | REG_0X09_FORCE_CALIBRATION);
+}
+
+static int do_rtk_phy_init(struct rtk_phy *rtk_phy, int index)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+       struct phy_parameter *phy_parameter;
+       int i = 0;
+
+       phy_cfg = rtk_phy->phy_cfg;
+       phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+       phy_reg = &phy_parameter->phy_reg;
+
+       if (phy_cfg->use_default_parameter)
+               goto do_toggle;
+
+       for (i = 0; i < phy_cfg->param_size; i++) {
+               struct phy_data *phy_data = phy_cfg->param + i;
+               u8 addr = phy_data->addr;
+               u16 data = phy_data->data;
+
+               if (!addr && !data)
+                       continue;
+
+               rtk_phy_write(phy_reg, addr, data);
+       }
+
+do_toggle:
+       if (phy_cfg->do_toggle_once)
+               phy_cfg->do_toggle = true;
+
+       do_rtk_usb3_phy_toggle(rtk_phy, index, false);
+
+       if (phy_cfg->do_toggle_once) {
+               u16 check_value = 0;
+               int count = 10;
+               u16 value_0x0d, value_0x10;
+
+               /* Enable Debug mode by set 0x0D and 0x10 */
+               value_0x0d = rtk_phy_read(phy_reg, PHY_ADDR_0X0D);
+               value_0x10 = rtk_phy_read(phy_reg, PHY_ADDR_0X10);
+
+               rtk_phy_write(phy_reg, PHY_ADDR_0X0D,
+                             value_0x0d | REG_0X0D_RX_DEBUG_TEST_EN);
+               rtk_phy_write(phy_reg, PHY_ADDR_0X10,
+                             (value_0x10 & ~REG_0X10_DEBUG_MODE_SETTING_MASK) |
+                             REG_0X10_DEBUG_MODE_SETTING);
+
+               check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30);
+
+               while (!(check_value & BIT(15))) {
+                       check_value = rtk_phy_read(phy_reg, PHY_ADDR_0X30);
+                       mdelay(1);
+                       if (count-- < 0)
+                               break;
+               }
+
+               if (!(check_value & BIT(15)))
+                       dev_info(rtk_phy->dev, "toggle fail addr=0x%02x, data=0x%04x\n",
+                                PHY_ADDR_0X30, check_value);
+
+               /* Disable Debug mode by set 0x0D and 0x10 to default*/
+               rtk_phy_write(phy_reg, PHY_ADDR_0X0D, value_0x0d);
+               rtk_phy_write(phy_reg, PHY_ADDR_0X10, value_0x10);
+
+               phy_cfg->do_toggle = false;
+       }
+
+       if (phy_cfg->check_rx_front_end_offset) {
+               u16 rx_offset_code, rx_offset_range;
+               u16 code_mask = REG_0X1F_RX_OFFSET_CODE_MASK;
+               u16 range_mask = REG_0X0B_RX_OFFSET_RANGE_MASK;
+               bool do_update = false;
+
+               rx_offset_code = rtk_phy_read(phy_reg, PHY_ADDR_0X1F);
+               if (((rx_offset_code & code_mask) == 0x0) ||
+                   ((rx_offset_code & code_mask) == code_mask))
+                       do_update = true;
+
+               rx_offset_range = rtk_phy_read(phy_reg, PHY_ADDR_0X0B);
+               if (((rx_offset_range & range_mask) == range_mask) && do_update) {
+                       dev_warn(rtk_phy->dev, "Don't update rx_offset_range (rx_offset_code=0x%x, rx_offset_range=0x%x)\n",
+                                rx_offset_code, rx_offset_range);
+                       do_update = false;
+               }
+
+               if (do_update) {
+                       u16 tmp1, tmp2;
+
+                       tmp1 = rx_offset_range & (~range_mask);
+                       tmp2 = rx_offset_range & range_mask;
+                       tmp2 += (1 << 2);
+                       rx_offset_range = tmp1 | (tmp2 & range_mask);
+                       rtk_phy_write(phy_reg, PHY_ADDR_0X0B, rx_offset_range);
+                       goto do_toggle;
+               }
+       }
+
+       return 0;
+}
+
+static int rtk_phy_init(struct phy *phy)
+{
+       struct rtk_phy *rtk_phy = phy_get_drvdata(phy);
+       int ret = 0;
+       int i;
+       unsigned long phy_init_time = jiffies;
+
+       for (i = 0; i < rtk_phy->num_phy; i++)
+               ret = do_rtk_phy_init(rtk_phy, i);
+
+       dev_dbg(rtk_phy->dev, "Initialized RTK USB 3.0 PHY (take %dms)\n",
+               jiffies_to_msecs(jiffies - phy_init_time));
+
+       return ret;
+}
+
+static int rtk_phy_exit(struct phy *phy)
+{
+       return 0;
+}
+
+static const struct phy_ops ops = {
+       .init           = rtk_phy_init,
+       .exit           = rtk_phy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static void rtk_phy_toggle(struct usb_phy *usb3_phy, bool connect, int port)
+{
+       int index = port;
+       struct rtk_phy *rtk_phy = NULL;
+
+       rtk_phy = dev_get_drvdata(usb3_phy->dev);
+
+       if (index > rtk_phy->num_phy) {
+               dev_err(rtk_phy->dev, "%s: The port=%d is not in usb phy (num_phy=%d)\n",
+                       __func__, index, rtk_phy->num_phy);
+               return;
+       }
+
+       do_rtk_usb3_phy_toggle(rtk_phy, index, connect);
+}
+
+static int rtk_phy_notify_port_status(struct usb_phy *x, int port,
+                                     u16 portstatus, u16 portchange)
+{
+       bool connect = false;
+
+       pr_debug("%s port=%d portstatus=0x%x portchange=0x%x\n",
+                __func__, port, (int)portstatus, (int)portchange);
+       if (portstatus & USB_PORT_STAT_CONNECTION)
+               connect = true;
+
+       if (portchange & USB_PORT_STAT_C_CONNECTION)
+               rtk_phy_toggle(x, connect, port);
+
+       return 0;
+}
+
+#ifdef CONFIG_DEBUG_FS
+static struct dentry *create_phy_debug_root(void)
+{
+       struct dentry *phy_debug_root;
+
+       phy_debug_root = debugfs_lookup("phy", usb_debug_root);
+       if (!phy_debug_root)
+               phy_debug_root = debugfs_create_dir("phy", usb_debug_root);
+
+       return phy_debug_root;
+}
+
+static int rtk_usb3_parameter_show(struct seq_file *s, void *unused)
+{
+       struct rtk_phy *rtk_phy = s->private;
+       struct phy_cfg *phy_cfg;
+       int i, index;
+
+       phy_cfg = rtk_phy->phy_cfg;
+
+       seq_puts(s, "Property:\n");
+       seq_printf(s, "  check_efuse: %s\n",
+                  phy_cfg->check_efuse ? "Enable" : "Disable");
+       seq_printf(s, "  do_toggle: %s\n",
+                  phy_cfg->do_toggle ? "Enable" : "Disable");
+       seq_printf(s, "  do_toggle_once: %s\n",
+                  phy_cfg->do_toggle_once ? "Enable" : "Disable");
+       seq_printf(s, "  use_default_parameter: %s\n",
+                  phy_cfg->use_default_parameter ? "Enable" : "Disable");
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               struct phy_reg *phy_reg;
+               struct phy_parameter *phy_parameter;
+
+               phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+               phy_reg = &phy_parameter->phy_reg;
+
+               seq_printf(s, "PHY %d:\n", index);
+
+               for (i = 0; i < phy_cfg->param_size; i++) {
+                       struct phy_data *phy_data = phy_cfg->param + i;
+                       u8 addr = ARRAY_INDEX_MAP_PHY_ADDR(i);
+                       u16 data = phy_data->data;
+
+                       if (!phy_data->addr && !data)
+                               seq_printf(s, "  addr = 0x%02x, data = none   ==> read value = 0x%04x\n",
+                                          addr, rtk_phy_read(phy_reg, addr));
+                       else
+                               seq_printf(s, "  addr = 0x%02x, data = 0x%04x ==> read value = 0x%04x\n",
+                                          addr, data, rtk_phy_read(phy_reg, addr));
+               }
+
+               seq_puts(s, "PHY Property:\n");
+               seq_printf(s, "  efuse_usb_u3_tx_lfps_swing_trim: 0x%x\n",
+                          (int)phy_parameter->efuse_usb_u3_tx_lfps_swing_trim);
+               seq_printf(s, "  amplitude_control_coarse: 0x%x\n",
+                          (int)phy_parameter->amplitude_control_coarse);
+               seq_printf(s, "  amplitude_control_fine: 0x%x\n",
+                          (int)phy_parameter->amplitude_control_fine);
+       }
+
+       return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(rtk_usb3_parameter);
+
+static inline void create_debug_files(struct rtk_phy *rtk_phy)
+{
+       struct dentry *phy_debug_root = NULL;
+
+       phy_debug_root = create_phy_debug_root();
+
+       if (!phy_debug_root)
+               return;
+
+       rtk_phy->debug_dir = debugfs_create_dir(dev_name(rtk_phy->dev), phy_debug_root);
+       if (!rtk_phy->debug_dir)
+               return;
+
+       if (!debugfs_create_file("parameter", 0444, rtk_phy->debug_dir, rtk_phy,
+                                &rtk_usb3_parameter_fops))
+               goto file_error;
+
+       return;
+
+file_error:
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+
+static inline void remove_debug_files(struct rtk_phy *rtk_phy)
+{
+       debugfs_remove_recursive(rtk_phy->debug_dir);
+}
+#else
+static inline void create_debug_files(struct rtk_phy *rtk_phy) { }
+static inline void remove_debug_files(struct rtk_phy *rtk_phy) { }
+#endif /* CONFIG_DEBUG_FS */
+
+static int get_phy_data_by_efuse(struct rtk_phy *rtk_phy,
+                                struct phy_parameter *phy_parameter, int index)
+{
+       struct phy_cfg *phy_cfg = rtk_phy->phy_cfg;
+       u8 value = 0;
+       struct nvmem_cell *cell;
+
+       if (!phy_cfg->check_efuse)
+               goto out;
+
+       cell = nvmem_cell_get(rtk_phy->dev, "usb_u3_tx_lfps_swing_trim");
+       if (IS_ERR(cell)) {
+               dev_dbg(rtk_phy->dev, "%s no usb_u3_tx_lfps_swing_trim: %ld\n",
+                       __func__, PTR_ERR(cell));
+       } else {
+               unsigned char *buf;
+               size_t buf_size;
+
+               buf = nvmem_cell_read(cell, &buf_size);
+               value = buf[0] & USB_U3_TX_LFPS_SWING_TRIM_MASK;
+
+               kfree(buf);
+               nvmem_cell_put(cell);
+       }
+
+       if (value > 0 && value < 0x8)
+               phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = 0x8;
+       else
+               phy_parameter->efuse_usb_u3_tx_lfps_swing_trim = (u8)value;
+
+out:
+       return 0;
+}
+
+static void update_amplitude_control_value(struct rtk_phy *rtk_phy,
+                                          struct phy_parameter *phy_parameter)
+{
+       struct phy_cfg *phy_cfg;
+       struct phy_reg *phy_reg;
+
+       phy_reg = &phy_parameter->phy_reg;
+       phy_cfg = rtk_phy->phy_cfg;
+
+       if (phy_parameter->amplitude_control_coarse != AMPLITUDE_CONTROL_COARSE_DEFAULT) {
+               u16 val_mask = AMPLITUDE_CONTROL_COARSE_MASK;
+               u16 data;
+
+               if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) {
+                       phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20;
+                       data = rtk_phy_read(phy_reg, PHY_ADDR_0X20);
+               } else {
+                       data = phy_cfg->param[PHY_ADDR_0X20].data;
+               }
+
+               data &= (~val_mask);
+               data |= (phy_parameter->amplitude_control_coarse & val_mask);
+
+               phy_cfg->param[PHY_ADDR_0X20].data = data;
+       }
+
+       if (phy_parameter->efuse_usb_u3_tx_lfps_swing_trim) {
+               u8 efuse_val = phy_parameter->efuse_usb_u3_tx_lfps_swing_trim;
+               u16 val_mask = USB_U3_TX_LFPS_SWING_TRIM_MASK;
+               int val_shift = USB_U3_TX_LFPS_SWING_TRIM_SHIFT;
+               u16 data;
+
+               if (!phy_cfg->param[PHY_ADDR_0X20].addr && !phy_cfg->param[PHY_ADDR_0X20].data) {
+                       phy_cfg->param[PHY_ADDR_0X20].addr = PHY_ADDR_0X20;
+                       data = rtk_phy_read(phy_reg, PHY_ADDR_0X20);
+               } else {
+                       data = phy_cfg->param[PHY_ADDR_0X20].data;
+               }
+
+               data &= ~(val_mask << val_shift);
+               data |= ((efuse_val & val_mask) << val_shift);
+
+               phy_cfg->param[PHY_ADDR_0X20].data = data;
+       }
+
+       if (phy_parameter->amplitude_control_fine != AMPLITUDE_CONTROL_FINE_DEFAULT) {
+               u16 val_mask = AMPLITUDE_CONTROL_FINE_MASK;
+
+               if (!phy_cfg->param[PHY_ADDR_0X21].addr && !phy_cfg->param[PHY_ADDR_0X21].data)
+                       phy_cfg->param[PHY_ADDR_0X21].addr = PHY_ADDR_0X21;
+
+               phy_cfg->param[PHY_ADDR_0X21].data =
+                           phy_parameter->amplitude_control_fine & val_mask;
+       }
+}
+
+static int parse_phy_data(struct rtk_phy *rtk_phy)
+{
+       struct device *dev = rtk_phy->dev;
+       struct phy_parameter *phy_parameter;
+       int ret = 0;
+       int index;
+
+       rtk_phy->phy_parameter = devm_kzalloc(dev, sizeof(struct phy_parameter) *
+                                             rtk_phy->num_phy, GFP_KERNEL);
+       if (!rtk_phy->phy_parameter)
+               return -ENOMEM;
+
+       for (index = 0; index < rtk_phy->num_phy; index++) {
+               phy_parameter = &((struct phy_parameter *)rtk_phy->phy_parameter)[index];
+
+               phy_parameter->phy_reg.reg_mdio_ctl = of_iomap(dev->of_node, 0) + index;
+
+               /* Amplitude control address 0x20 bit 0 to bit 7 */
+               if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-coarse-tuning",
+                                        &phy_parameter->amplitude_control_coarse))
+                       phy_parameter->amplitude_control_coarse = AMPLITUDE_CONTROL_COARSE_DEFAULT;
+
+               /* Amplitude control address 0x21 bit 0 to bit 16 */
+               if (of_property_read_u32(dev->of_node, "realtek,amplitude-control-fine-tuning",
+                                        &phy_parameter->amplitude_control_fine))
+                       phy_parameter->amplitude_control_fine = AMPLITUDE_CONTROL_FINE_DEFAULT;
+
+               get_phy_data_by_efuse(rtk_phy, phy_parameter, index);
+
+               update_amplitude_control_value(rtk_phy, phy_parameter);
+       }
+
+       return ret;
+}
+
+static int rtk_usb3phy_probe(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy;
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       const struct phy_cfg *phy_cfg;
+       int ret;
+
+       phy_cfg = of_device_get_match_data(dev);
+       if (!phy_cfg) {
+               dev_err(dev, "phy config are not assigned!\n");
+               return -EINVAL;
+       }
+
+       rtk_phy = devm_kzalloc(dev, sizeof(*rtk_phy), GFP_KERNEL);
+       if (!rtk_phy)
+               return -ENOMEM;
+
+       rtk_phy->dev                    = &pdev->dev;
+       rtk_phy->phy.dev                = rtk_phy->dev;
+       rtk_phy->phy.label              = "rtk-usb3phy";
+       rtk_phy->phy.notify_port_status = rtk_phy_notify_port_status;
+
+       rtk_phy->phy_cfg = devm_kzalloc(dev, sizeof(*phy_cfg), GFP_KERNEL);
+
+       memcpy(rtk_phy->phy_cfg, phy_cfg, sizeof(*phy_cfg));
+
+       rtk_phy->num_phy = 1;
+
+       ret = parse_phy_data(rtk_phy);
+       if (ret)
+               goto err;
+
+       platform_set_drvdata(pdev, rtk_phy);
+
+       generic_phy = devm_phy_create(rtk_phy->dev, NULL, &ops);
+       if (IS_ERR(generic_phy))
+               return PTR_ERR(generic_phy);
+
+       phy_set_drvdata(generic_phy, rtk_phy);
+
+       phy_provider = devm_of_phy_provider_register(rtk_phy->dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       ret = usb_add_phy_dev(&rtk_phy->phy);
+       if (ret)
+               goto err;
+
+       create_debug_files(rtk_phy);
+
+err:
+       return ret;
+}
+
+static void rtk_usb3phy_remove(struct platform_device *pdev)
+{
+       struct rtk_phy *rtk_phy = platform_get_drvdata(pdev);
+
+       remove_debug_files(rtk_phy);
+
+       usb_remove_phy(&rtk_phy->phy);
+}
+
+static const struct phy_cfg rtd1295_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [0] = {0x01, 0x4008},  [1] = {0x01, 0xe046},
+                   [2] = {0x02, 0x6046},  [3] = {0x03, 0x2779},
+                   [4] = {0x04, 0x72f5},  [5] = {0x05, 0x2ad3},
+                   [6] = {0x06, 0x000e},  [7] = {0x07, 0x2e00},
+                   [8] = {0x08, 0x3591},  [9] = {0x09, 0x525c},
+                  [10] = {0x0a, 0xa600}, [11] = {0x0b, 0xa904},
+                  [12] = {0x0c, 0xc000}, [13] = {0x0d, 0xef1c},
+                  [14] = {0x0e, 0x2000}, [15] = {0x0f, 0x0000},
+                  [16] = {0x10, 0x000c}, [17] = {0x11, 0x4c00},
+                  [18] = {0x12, 0xfc00}, [19] = {0x13, 0x0c81},
+                  [20] = {0x14, 0xde01}, [21] = {0x15, 0x0000},
+                  [22] = {0x16, 0x0000}, [23] = {0x17, 0x0000},
+                  [24] = {0x18, 0x0000}, [25] = {0x19, 0x4004},
+                  [26] = {0x1a, 0x1260}, [27] = {0x1b, 0xff00},
+                  [28] = {0x1c, 0xcb00}, [29] = {0x1d, 0xa03f},
+                  [30] = {0x1e, 0xc2e0}, [31] = {0x1f, 0x2807},
+                  [32] = {0x20, 0x947a}, [33] = {0x21, 0x88aa},
+                  [34] = {0x22, 0x0057}, [35] = {0x23, 0xab66},
+                  [36] = {0x24, 0x0800}, [37] = {0x25, 0x0000},
+                  [38] = {0x26, 0x040a}, [39] = {0x27, 0x01d6},
+                  [40] = {0x28, 0xf8c2}, [41] = {0x29, 0x3080},
+                  [42] = {0x2a, 0x3082}, [43] = {0x2b, 0x2078},
+                  [44] = {0x2c, 0xffff}, [45] = {0x2d, 0xffff},
+                  [46] = {0x2e, 0x0000}, [47] = {0x2f, 0x0040}, },
+       .check_efuse = false,
+       .do_toggle = true,
+       .do_toggle_once = false,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const struct phy_cfg rtd1619_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [8] = {0x08, 0x3591},
+                  [38] = {0x26, 0x840b},
+                  [40] = {0x28, 0xf842}, },
+       .check_efuse = false,
+       .do_toggle = true,
+       .do_toggle_once = false,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const struct phy_cfg rtd1319_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [1] = {0x01, 0xac86},
+                   [6] = {0x06, 0x0003},
+                   [9] = {0x09, 0x924c},
+                  [10] = {0x0a, 0xa608},
+                  [11] = {0x0b, 0xb905},
+                  [14] = {0x0e, 0x2010},
+                  [32] = {0x20, 0x705a},
+                  [33] = {0x21, 0xf645},
+                  [34] = {0x22, 0x0013},
+                  [35] = {0x23, 0xcb66},
+                  [41] = {0x29, 0xff00}, },
+       .check_efuse = true,
+       .do_toggle = true,
+       .do_toggle_once = false,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const struct phy_cfg rtd1619b_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [1] = {0x01, 0xac8c},
+                   [6] = {0x06, 0x0017},
+                   [9] = {0x09, 0x724c},
+                  [10] = {0x0a, 0xb610},
+                  [11] = {0x0b, 0xb90d},
+                  [13] = {0x0d, 0xef2a},
+                  [15] = {0x0f, 0x9050},
+                  [16] = {0x10, 0x000c},
+                  [32] = {0x20, 0x70ff},
+                  [34] = {0x22, 0x0013},
+                  [35] = {0x23, 0xdb66},
+                  [38] = {0x26, 0x8609},
+                  [41] = {0x29, 0xff13},
+                  [42] = {0x2a, 0x3070}, },
+       .check_efuse = true,
+       .do_toggle = false,
+       .do_toggle_once = true,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = false,
+};
+
+static const  struct phy_cfg rtd1319d_phy_cfg = {
+       .param_size = MAX_USB_PHY_DATA_SIZE,
+       .param = {  [1] = {0x01, 0xac89},
+                   [4] = {0x04, 0xf2f5},
+                   [6] = {0x06, 0x0017},
+                   [9] = {0x09, 0x424c},
+                  [10] = {0x0a, 0x9610},
+                  [11] = {0x0b, 0x9901},
+                  [12] = {0x0c, 0xf000},
+                  [13] = {0x0d, 0xef2a},
+                  [14] = {0x0e, 0x1000},
+                  [15] = {0x0f, 0x9050},
+                  [32] = {0x20, 0x7077},
+                  [35] = {0x23, 0x0b62},
+                  [37] = {0x25, 0x10ec},
+                  [42] = {0x2a, 0x3070}, },
+       .check_efuse = true,
+       .do_toggle = false,
+       .do_toggle_once = true,
+       .use_default_parameter = false,
+       .check_rx_front_end_offset = true,
+};
+
+static const struct of_device_id usbphy_rtk_dt_match[] = {
+       { .compatible = "realtek,rtd1295-usb3phy", .data = &rtd1295_phy_cfg },
+       { .compatible = "realtek,rtd1319-usb3phy", .data = &rtd1319_phy_cfg },
+       { .compatible = "realtek,rtd1319d-usb3phy", .data = &rtd1319d_phy_cfg },
+       { .compatible = "realtek,rtd1619-usb3phy", .data = &rtd1619_phy_cfg },
+       { .compatible = "realtek,rtd1619b-usb3phy", .data = &rtd1619b_phy_cfg },
+       {},
+};
+MODULE_DEVICE_TABLE(of, usbphy_rtk_dt_match);
+
+static struct platform_driver rtk_usb3phy_driver = {
+       .probe          = rtk_usb3phy_probe,
+       .remove_new     = rtk_usb3phy_remove,
+       .driver         = {
+               .name   = "rtk-usb3phy",
+               .of_match_table = usbphy_rtk_dt_match,
+       },
+};
+
+module_platform_driver(rtk_usb3phy_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform: rtk-usb3phy");
+MODULE_AUTHOR("Stanley Chang <stanley_chang@realtek.com>");
+MODULE_DESCRIPTION("Realtek usb 3.0 phy driver");
index aa0111b..11a5b34 100644 (file)
@@ -61,6 +61,7 @@
 #include <linux/module.h>
 #include <linux/dmapool.h>
 #include <linux/iopoll.h>
+#include <linux/property.h>
 
 #include "core.h"
 #include "gadget-export.h"
index 884e230..2c1aca8 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/irq.h>
 #include <linux/kernel.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 
@@ -255,9 +256,10 @@ static int cdns3_controller_resume(struct device *dev, pm_message_t msg)
        cdns3_set_platform_suspend(cdns->dev, false, false);
 
        spin_lock_irqsave(&cdns->lock, flags);
-       cdns_resume(cdns, !PMSG_IS_AUTO(msg));
+       cdns_resume(cdns);
        cdns->in_lpm = false;
        spin_unlock_irqrestore(&cdns->lock, flags);
+       cdns_set_active(cdns, !PMSG_IS_AUTO(msg));
        if (cdns->wakeup_pending) {
                cdns->wakeup_pending = false;
                enable_irq(cdns->wakeup_irq);
index fc1f003..a7265b8 100644 (file)
@@ -166,7 +166,7 @@ static int cdns_starfive_remove_core(struct device *dev, void *c)
        return 0;
 }
 
-static int cdns_starfive_remove(struct platform_device *pdev)
+static void cdns_starfive_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct cdns_starfive *data = dev_get_drvdata(dev);
@@ -178,8 +178,6 @@ static int cdns_starfive_remove(struct platform_device *pdev)
        pm_runtime_put_noidle(dev);
        cdns_clk_rst_deinit(data);
        platform_set_drvdata(pdev, NULL);
-
-       return 0;
 }
 
 #ifdef CONFIG_PM
@@ -232,7 +230,7 @@ MODULE_DEVICE_TABLE(of, cdns_starfive_of_match);
 
 static struct platform_driver cdns_starfive_driver = {
        .probe          = cdns_starfive_probe,
-       .remove         = cdns_starfive_remove,
+       .remove_new     = cdns_starfive_remove,
        .driver         = {
                .name   = "cdns3-starfive",
                .of_match_table = cdns_starfive_of_match,
index 81b9132..5945c4b 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/io.h>
 #include <linux/of_platform.h>
 #include <linux/pm_runtime.h>
+#include <linux/property.h>
 
 /* USB Wrapper register offsets */
 #define USBSS_PID              0x0
index 7b151f5..0725668 100644 (file)
@@ -208,8 +208,9 @@ static int __maybe_unused cdnsp_pci_resume(struct device *dev)
        int ret;
 
        spin_lock_irqsave(&cdns->lock, flags);
-       ret = cdns_resume(cdns, 1);
+       ret = cdns_resume(cdns);
        spin_unlock_irqrestore(&cdns->lock, flags);
+       cdns_set_active(cdns, 1);
 
        return ret;
 }
index dbcdf3b..3354877 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
@@ -522,9 +523,8 @@ int cdns_suspend(struct cdns *cdns)
 }
 EXPORT_SYMBOL_GPL(cdns_suspend);
 
-int cdns_resume(struct cdns *cdns, u8 set_active)
+int cdns_resume(struct cdns *cdns)
 {
-       struct device *dev = cdns->dev;
        enum usb_role real_role;
        bool role_changed = false;
        int ret = 0;
@@ -556,15 +556,23 @@ int cdns_resume(struct cdns *cdns, u8 set_active)
        if (cdns->roles[cdns->role]->resume)
                cdns->roles[cdns->role]->resume(cdns, cdns_power_is_lost(cdns));
 
+       return 0;
+}
+EXPORT_SYMBOL_GPL(cdns_resume);
+
+void cdns_set_active(struct cdns *cdns, u8 set_active)
+{
+       struct device *dev = cdns->dev;
+
        if (set_active) {
                pm_runtime_disable(dev);
                pm_runtime_set_active(dev);
                pm_runtime_enable(dev);
        }
 
-       return 0;
+       return;
 }
-EXPORT_SYMBOL_GPL(cdns_resume);
+EXPORT_SYMBOL_GPL(cdns_set_active);
 #endif /* CONFIG_PM_SLEEP */
 
 MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
index 2d332a7..4a4dbc2 100644 (file)
@@ -125,10 +125,13 @@ int cdns_init(struct cdns *cdns);
 int cdns_remove(struct cdns *cdns);
 
 #ifdef CONFIG_PM_SLEEP
-int cdns_resume(struct cdns *cdns, u8 set_active);
+int cdns_resume(struct cdns *cdns);
 int cdns_suspend(struct cdns *cdns);
+void cdns_set_active(struct cdns *cdns, u8 set_active);
 #else /* CONFIG_PM_SLEEP */
-static inline int cdns_resume(struct cdns *cdns, u8 set_active)
+static inline int cdns_resume(struct cdns *cdns)
+{ return 0; }
+static inline int cdns_set_active(struct cdns *cdns, u8 set_active)
 { return 0; }
 static inline int cdns_suspend(struct cdns *cdns)
 { return 0; }
index d00ff98..04b6d12 100644 (file)
@@ -196,6 +196,7 @@ int cdns_drd_host_on(struct cdns *cdns)
        if (ret)
                dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
 
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_HOST);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_HOST);
        return ret;
 }
@@ -216,6 +217,7 @@ void cdns_drd_host_off(struct cdns *cdns)
        readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
                                  !(val & OTGSTATE_HOST_STATE_MASK),
                                  1, 2000000);
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
 }
 
@@ -248,6 +250,7 @@ int cdns_drd_gadget_on(struct cdns *cdns)
                return ret;
        }
 
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_USB_DEVICE);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_USB_DEVICE);
        return 0;
 }
@@ -273,6 +276,7 @@ void cdns_drd_gadget_off(struct cdns *cdns)
        readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
                                  !(val & OTGSTATE_DEV_STATE_MASK),
                                  1, 2000000);
+       phy_set_mode(cdns->usb2_phy, PHY_MODE_INVALID);
        phy_set_mode(cdns->usb3_phy, PHY_MODE_INVALID);
 }
 EXPORT_SYMBOL_GPL(cdns_drd_gadget_off);
index f210b74..d262b9d 100644 (file)
@@ -281,8 +281,19 @@ static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
                return -ENXIO;
 
        ret = ci->roles[role]->start(ci);
-       if (!ret)
-               ci->role = role;
+       if (ret)
+               return ret;
+
+       ci->role = role;
+
+       if (ci->usb_phy) {
+               if (role == CI_ROLE_HOST)
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_ID);
+               else
+                       /* in device mode but vbus is invalid*/
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
+       }
+
        return ret;
 }
 
@@ -296,6 +307,9 @@ static inline void ci_role_stop(struct ci_hdrc *ci)
        ci->role = CI_ROLE_END;
 
        ci->roles[role]->stop(ci);
+
+       if (ci->usb_phy)
+               usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
 }
 
 static inline enum usb_role ci_role_to_usb_role(struct ci_hdrc *ci)
index 336ef6d..772bbda 100644 (file)
@@ -6,6 +6,7 @@
  */
 
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
@@ -175,10 +176,15 @@ static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
        if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
                data->ulpi = 1;
 
-       of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
-                       &data->emp_curr_control);
-       of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
-                       &data->dc_vol_level_adjust);
+       if (of_property_read_u32(np, "samsung,picophy-pre-emp-curr-control",
+                       &data->emp_curr_control))
+               data->emp_curr_control = -1;
+       if (of_property_read_u32(np, "samsung,picophy-dc-vol-level-adjust",
+                       &data->dc_vol_level_adjust))
+               data->dc_vol_level_adjust = -1;
+       if (of_property_read_u32(np, "fsl,picophy-rise-fall-time-adjust",
+                       &data->rise_fall_time_adjust))
+               data->rise_fall_time_adjust = -1;
 
        return data;
 }
index 7135b9a..88b8da7 100644 (file)
@@ -28,6 +28,7 @@ struct imx_usbmisc_data {
        enum usb_dr_mode available_role; /* runtime usb dr mode */
        int emp_curr_control;
        int dc_vol_level_adjust;
+       int rise_fall_time_adjust;
 };
 
 int imx_usbmisc_init(struct imx_usbmisc_data *data);
index ca36d11..8e78bf6 100644 (file)
@@ -6,7 +6,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 
index 51994d6..6e1196b 100644 (file)
@@ -1028,8 +1028,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
                return -ENODEV;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(dev, res);
+       base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(base))
                return PTR_ERR(base);
 
index 54c0924..d583554 100644 (file)
@@ -1718,6 +1718,13 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
                ret = ci->platdata->notify_event(ci,
                                CI_HDRC_CONTROLLER_VBUS_EVENT);
 
+       if (ci->usb_phy) {
+               if (is_active)
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_VBUS);
+               else
+                       usb_phy_set_event(ci->usb_phy, USB_EVENT_NONE);
+       }
+
        if (ci->driver)
                ci_hdrc_gadget_connect(_gadget, is_active);
 
@@ -2034,6 +2041,9 @@ static irqreturn_t udc_irq(struct ci_hdrc *ci)
                if (USBi_PCI & intr) {
                        ci->gadget.speed = hw_port_is_high_speed(ci) ?
                                USB_SPEED_HIGH : USB_SPEED_FULL;
+                       if (ci->usb_phy)
+                               usb_phy_set_event(ci->usb_phy,
+                                       USB_EVENT_ENUMERATED);
                        if (ci->suspended) {
                                if (ci->driver->resume) {
                                        spin_unlock(&ci->lock);
index 9ee9621..173c78a 100644 (file)
@@ -4,10 +4,11 @@
  */
 
 #include <linux/module.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <linux/platform_device.h>
 #include <linux/usb/otg.h>
 
 #include "ci_hdrc_imx.h"
 #define MX7D_USB_OTG_PHY_CFG1          0x30
 #define TXPREEMPAMPTUNE0_BIT           28
 #define TXPREEMPAMPTUNE0_MASK          (3 << 28)
+#define TXRISETUNE0_BIT                        24
+#define TXRISETUNE0_MASK               (3 << 24)
 #define TXVREFTUNE0_BIT                        20
 #define TXVREFTUNE0_MASK               (0xf << 20)
 
@@ -659,18 +662,27 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
                        usbmisc->base + MX7D_USBNC_USB_CTRL2);
                /* PHY tuning for signal quality */
                reg = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
-               if (data->emp_curr_control && data->emp_curr_control <=
+               if (data->emp_curr_control >= 0 &&
+                       data->emp_curr_control <=
                        (TXPREEMPAMPTUNE0_MASK >> TXPREEMPAMPTUNE0_BIT)) {
                        reg &= ~TXPREEMPAMPTUNE0_MASK;
                        reg |= (data->emp_curr_control << TXPREEMPAMPTUNE0_BIT);
                }
 
-               if (data->dc_vol_level_adjust && data->dc_vol_level_adjust <=
+               if (data->dc_vol_level_adjust >= 0 &&
+                       data->dc_vol_level_adjust <=
                        (TXVREFTUNE0_MASK >> TXVREFTUNE0_BIT)) {
                        reg &= ~TXVREFTUNE0_MASK;
                        reg |= (data->dc_vol_level_adjust << TXVREFTUNE0_BIT);
                }
 
+               if (data->rise_fall_time_adjust >= 0 &&
+                       data->rise_fall_time_adjust <=
+                       (TXRISETUNE0_MASK >> TXRISETUNE0_BIT)) {
+                       reg &= ~TXRISETUNE0_MASK;
+                       reg |= (data->rise_fall_time_adjust << TXRISETUNE0_BIT);
+               }
+
                writel(reg, usbmisc->base + MX7D_USB_OTG_PHY_CFG1);
        }
 
index c9bdeb4..b84efae 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/of.h>
 #include <linux/usb/otg.h>
index c4ed331..a88ced9 100644 (file)
@@ -29,7 +29,6 @@
 #define MAX_USB_MINORS 256
 static const struct file_operations *usb_minors[MAX_USB_MINORS];
 static DECLARE_RWSEM(minor_rwsem);
-static DEFINE_MUTEX(init_usb_class_mutex);
 
 static int usb_open(struct inode *inode, struct file *file)
 {
@@ -57,11 +56,6 @@ static const struct file_operations usb_fops = {
        .llseek =       noop_llseek,
 };
 
-static struct usb_class {
-       struct kref kref;
-       struct class *class;
-} *usb_class;
-
 static char *usb_devnode(const struct device *dev, umode_t *mode)
 {
        struct usb_class_driver *drv;
@@ -72,50 +66,10 @@ static char *usb_devnode(const struct device *dev, umode_t *mode)
        return drv->devnode(dev, mode);
 }
 
-static int init_usb_class(void)
-{
-       int result = 0;
-
-       if (usb_class != NULL) {
-               kref_get(&usb_class->kref);
-               goto exit;
-       }
-
-       usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL);
-       if (!usb_class) {
-               result = -ENOMEM;
-               goto exit;
-       }
-
-       kref_init(&usb_class->kref);
-       usb_class->class = class_create("usbmisc");
-       if (IS_ERR(usb_class->class)) {
-               result = PTR_ERR(usb_class->class);
-               printk(KERN_ERR "class_create failed for usb devices\n");
-               kfree(usb_class);
-               usb_class = NULL;
-               goto exit;
-       }
-       usb_class->class->devnode = usb_devnode;
-
-exit:
-       return result;
-}
-
-static void release_usb_class(struct kref *kref)
-{
-       /* Ok, we cheat as we know we only have one usb_class */
-       class_destroy(usb_class->class);
-       kfree(usb_class);
-       usb_class = NULL;
-}
-
-static void destroy_usb_class(void)
-{
-       mutex_lock(&init_usb_class_mutex);
-       kref_put(&usb_class->kref, release_usb_class);
-       mutex_unlock(&init_usb_class_mutex);
-}
+const struct class usbmisc_class = {
+       .name           = "usbmisc",
+       .devnode        = usb_devnode,
+};
 
 int usb_major_init(void)
 {
@@ -156,7 +110,7 @@ void usb_major_cleanup(void)
 int usb_register_dev(struct usb_interface *intf,
                     struct usb_class_driver *class_driver)
 {
-       int retval;
+       int retval = 0;
        int minor_base = class_driver->minor_base;
        int minor;
        char name[20];
@@ -175,13 +129,6 @@ int usb_register_dev(struct usb_interface *intf,
        if (intf->minor >= 0)
                return -EADDRINUSE;
 
-       mutex_lock(&init_usb_class_mutex);
-       retval = init_usb_class();
-       mutex_unlock(&init_usb_class_mutex);
-
-       if (retval)
-               return retval;
-
        dev_dbg(&intf->dev, "looking for a minor, starting at %d\n", minor_base);
 
        down_write(&minor_rwsem);
@@ -200,7 +147,7 @@ int usb_register_dev(struct usb_interface *intf,
 
        /* create a usb class device for this usb interface */
        snprintf(name, sizeof(name), class_driver->name, minor - minor_base);
-       intf->usb_dev = device_create(usb_class->class, &intf->dev,
+       intf->usb_dev = device_create(&usbmisc_class, &intf->dev,
                                      MKDEV(USB_MAJOR, minor), class_driver,
                                      "%s", kbasename(name));
        if (IS_ERR(intf->usb_dev)) {
@@ -234,7 +181,7 @@ void usb_deregister_dev(struct usb_interface *intf,
                return;
 
        dev_dbg(&intf->dev, "removing %d minor\n", intf->minor);
-       device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
+       device_destroy(&usbmisc_class, MKDEV(USB_MAJOR, intf->minor));
 
        down_write(&minor_rwsem);
        usb_minors[intf->minor] = NULL;
@@ -242,6 +189,5 @@ void usb_deregister_dev(struct usb_interface *intf,
 
        intf->usb_dev = NULL;
        intf->minor = -1;
-       destroy_usb_class();
 }
 EXPORT_SYMBOL_GPL(usb_deregister_dev);
index a739403..fcbad9e 100644 (file)
@@ -614,6 +614,29 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type,
                ret = 0;
        }
        mutex_unlock(&hub->status_mutex);
+
+       /*
+        * There is no need to lock status_mutex here, because status_mutex
+        * protects hub->status, and the phy driver only checks the port
+        * status without changing the status.
+        */
+       if (!ret) {
+               struct usb_device *hdev = hub->hdev;
+
+               /*
+                * Only roothub will be notified of port state changes,
+                * since the USB PHY only cares about changes at the next
+                * level.
+                */
+               if (is_root_hub(hdev)) {
+                       struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
+
+                       if (hcd->usb_phy)
+                               usb_phy_notify_port_status(hcd->usb_phy,
+                                                          port1 - 1, *status, *change);
+               }
+       }
+
        return ret;
 }
 
index b581162..0d2bfc9 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/pci.h> /* for scatterlist macros */
 #include <linux/usb.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/slab.h>
 #include <linux/mm.h>
 #include <linux/timer.h>
index 617e925..db4ccf9 100644 (file)
@@ -8,7 +8,6 @@
  */
 
 #include <linux/of.h>
-#include <linux/of_platform.h>
 #include <linux/usb/of.h>
 
 /**
index 901ec73..bba87e5 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <linux/module.h>
 #include <linux/moduleparam.h>
+#include <linux/of.h>
 #include <linux/string.h>
 #include <linux/bitops.h>
 #include <linux/slab.h>
@@ -1101,6 +1102,9 @@ static int __init usb_init(void)
        retval = usb_major_init();
        if (retval)
                goto major_init_failed;
+       retval = class_register(&usbmisc_class);
+       if (retval)
+               goto class_register_failed;
        retval = usb_register(&usbfs_driver);
        if (retval)
                goto driver_register_failed;
@@ -1120,6 +1124,8 @@ hub_init_failed:
 usb_devio_init_failed:
        usb_deregister(&usbfs_driver);
 driver_register_failed:
+       class_unregister(&usbmisc_class);
+class_register_failed:
        usb_major_cleanup();
 major_init_failed:
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
@@ -1147,6 +1153,7 @@ static void __exit usb_exit(void)
        usb_deregister(&usbfs_driver);
        usb_devio_cleanup();
        usb_hub_cleanup();
+       class_unregister(&usbmisc_class);
        bus_unregister_notifier(&usb_bus_type, &usb_bus_nb);
        bus_unregister(&usb_bus_type);
        usb_acpi_unregister();
index ffe3f68..69ca598 100644 (file)
@@ -141,6 +141,7 @@ static inline int usb_disable_usb2_hardware_lpm(struct usb_device *udev)
 
 #endif
 
+extern const struct class usbmisc_class;
 extern const struct bus_type usb_bus_type;
 extern struct mutex usb_port_peer_mutex;
 extern struct device_type usb_device_type;
index 8b15742..b517a72 100644 (file)
@@ -22,7 +22,6 @@
 #include <linux/delay.h>
 #include <linux/io.h>
 #include <linux/slab.h>
-#include <linux/of_platform.h>
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
index 0a806f8..b1d4801 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/clk.h>
 #include <linux/device.h>
 #include <linux/dma-mapping.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/mutex.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
index 1755f2f..94dcbc4 100644 (file)
 
 #define DWC3_AM62_AUTOSUSPEND_DELAY    100
 
-struct dwc3_data {
+struct dwc3_am62 {
        struct device *dev;
        void __iomem *usbss;
        struct clk *usb2_refclk;
@@ -129,19 +129,19 @@ static const int dwc3_ti_rate_table[] = { /* in KHZ */
        52000,
 };
 
-static inline u32 dwc3_ti_readl(struct dwc3_data *data, u32 offset)
+static inline u32 dwc3_ti_readl(struct dwc3_am62 *am62, u32 offset)
 {
-       return readl((data->usbss) + offset);
+       return readl((am62->usbss) + offset);
 }
 
-static inline void dwc3_ti_writel(struct dwc3_data *data, u32 offset, u32 value)
+static inline void dwc3_ti_writel(struct dwc3_am62 *am62, u32 offset, u32 value)
 {
-       writel(value, (data->usbss) + offset);
+       writel(value, (am62->usbss) + offset);
 }
 
-static int phy_syscon_pll_refclk(struct dwc3_data *data)
+static int phy_syscon_pll_refclk(struct dwc3_am62 *am62)
 {
-       struct device *dev = data->dev;
+       struct device *dev = am62->dev;
        struct device_node *node = dev->of_node;
        struct of_phandle_args args;
        struct regmap *syscon;
@@ -153,16 +153,16 @@ static int phy_syscon_pll_refclk(struct dwc3_data *data)
                return PTR_ERR(syscon);
        }
 
-       data->syscon = syscon;
+       am62->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];
+       am62->offset = args.args[0];
 
-       ret = regmap_update_bits(data->syscon, data->offset, PHY_PLL_REFCLK_MASK, data->rate_code);
+       ret = regmap_update_bits(am62->syscon, am62->offset, PHY_PLL_REFCLK_MASK, am62->rate_code);
        if (ret) {
                dev_err(dev, "failed to set phy pll reference clock rate\n");
                return ret;
@@ -175,32 +175,32 @@ 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;
+       struct dwc3_am62 *am62;
        int i, ret;
        unsigned long rate;
        u32 reg;
 
-       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
-       if (!data)
+       am62 = devm_kzalloc(dev, sizeof(*am62), GFP_KERNEL);
+       if (!am62)
                return -ENOMEM;
 
-       data->dev = dev;
-       platform_set_drvdata(pdev, data);
+       am62->dev = dev;
+       platform_set_drvdata(pdev, am62);
 
-       data->usbss = devm_platform_ioremap_resource(pdev, 0);
-       if (IS_ERR(data->usbss)) {
+       am62->usbss = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(am62->usbss)) {
                dev_err(dev, "can't map IOMEM resource\n");
-               return PTR_ERR(data->usbss);
+               return PTR_ERR(am62->usbss);
        }
 
-       data->usb2_refclk = devm_clk_get(dev, "ref");
-       if (IS_ERR(data->usb2_refclk)) {
+       am62->usb2_refclk = devm_clk_get(dev, "ref");
+       if (IS_ERR(am62->usb2_refclk)) {
                dev_err(dev, "can't get usb2_refclk\n");
-               return PTR_ERR(data->usb2_refclk);
+               return PTR_ERR(am62->usb2_refclk);
        }
 
        /* Calculate the rate code */
-       rate = clk_get_rate(data->usb2_refclk);
+       rate = clk_get_rate(am62->usb2_refclk);
        rate /= 1000;   // To KHz
        for (i = 0; i < ARRAY_SIZE(dwc3_ti_rate_table); i++) {
                if (dwc3_ti_rate_table[i] == rate)
@@ -212,20 +212,20 @@ static int dwc3_ti_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       data->rate_code = i;
+       am62->rate_code = i;
 
        /* Read the syscon property and set the rate code */
-       ret = phy_syscon_pll_refclk(data);
+       ret = phy_syscon_pll_refclk(am62);
        if (ret)
                return ret;
 
        /* 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)
+       am62->vbus_divider = device_property_read_bool(dev, "ti,vbus-divider");
+       reg = dwc3_ti_readl(am62, USBSS_PHY_CONFIG);
+       if (am62->vbus_divider)
                reg |= 1 << USBSS_PHY_VBUS_SEL_SHIFT;
 
-       dwc3_ti_writel(data, USBSS_PHY_CONFIG, reg);
+       dwc3_ti_writel(am62, USBSS_PHY_CONFIG, reg);
 
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
@@ -233,7 +233,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
         * Don't ignore its dependencies with its children
         */
        pm_suspend_ignore_children(dev, false);
-       clk_prepare_enable(data->usb2_refclk);
+       clk_prepare_enable(am62->usb2_refclk);
        pm_runtime_get_noresume(dev);
 
        ret = of_platform_populate(node, NULL, NULL, dev);
@@ -243,9 +243,9 @@ static int dwc3_ti_probe(struct platform_device *pdev)
        }
 
        /* Set mode valid bit to indicate role is valid */
-       reg = dwc3_ti_readl(data, USBSS_MODE_CONTROL);
+       reg = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
        reg |= USBSS_MODE_VALID;
-       dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+       dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
 
        /* Device has capability to wakeup system from sleep */
        device_set_wakeup_capable(dev, true);
@@ -261,7 +261,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
        return 0;
 
 err_pm_disable:
-       clk_disable_unprepare(data->usb2_refclk);
+       clk_disable_unprepare(am62->usb2_refclk);
        pm_runtime_disable(dev);
        pm_runtime_set_suspended(dev);
        return ret;
@@ -278,18 +278,18 @@ static int dwc3_ti_remove_core(struct device *dev, void *c)
 static void dwc3_ti_remove(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
-       struct dwc3_data *data = platform_get_drvdata(pdev);
+       struct dwc3_am62 *am62 = 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 = dwc3_ti_readl(am62, USBSS_MODE_CONTROL);
        reg &= ~USBSS_MODE_VALID;
-       dwc3_ti_writel(data, USBSS_MODE_CONTROL, reg);
+       dwc3_ti_writel(am62, USBSS_MODE_CONTROL, reg);
 
        pm_runtime_put_sync(dev);
-       clk_disable_unprepare(data->usb2_refclk);
+       clk_disable_unprepare(am62->usb2_refclk);
        pm_runtime_disable(dev);
        pm_runtime_set_suspended(dev);
 
@@ -299,15 +299,15 @@ static void dwc3_ti_remove(struct platform_device *pdev)
 #ifdef CONFIG_PM
 static int dwc3_ti_suspend_common(struct device *dev)
 {
-       struct dwc3_data *data = dev_get_drvdata(dev);
+       struct dwc3_am62 *am62 = dev_get_drvdata(dev);
        u32 reg, current_prtcap_dir;
 
        if (device_may_wakeup(dev)) {
-               reg = dwc3_ti_readl(data, USBSS_CORE_STAT);
+               reg = dwc3_ti_readl(am62, USBSS_CORE_STAT);
                current_prtcap_dir = (reg & USBSS_CORE_OPERATIONAL_MODE_MASK)
                                     >> USBSS_CORE_OPERATIONAL_MODE_SHIFT;
                /* Set wakeup config enable bits */
-               reg = dwc3_ti_readl(data, USBSS_WAKEUP_CONFIG);
+               reg = dwc3_ti_readl(am62, USBSS_WAKEUP_CONFIG);
                if (current_prtcap_dir == DWC3_GCTL_PRTCAP_HOST) {
                        reg = USBSS_WAKEUP_CFG_LINESTATE_EN | USBSS_WAKEUP_CFG_OVERCURRENT_EN;
                } else {
@@ -317,30 +317,30 @@ static int dwc3_ti_suspend_common(struct device *dev)
                         * and in U2/L3 state else it causes spurious wake-up.
                         */
                }
-               dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, reg);
+               dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, reg);
                /* clear wakeup status so we know what caused the wake up */
-               dwc3_ti_writel(data, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
+               dwc3_ti_writel(am62, USBSS_WAKEUP_STAT, USBSS_WAKEUP_STAT_CLR);
        }
 
-       clk_disable_unprepare(data->usb2_refclk);
+       clk_disable_unprepare(am62->usb2_refclk);
 
        return 0;
 }
 
 static int dwc3_ti_resume_common(struct device *dev)
 {
-       struct dwc3_data *data = dev_get_drvdata(dev);
+       struct dwc3_am62 *am62 = dev_get_drvdata(dev);
        u32 reg;
 
-       clk_prepare_enable(data->usb2_refclk);
+       clk_prepare_enable(am62->usb2_refclk);
 
        if (device_may_wakeup(dev)) {
                /* Clear wakeup config enable bits */
-               dwc3_ti_writel(data, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
+               dwc3_ti_writel(am62, USBSS_WAKEUP_CONFIG, USBSS_WAKEUP_CFG_NONE);
        }
 
-       reg = dwc3_ti_readl(data, USBSS_WAKEUP_STAT);
-       data->wakeup_stat = reg;
+       reg = dwc3_ti_readl(am62, USBSS_WAKEUP_STAT);
+       am62->wakeup_stat = reg;
 
        return 0;
 }
index 8b9a3bb..4285bde 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
index 0a09aed..4155e8d 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/phy/phy.h>
 #include <linux/pm_runtime.h>
index 336db8f..b3592bc 100644 (file)
@@ -208,6 +208,9 @@ config USB_F_UVC
 config USB_F_MIDI
        tristate
 
+config USB_F_MIDI2
+       tristate
+
 config USB_F_HID
        tristate
 
@@ -436,6 +439,21 @@ config USB_CONFIGFS_F_MIDI
          connections can then be made on the gadget system, using
          ALSA's aconnect utility etc.
 
+config USB_CONFIGFS_F_MIDI2
+       bool "MIDI 2.0 function"
+       depends on USB_CONFIGFS
+       depends on SND
+       select USB_LIBCOMPOSITE
+       select SND_UMP
+       select SND_UMP_LEGACY_RAWMIDI
+       select USB_F_MIDI2
+       help
+         The MIDI 2.0 function driver provides the generic emulated
+         USB MIDI 2.0 interface, looped back to ALSA UMP rawmidi
+         device on the gadget host. It supports UMP 1.1 spec and
+         responds UMP Stream messages for UMP Endpoint and Function
+         Block information / configuration.
+
 config USB_CONFIGFS_F_HID
        bool "HID function"
        depends on USB_CONFIGFS
index 5d3a6cf..87917a7 100644 (file)
@@ -44,6 +44,8 @@ usb_f_uvc-y                   := f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
 obj-$(CONFIG_USB_F_UVC)                += usb_f_uvc.o
 usb_f_midi-y                   := f_midi.o
 obj-$(CONFIG_USB_F_MIDI)       += usb_f_midi.o
+usb_f_midi2-y                  := f_midi2.o
+obj-$(CONFIG_USB_F_MIDI2)      += usb_f_midi2.o
 usb_f_hid-y                    := f_hid.o
 obj-$(CONFIG_USB_F_HID)                += usb_f_hid.o
 usb_f_printer-y                        := f_printer.o
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
new file mode 100644 (file)
index 0000000..5a971ba
--- /dev/null
@@ -0,0 +1,2883 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * f_midi2.c -- USB MIDI 2.0 class function driver
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/control.h>
+#include <sound/ump.h>
+#include <sound/ump_msg.h>
+#include <sound/ump_convert.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/audio.h>
+#include <linux/usb/midi-v2.h>
+
+#include "u_f.h"
+#include "u_midi2.h"
+
+struct f_midi2;
+struct f_midi2_ep;
+struct f_midi2_usb_ep;
+
+/* Context for each USB request */
+struct f_midi2_req_ctx {
+       struct f_midi2_usb_ep *usb_ep;  /* belonging USB EP */
+       unsigned int index;             /* array index: 0-31 */
+       struct usb_request *req;        /* assigned request */
+};
+
+/* Resources for a USB Endpoint */
+struct f_midi2_usb_ep {
+       struct f_midi2 *card;           /* belonging card */
+       struct f_midi2_ep *ep;          /* belonging UMP EP (optional) */
+       struct usb_ep *usb_ep;          /* assigned USB EP */
+       void (*complete)(struct usb_ep *usb_ep, struct usb_request *req);
+       unsigned long free_reqs;        /* bitmap for unused requests */
+       unsigned int num_reqs;          /* number of allocated requests */
+       struct f_midi2_req_ctx *reqs;   /* request context array */
+};
+
+/* Resources for UMP Function Block (and USB Group Terminal Block) */
+struct f_midi2_block {
+       struct f_midi2_block_info info; /* FB info, copied from configfs */
+       struct snd_ump_block *fb;       /* assigned FB */
+       unsigned int gtb_id;            /* assigned GTB id */
+       unsigned int string_id;         /* assigned string id */
+};
+
+/* Temporary buffer for altset 0 MIDI 1.0 handling */
+struct f_midi2_midi1_port {
+       unsigned int pending; /* pending bytes on the input buffer */
+       u8 buf[32];     /* raw MIDI 1.0 byte input */
+       u8 state;       /* running status */
+       u8 data[2];     /* rendered USB MIDI 1.0 packet data */
+};
+
+/* MIDI 1.0 message states */
+enum {
+       STATE_INITIAL = 0,      /* pseudo state */
+       STATE_1PARAM,
+       STATE_2PARAM_1,
+       STATE_2PARAM_2,
+       STATE_SYSEX_0,
+       STATE_SYSEX_1,
+       STATE_SYSEX_2,
+       STATE_REAL_TIME,
+       STATE_FINISHED,         /* pseudo state */
+};
+
+/* Resources for UMP Endpoint */
+struct f_midi2_ep {
+       struct snd_ump_endpoint *ump;   /* assigned UMP EP */
+       struct f_midi2 *card;           /* belonging MIDI 2.0 device */
+
+       struct f_midi2_ep_info info;    /* UMP EP info, copied from configfs */
+       unsigned int num_blks;          /* number of FBs */
+       struct f_midi2_block blks[SNDRV_UMP_MAX_BLOCKS];        /* UMP FBs */
+
+       struct f_midi2_usb_ep ep_in;    /* USB MIDI EP-in */
+       struct f_midi2_usb_ep ep_out;   /* USB MIDI EP-out */
+
+       u8 in_group_to_cable[SNDRV_UMP_MAX_GROUPS]; /* map to cable; 1-based! */
+};
+
+/* indices for USB strings */
+enum {
+       STR_IFACE = 0,
+       STR_GTB1 = 1,
+};
+
+/* 1-based GTB id to string id */
+#define gtb_to_str_id(id)      (STR_GTB1 + (id) - 1)
+
+/* mapping from MIDI 1.0 cable to UMP group */
+struct midi1_cable_mapping {
+       struct f_midi2_ep *ep;
+       unsigned char block;
+       unsigned char group;
+};
+
+/* operation mode */
+enum {
+       MIDI_OP_MODE_UNSET,     /* no altset set yet */
+       MIDI_OP_MODE_MIDI1,     /* MIDI 1.0 (altset 0) is used */
+       MIDI_OP_MODE_MIDI2,     /* MIDI 2.0 (altset 1) is used */
+};
+
+/* Resources for MIDI 2.0 Device */
+struct f_midi2 {
+       struct usb_function func;
+       struct usb_gadget *gadget;
+       struct snd_card *card;
+
+       /* MIDI 1.0 in/out USB EPs */
+       struct f_midi2_usb_ep midi1_ep_in;
+       struct f_midi2_usb_ep midi1_ep_out;
+
+       /* number of MIDI 1.0 I/O cables */
+       unsigned int num_midi1_in;
+       unsigned int num_midi1_out;
+
+       /* conversion for MIDI 1.0 EP-in */
+       struct f_midi2_midi1_port midi1_port[MAX_CABLES];
+       /* conversion for MIDI 1.0 EP-out */
+       struct ump_cvt_to_ump midi1_ump_cvt;
+       /* mapping between cables and UMP groups */
+       struct midi1_cable_mapping in_cable_mapping[MAX_CABLES];
+       struct midi1_cable_mapping out_cable_mapping[MAX_CABLES];
+
+       int midi_if;                    /* USB MIDI interface number */
+       int operation_mode;             /* current operation mode */
+
+       spinlock_t queue_lock;
+
+       struct f_midi2_card_info info;  /* card info, copied from configfs */
+
+       unsigned int num_eps;
+       struct f_midi2_ep midi2_eps[MAX_UMP_EPS];
+
+       unsigned int total_blocks;      /* total number of blocks of all EPs */
+       struct usb_string *string_defs;
+       struct usb_string *strings;
+};
+
+#define func_to_midi2(f)       container_of(f, struct f_midi2, func)
+
+/* get EP name string */
+static const char *ump_ep_name(const struct f_midi2_ep *ep)
+{
+       return ep->info.ep_name ? ep->info.ep_name : "MIDI 2.0 Gadget";
+}
+
+/* get EP product ID string */
+static const char *ump_product_id(const struct f_midi2_ep *ep)
+{
+       return ep->info.product_id ? ep->info.product_id : "Unique Product ID";
+}
+
+/* get FB name string */
+static const char *ump_fb_name(const struct f_midi2_block_info *info)
+{
+       return info->name ? info->name : "MIDI 2.0 Gadget I/O";
+}
+
+/*
+ * USB Descriptor Definitions
+ */
+/* GTB header descriptor */
+static struct usb_ms20_gr_trm_block_header_descriptor gtb_header_desc = {
+       .bLength =              sizeof(gtb_header_desc),
+       .bDescriptorType =      USB_DT_CS_GR_TRM_BLOCK,
+       .bDescriptorSubtype =   USB_MS_GR_TRM_BLOCK_HEADER,
+       .wTotalLength =         __cpu_to_le16(0x12), // to be filled
+};
+
+/* GTB descriptor template: most items are replaced dynamically */
+static struct usb_ms20_gr_trm_block_descriptor gtb_desc = {
+       .bLength =              sizeof(gtb_desc),
+       .bDescriptorType =      USB_DT_CS_GR_TRM_BLOCK,
+       .bDescriptorSubtype =   USB_MS_GR_TRM_BLOCK,
+       .bGrpTrmBlkID =         0x01,
+       .bGrpTrmBlkType =       USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL,
+       .nGroupTrm =            0x00,
+       .nNumGroupTrm =         1,
+       .iBlockItem =           0,
+       .bMIDIProtocol =        USB_MS_MIDI_PROTO_1_0_64,
+       .wMaxInputBandwidth =   0,
+       .wMaxOutputBandwidth =  0,
+};
+
+DECLARE_USB_MIDI_OUT_JACK_DESCRIPTOR(1);
+DECLARE_USB_MS_ENDPOINT_DESCRIPTOR(16);
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
+DECLARE_USB_MS20_ENDPOINT_DESCRIPTOR(32);
+
+#define EP_MAX_PACKET_INT      8
+
+/* Audio Control Interface */
+static struct usb_interface_descriptor midi2_audio_if_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     0, // to be filled
+       .bNumEndpoints =        0,
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_AUDIOCONTROL,
+       .bInterfaceProtocol =   0,
+       .iInterface =           0,
+};
+
+static struct uac1_ac_header_descriptor_1 midi2_audio_class_desc = {
+       .bLength =              0x09,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   0x01,
+       .bcdADC =               __cpu_to_le16(0x0100),
+       .wTotalLength =         __cpu_to_le16(0x0009),
+       .bInCollection =        0x01,
+       .baInterfaceNr =        { 0x01 }, // to be filled
+};
+
+/* MIDI 1.0 Streaming Interface (altset 0) */
+static struct usb_interface_descriptor midi2_midi1_if_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     0, // to be filled
+       .bAlternateSetting =    0,
+       .bNumEndpoints =        2, // to be filled
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_MIDISTREAMING,
+       .bInterfaceProtocol =   0,
+       .iInterface =           0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi1_class_desc = {
+       .bLength =              0x07,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdMSC =               __cpu_to_le16(0x0100),
+       .wTotalLength =         __cpu_to_le16(0x41), // to be calculated
+};
+
+/* MIDI 1.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi1_ep_out_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_OUT | 0, // set up dynamically
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_out_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_out_class_desc = {
+       .bLength =              0x05, // to be filled
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       .bNumEmbMIDIJack =      1,
+       .baAssocJackID =        { 0x01 },
+};
+
+/* MIDI 1.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi1_ep_in_desc = {
+       .bLength =              USB_DT_ENDPOINT_AUDIO_SIZE,
+       .bDescriptorType =      USB_DT_ENDPOINT,
+       .bEndpointAddress =     USB_DIR_IN | 0, // set up dynamically
+       .bmAttributes =         USB_ENDPOINT_XFER_BULK,
+};
+
+static struct usb_ss_ep_comp_descriptor midi2_midi1_ep_in_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi1_ep_in_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms_endpoint_descriptor_16 midi2_midi1_ep_in_class_desc = {
+       .bLength =              0x05, // to be filled
+       .bDescriptorType =      USB_DT_CS_ENDPOINT,
+       .bDescriptorSubtype =   USB_MS_GENERAL,
+       .bNumEmbMIDIJack =      1,
+       .baAssocJackID =        { 0x03 },
+};
+
+/* MIDI 2.0 Streaming Interface (altset 1) */
+static struct usb_interface_descriptor midi2_midi2_if_desc = {
+       .bLength =              USB_DT_INTERFACE_SIZE,
+       .bDescriptorType =      USB_DT_INTERFACE,
+       .bInterfaceNumber =     0, // to be filled
+       .bAlternateSetting =    1,
+       .bNumEndpoints =        2, // to be filled
+       .bInterfaceClass =      USB_CLASS_AUDIO,
+       .bInterfaceSubClass =   USB_SUBCLASS_MIDISTREAMING,
+       .bInterfaceProtocol =   0,
+       .iInterface =           0, // to be filled
+};
+
+static struct usb_ms_header_descriptor midi2_midi2_class_desc = {
+       .bLength =              0x07,
+       .bDescriptorType =      USB_DT_CS_INTERFACE,
+       .bDescriptorSubtype =   USB_MS_HEADER,
+       .bcdMSC =               __cpu_to_le16(0x0200),
+       .wTotalLength =         __cpu_to_le16(0x07),
+};
+
+/* MIDI 2.0 EP OUT */
+static struct usb_endpoint_descriptor midi2_midi2_ep_out_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_out_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi1_ep_out_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_out_class_desc[MAX_UMP_EPS];
+
+/* MIDI 2.0 EP IN */
+static struct usb_endpoint_descriptor midi2_midi2_ep_in_desc[MAX_UMP_EPS];
+
+static struct usb_ss_ep_comp_descriptor midi2_midi2_ep_in_ss_comp_desc = {
+       .bLength                = sizeof(midi2_midi2_ep_in_ss_comp_desc),
+       .bDescriptorType        = USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_ms20_endpoint_descriptor_32 midi2_midi2_ep_in_class_desc[MAX_UMP_EPS];
+
+/* Arrays of descriptors to be created */
+static void *midi2_audio_descs[] = {
+       &midi2_audio_if_desc,
+       &midi2_audio_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_descs[] = {
+       &midi2_midi1_if_desc,
+       &midi2_midi1_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_out_descs[] = {
+       &midi2_midi1_ep_out_desc,
+       &midi2_midi1_ep_out_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_in_descs[] = {
+       &midi2_midi1_ep_in_desc,
+       &midi2_midi1_ep_in_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_out_ss_descs[] = {
+       &midi2_midi1_ep_out_desc,
+       &midi2_midi1_ep_out_ss_comp_desc,
+       &midi2_midi1_ep_out_class_desc,
+       NULL
+};
+
+static void *midi2_midi1_ep_in_ss_descs[] = {
+       &midi2_midi1_ep_in_desc,
+       &midi2_midi1_ep_in_ss_comp_desc,
+       &midi2_midi1_ep_in_class_desc,
+       NULL
+};
+
+static void *midi2_midi2_descs[] = {
+       &midi2_midi2_if_desc,
+       &midi2_midi2_class_desc,
+       NULL
+};
+
+/*
+ * USB request handling
+ */
+
+/* get an empty request for the given EP */
+static struct usb_request *get_empty_request(struct f_midi2_usb_ep *usb_ep)
+{
+       struct usb_request *req = NULL;
+       unsigned long flags;
+       int index;
+
+       spin_lock_irqsave(&usb_ep->card->queue_lock, flags);
+       if (!usb_ep->free_reqs)
+               goto unlock;
+       index = find_first_bit(&usb_ep->free_reqs, usb_ep->num_reqs);
+       if (index >= usb_ep->num_reqs)
+               goto unlock;
+       req = usb_ep->reqs[index].req;
+       if (!req)
+               goto unlock;
+       clear_bit(index, &usb_ep->free_reqs);
+       req->length = 0;
+ unlock:
+       spin_unlock_irqrestore(&usb_ep->card->queue_lock, flags);
+       return req;
+}
+
+/* put the empty request back */
+static void put_empty_request(struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ctx->usb_ep->card->queue_lock, flags);
+       set_bit(ctx->index, &ctx->usb_ep->free_reqs);
+       spin_unlock_irqrestore(&ctx->usb_ep->card->queue_lock, flags);
+}
+
+/*
+ * UMP v1.1 Stream message handling
+ */
+
+/* queue a request to UMP EP; request is either queued or freed after this */
+static int queue_request_ep_raw(struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       int err;
+
+       req->complete = ctx->usb_ep->complete;
+       err = usb_ep_queue(ctx->usb_ep->usb_ep, req, GFP_ATOMIC);
+       if (err) {
+               put_empty_request(req);
+               return err;
+       }
+       return 0;
+}
+
+/* queue a request with endianness conversion */
+static int queue_request_ep_in(struct usb_request *req)
+{
+       /* UMP packets have to be converted to little-endian */
+       cpu_to_le32_array((u32 *)req->buf, req->length >> 2);
+       return queue_request_ep_raw(req);
+}
+
+/* reply a UMP packet via EP-in */
+static int reply_ep_in(struct f_midi2_ep *ep, const void *buf, int len)
+{
+       struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+       struct usb_request *req;
+
+       req = get_empty_request(usb_ep);
+       if (!req)
+               return -ENOSPC;
+
+       req->length = len;
+       memcpy(req->buf, buf, len);
+       return queue_request_ep_in(req);
+}
+
+/* reply a UMP stream EP info */
+static void reply_ump_stream_ep_info(struct f_midi2_ep *ep)
+{
+       struct snd_ump_stream_msg_ep_info rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_EP_INFO,
+               .ump_version_major = 0x01,
+               .ump_version_minor = 0x01,
+               .num_function_blocks = ep->num_blks,
+               .static_function_block = !!ep->card->info.static_block,
+               .protocol = (UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 |
+                            UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) >> 8,
+       };
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* reply a UMP EP device info */
+static void reply_ump_stream_ep_device(struct f_midi2_ep *ep)
+{
+       struct snd_ump_stream_msg_devince_info rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_DEVICE_INFO,
+               .manufacture_id = ep->info.manufacturer,
+               .family_lsb = ep->info.family & 0xff,
+               .family_msb = (ep->info.family >> 8) & 0xff,
+               .model_lsb = ep->info.model & 0xff,
+               .model_msb = (ep->info.model >> 8) & 0xff,
+               .sw_revision = ep->info.sw_revision,
+       };
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+#define UMP_STREAM_PKT_BYTES   16      /* UMP stream packet size = 16 bytes*/
+#define UMP_STREAM_EP_STR_OFF  2       /* offset of name string for EP info */
+#define UMP_STREAM_FB_STR_OFF  3       /* offset of name string for FB info */
+
+/* Helper to replay a string */
+static void reply_ump_stream_string(struct f_midi2_ep *ep, const u8 *name,
+                                   unsigned int type, unsigned int extra,
+                                   unsigned int start_ofs)
+{
+       struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+       struct f_midi2 *midi2 = ep->card;
+       struct usb_request *req;
+       unsigned int pos;
+       u32 *buf;
+
+       if (!*name)
+               return;
+       req = get_empty_request(usb_ep);
+       if (!req)
+               return;
+
+       buf = (u32 *)req->buf;
+       pos = start_ofs;
+       for (;;) {
+               if (pos == start_ofs) {
+                       memset(buf, 0, UMP_STREAM_PKT_BYTES);
+                       buf[0] = ump_stream_compose(type, 0) | extra;
+               }
+               buf[pos / 4] |= *name++ << ((3 - (pos % 4)) * 8);
+               if (!*name) {
+                       if (req->length)
+                               buf[0] |= UMP_STREAM_MSG_FORMAT_END << 26;
+                       req->length += UMP_STREAM_PKT_BYTES;
+                       break;
+               }
+               if (++pos == UMP_STREAM_PKT_BYTES) {
+                       if (!req->length)
+                               buf[0] |= UMP_STREAM_MSG_FORMAT_START << 26;
+                       else
+                               buf[0] |= UMP_STREAM_MSG_FORMAT_CONTINUE << 26;
+                       req->length += UMP_STREAM_PKT_BYTES;
+                       if (midi2->info.req_buf_size - req->length < UMP_STREAM_PKT_BYTES)
+                               break;
+                       buf += 4;
+                       pos = start_ofs;
+               }
+       }
+
+       if (req->length)
+               queue_request_ep_in(req);
+       else
+               put_empty_request(req);
+}
+
+/* Reply a UMP EP name string */
+static void reply_ump_stream_ep_name(struct f_midi2_ep *ep)
+{
+       reply_ump_stream_string(ep, ump_ep_name(ep),
+                               UMP_STREAM_MSG_STATUS_EP_NAME, 0,
+                               UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP product ID string */
+static void reply_ump_stream_ep_pid(struct f_midi2_ep *ep)
+{
+       reply_ump_stream_string(ep, ump_product_id(ep),
+                               UMP_STREAM_MSG_STATUS_PRODUCT_ID, 0,
+                               UMP_STREAM_EP_STR_OFF);
+}
+
+/* Reply a UMP EP stream config */
+static void reply_ump_stream_ep_config(struct f_midi2_ep *ep)
+{
+       struct snd_ump_stream_msg_stream_cfg rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_STREAM_CFG,
+       };
+
+       if ((ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI_MASK) ==
+           SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+               rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI2 >> 8;
+       else
+               rep.protocol = UMP_STREAM_MSG_EP_INFO_CAP_MIDI1 >> 8;
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a UMP FB info */
+static void reply_ump_stream_fb_info(struct f_midi2_ep *ep, int blk)
+{
+       struct f_midi2_block_info *b = &ep->blks[blk].info;
+       struct snd_ump_stream_msg_fb_info rep = {
+               .type = UMP_MSG_TYPE_STREAM,
+               .status = UMP_STREAM_MSG_STATUS_FB_INFO,
+               .active = !!b->active,
+               .function_block_id = blk,
+               .ui_hint = b->ui_hint,
+               .midi_10 = b->is_midi1,
+               .direction = b->direction,
+               .first_group = b->first_group,
+               .num_groups = b->num_groups,
+               .midi_ci_version = b->midi_ci_version,
+               .sysex8_streams = b->sysex8_streams,
+       };
+
+       reply_ep_in(ep, &rep, sizeof(rep));
+}
+
+/* Reply a FB name string */
+static void reply_ump_stream_fb_name(struct f_midi2_ep *ep, unsigned int blk)
+{
+       reply_ump_stream_string(ep, ump_fb_name(&ep->blks[blk].info),
+                               UMP_STREAM_MSG_STATUS_FB_NAME, blk << 8,
+                               UMP_STREAM_FB_STR_OFF);
+}
+
+/* Process a UMP Stream message */
+static void process_ump_stream_msg(struct f_midi2_ep *ep, const u32 *data)
+{
+       struct f_midi2 *midi2 = ep->card;
+       unsigned int format, status, blk;
+
+       format = ump_stream_message_format(*data);
+       status = ump_stream_message_status(*data);
+       switch (status) {
+       case UMP_STREAM_MSG_STATUS_EP_DISCOVERY:
+               if (format)
+                       return; // invalid
+               if (data[1] & UMP_STREAM_MSG_REQUEST_EP_INFO)
+                       reply_ump_stream_ep_info(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_DEVICE_INFO)
+                       reply_ump_stream_ep_device(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_EP_NAME)
+                       reply_ump_stream_ep_name(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_PRODUCT_ID)
+                       reply_ump_stream_ep_pid(ep);
+               if (data[1] & UMP_STREAM_MSG_REQUEST_STREAM_CFG)
+                       reply_ump_stream_ep_config(ep);
+               return;
+       case UMP_STREAM_MSG_STATUS_STREAM_CFG_REQUEST:
+               if (*data & UMP_STREAM_MSG_EP_INFO_CAP_MIDI2) {
+                       ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI2;
+                       DBG(midi2, "Switching Protocol to MIDI2\n");
+               } else {
+                       ep->info.protocol = SNDRV_UMP_EP_INFO_PROTO_MIDI1;
+                       DBG(midi2, "Switching Protocol to MIDI1\n");
+               }
+               snd_ump_switch_protocol(ep->ump, ep->info.protocol);
+               reply_ump_stream_ep_config(ep);
+               return;
+       case UMP_STREAM_MSG_STATUS_FB_DISCOVERY:
+               if (format)
+                       return; // invalid
+               blk = (*data >> 8) & 0xff;
+               if (blk >= ep->num_blks)
+                       return;
+               if (*data & UMP_STREAM_MSG_REQUEST_FB_INFO)
+                       reply_ump_stream_fb_info(ep, blk);
+               if (*data & UMP_STREAM_MSG_REQUEST_FB_NAME)
+                       reply_ump_stream_fb_name(ep, blk);
+               return;
+       }
+}
+
+/* Process UMP messages included in a USB request */
+static void process_ump(struct f_midi2_ep *ep, const struct usb_request *req)
+{
+       const u32 *data = (u32 *)req->buf;
+       int len = req->actual >> 2;
+       const u32 *in_buf = ep->ump->input_buf;
+
+       for (; len > 0; len--, data++) {
+               if (snd_ump_receive_ump_val(ep->ump, *data) <= 0)
+                       continue;
+               if (ump_message_type(*in_buf) == UMP_MSG_TYPE_STREAM)
+                       process_ump_stream_msg(ep, in_buf);
+       }
+}
+
+/*
+ * MIDI 2.0 UMP USB request handling
+ */
+
+/* complete handler for UMP EP-out requests */
+static void f_midi2_ep_out_complete(struct usb_ep *usb_ep,
+                                   struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2_ep *ep = ctx->usb_ep->ep;
+       struct f_midi2 *midi2 = ep->card;
+       int status = req->status;
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               goto error;
+       }
+
+       /* convert to UMP packet in native endianness */
+       le32_to_cpu_array((u32 *)req->buf, req->actual >> 2);
+
+       if (midi2->info.process_ump)
+               process_ump(ep, req);
+
+       snd_ump_receive(ep->ump, req->buf, req->actual & ~3);
+
+       if (midi2->operation_mode != MIDI_OP_MODE_MIDI2)
+               goto error;
+
+       if (queue_request_ep_raw(req))
+               goto error;
+       return;
+
+ error:
+       put_empty_request(req);
+}
+
+/* Transmit UMP packets received from user-space to the gadget */
+static void process_ump_transmit(struct f_midi2_ep *ep)
+{
+       struct f_midi2_usb_ep *usb_ep = &ep->ep_in;
+       struct f_midi2 *midi2 = ep->card;
+       struct usb_request *req;
+       int len;
+
+       if (!usb_ep->usb_ep->enabled)
+               return;
+
+       for (;;) {
+               req = get_empty_request(usb_ep);
+               if (!req)
+                       break;
+               len = snd_ump_transmit(ep->ump, (u32 *)req->buf,
+                                      midi2->info.req_buf_size);
+               if (len <= 0) {
+                       put_empty_request(req);
+                       break;
+               }
+
+               req->length = len;
+               if (queue_request_ep_in(req) < 0)
+                       break;
+       }
+}
+
+/* Complete handler for UMP EP-in requests */
+static void f_midi2_ep_in_complete(struct usb_ep *usb_ep,
+                                  struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2_ep *ep = ctx->usb_ep->ep;
+       struct f_midi2 *midi2 = ep->card;
+       int status = req->status;
+
+       put_empty_request(req);
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               return;
+       }
+
+       process_ump_transmit(ep);
+}
+
+/*
+ * MIDI1 (altset 0) USB request handling
+ */
+
+/* process one MIDI byte -- copied from f_midi.c
+ *
+ * fill the packet or request if needed
+ * returns true if the request became empty (queued)
+ */
+static bool process_midi1_byte(struct f_midi2 *midi2, u8 cable, u8 b,
+                              struct usb_request **req_p)
+{
+       struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+       u8 p[4] = { cable << 4, 0, 0, 0 };
+       int next_state = STATE_INITIAL;
+       struct usb_request *req = *req_p;
+
+       switch (b) {
+       case 0xf8 ... 0xff:
+               /* System Real-Time Messages */
+               p[0] |= 0x0f;
+               p[1] = b;
+               next_state = port->state;
+               port->state = STATE_REAL_TIME;
+               break;
+
+       case 0xf7:
+               /* End of SysEx */
+               switch (port->state) {
+               case STATE_SYSEX_0:
+                       p[0] |= 0x05;
+                       p[1] = 0xf7;
+                       next_state = STATE_FINISHED;
+                       break;
+               case STATE_SYSEX_1:
+                       p[0] |= 0x06;
+                       p[1] = port->data[0];
+                       p[2] = 0xf7;
+                       next_state = STATE_FINISHED;
+                       break;
+               case STATE_SYSEX_2:
+                       p[0] |= 0x07;
+                       p[1] = port->data[0];
+                       p[2] = port->data[1];
+                       p[3] = 0xf7;
+                       next_state = STATE_FINISHED;
+                       break;
+               default:
+                       /* Ignore byte */
+                       next_state = port->state;
+                       port->state = STATE_INITIAL;
+               }
+               break;
+
+       case 0xf0 ... 0xf6:
+               /* System Common Messages */
+               port->data[0] = port->data[1] = 0;
+               port->state = STATE_INITIAL;
+               switch (b) {
+               case 0xf0:
+                       port->data[0] = b;
+                       port->data[1] = 0;
+                       next_state = STATE_SYSEX_1;
+                       break;
+               case 0xf1:
+               case 0xf3:
+                       port->data[0] = b;
+                       next_state = STATE_1PARAM;
+                       break;
+               case 0xf2:
+                       port->data[0] = b;
+                       next_state = STATE_2PARAM_1;
+                       break;
+               case 0xf4:
+               case 0xf5:
+                       next_state = STATE_INITIAL;
+                       break;
+               case 0xf6:
+                       p[0] |= 0x05;
+                       p[1] = 0xf6;
+                       next_state = STATE_FINISHED;
+                       break;
+               }
+               break;
+
+       case 0x80 ... 0xef:
+               /*
+                * Channel Voice Messages, Channel Mode Messages
+                * and Control Change Messages.
+                */
+               port->data[0] = b;
+               port->data[1] = 0;
+               port->state = STATE_INITIAL;
+               if (b >= 0xc0 && b <= 0xdf)
+                       next_state = STATE_1PARAM;
+               else
+                       next_state = STATE_2PARAM_1;
+               break;
+
+       case 0x00 ... 0x7f:
+               /* Message parameters */
+               switch (port->state) {
+               case STATE_1PARAM:
+                       if (port->data[0] < 0xf0)
+                               p[0] |= port->data[0] >> 4;
+                       else
+                               p[0] |= 0x02;
+
+                       p[1] = port->data[0];
+                       p[2] = b;
+                       /* This is to allow Running State Messages */
+                       next_state = STATE_1PARAM;
+                       break;
+               case STATE_2PARAM_1:
+                       port->data[1] = b;
+                       next_state = STATE_2PARAM_2;
+                       break;
+               case STATE_2PARAM_2:
+                       if (port->data[0] < 0xf0)
+                               p[0] |= port->data[0] >> 4;
+                       else
+                               p[0] |= 0x03;
+
+                       p[1] = port->data[0];
+                       p[2] = port->data[1];
+                       p[3] = b;
+                       /* This is to allow Running State Messages */
+                       next_state = STATE_2PARAM_1;
+                       break;
+               case STATE_SYSEX_0:
+                       port->data[0] = b;
+                       next_state = STATE_SYSEX_1;
+                       break;
+               case STATE_SYSEX_1:
+                       port->data[1] = b;
+                       next_state = STATE_SYSEX_2;
+                       break;
+               case STATE_SYSEX_2:
+                       p[0] |= 0x04;
+                       p[1] = port->data[0];
+                       p[2] = port->data[1];
+                       p[3] = b;
+                       next_state = STATE_SYSEX_0;
+                       break;
+               }
+               break;
+       }
+
+       /* States where we have to write into the USB request */
+       if (next_state == STATE_FINISHED ||
+           port->state == STATE_SYSEX_2 ||
+           port->state == STATE_1PARAM ||
+           port->state == STATE_2PARAM_2 ||
+           port->state == STATE_REAL_TIME) {
+               memcpy(req->buf + req->length, p, sizeof(p));
+               req->length += sizeof(p);
+
+               if (next_state == STATE_FINISHED) {
+                       next_state = STATE_INITIAL;
+                       port->data[0] = port->data[1] = 0;
+               }
+
+               if (midi2->info.req_buf_size - req->length <= 4) {
+                       queue_request_ep_raw(req);
+                       *req_p = NULL;
+                       return true;
+               }
+       }
+
+       port->state = next_state;
+       return false;
+}
+
+/* process all pending MIDI bytes in the internal buffer;
+ * returns true if the request gets empty
+ * returns false if all have been processed
+ */
+static bool process_midi1_pending_buf(struct f_midi2 *midi2,
+                                     struct usb_request **req_p)
+{
+       unsigned int cable, c;
+
+       for (cable = 0; cable < midi2->num_midi1_in; cable++) {
+               struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+
+               if (!port->pending)
+                       continue;
+               for (c = 0; c < port->pending; c++) {
+                       if (process_midi1_byte(midi2, cable, port->buf[c],
+                                              req_p)) {
+                               port->pending -= c;
+                               if (port->pending)
+                                       memmove(port->buf, port->buf + c,
+                                               port->pending);
+                               return true;
+                       }
+               }
+               port->pending = 0;
+       }
+
+       return false;
+}
+
+/* fill the MIDI bytes onto the temporary buffer
+ */
+static void fill_midi1_pending_buf(struct f_midi2 *midi2, u8 cable, u8 *buf,
+                                  unsigned int size)
+{
+       struct f_midi2_midi1_port *port = &midi2->midi1_port[cable];
+
+       if (port->pending + size > sizeof(port->buf))
+               return;
+       memcpy(port->buf + port->pending, buf, size);
+       port->pending += size;
+}
+
+/* try to process data given from the associated UMP stream */
+static void process_midi1_transmit(struct f_midi2 *midi2)
+{
+       struct f_midi2_usb_ep *usb_ep = &midi2->midi1_ep_in;
+       struct f_midi2_ep *ep = &midi2->midi2_eps[0];
+       struct usb_request *req = NULL;
+       /* 12 is the largest outcome (4 MIDI1 cmds) for a single UMP packet */
+       unsigned char outbuf[12];
+       unsigned char group, cable;
+       int len, size;
+       u32 ump;
+
+       if (!usb_ep->usb_ep || !usb_ep->usb_ep->enabled)
+               return;
+
+       for (;;) {
+               if (!req) {
+                       req = get_empty_request(usb_ep);
+                       if (!req)
+                               break;
+               }
+
+               if (process_midi1_pending_buf(midi2, &req))
+                       continue;
+
+               len = snd_ump_transmit(ep->ump, &ump, 4);
+               if (len <= 0)
+                       break;
+               if (snd_ump_receive_ump_val(ep->ump, ump) <= 0)
+                       continue;
+               size = snd_ump_convert_from_ump(ep->ump->input_buf, outbuf,
+                                               &group);
+               if (size <= 0)
+                       continue;
+               cable = ep->in_group_to_cable[group];
+               if (!cable)
+                       continue;
+               cable--; /* to 0-base */
+               fill_midi1_pending_buf(midi2, cable, outbuf, size);
+       }
+
+       if (req) {
+               if (req->length)
+                       queue_request_ep_raw(req);
+               else
+                       put_empty_request(req);
+       }
+}
+
+/* complete handler for MIDI1 EP-in requests */
+static void f_midi2_midi1_ep_in_complete(struct usb_ep *usb_ep,
+                                        struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2 *midi2 = ctx->usb_ep->card;
+       int status = req->status;
+
+       put_empty_request(req);
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               return;
+       }
+
+       process_midi1_transmit(midi2);
+}
+
+/* complete handler for MIDI1 EP-out requests */
+static void f_midi2_midi1_ep_out_complete(struct usb_ep *usb_ep,
+                                         struct usb_request *req)
+{
+       struct f_midi2_req_ctx *ctx = req->context;
+       struct f_midi2 *midi2 = ctx->usb_ep->card;
+       struct f_midi2_ep *ep;
+       struct ump_cvt_to_ump *cvt = &midi2->midi1_ump_cvt;
+       static const u8 midi1_packet_bytes[16] = {
+               0, 0, 2, 3, 3, 1, 2, 3, 3, 3, 3, 3, 2, 2, 3, 1
+       };
+       unsigned int group, cable, bytes, c, len;
+       int status = req->status;
+       const u8 *buf = req->buf;
+
+       if (status) {
+               DBG(midi2, "%s complete error %d: %d/%d\n",
+                   usb_ep->name, status, req->actual, req->length);
+               goto error;
+       }
+
+       len = req->actual >> 2;
+       for (; len; len--, buf += 4) {
+               cable = *buf >> 4;
+               ep = midi2->out_cable_mapping[cable].ep;
+               if (!ep)
+                       continue;
+               group = midi2->out_cable_mapping[cable].group;
+               bytes = midi1_packet_bytes[*buf & 0x0f];
+               for (c = 0; c < bytes; c++) {
+                       snd_ump_convert_to_ump(cvt, group, ep->info.protocol,
+                                              buf[c + 1]);
+                       if (cvt->ump_bytes) {
+                               snd_ump_receive(ep->ump, cvt->ump,
+                                               cvt->ump_bytes);
+                               cvt->ump_bytes = 0;
+                       }
+               }
+       }
+
+       if (midi2->operation_mode != MIDI_OP_MODE_MIDI1)
+               goto error;
+
+       if (queue_request_ep_raw(req))
+               goto error;
+       return;
+
+ error:
+       put_empty_request(req);
+}
+
+/*
+ * Common EP handling helpers
+ */
+
+/* Start MIDI EP */
+static int f_midi2_start_ep(struct f_midi2_usb_ep *usb_ep,
+                           struct usb_function *fn)
+{
+       int err;
+
+       if (!usb_ep->usb_ep)
+               return 0;
+
+       usb_ep_disable(usb_ep->usb_ep);
+       err = config_ep_by_speed(usb_ep->card->gadget, fn, usb_ep->usb_ep);
+       if (err)
+               return err;
+       return usb_ep_enable(usb_ep->usb_ep);
+}
+
+/* Drop pending requests */
+static void f_midi2_drop_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       int i;
+
+       if (!usb_ep->usb_ep || !usb_ep->num_reqs)
+               return;
+
+       for (i = 0; i < usb_ep->num_reqs; i++) {
+               if (!test_bit(i, &usb_ep->free_reqs) && usb_ep->reqs[i].req) {
+                       usb_ep_dequeue(usb_ep->usb_ep, usb_ep->reqs[i].req);
+                       set_bit(i, &usb_ep->free_reqs);
+               }
+       }
+}
+
+/* Allocate requests for the given EP */
+static int f_midi2_alloc_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       struct f_midi2 *midi2 = usb_ep->card;
+       int i;
+
+       if (!usb_ep->usb_ep)
+               return 0;
+       if (!usb_ep->reqs)
+               return -EINVAL;
+
+       for (i = 0; i < midi2->info.num_reqs; i++) {
+               if (usb_ep->reqs[i].req)
+                       continue;
+               usb_ep->reqs[i].req = alloc_ep_req(usb_ep->usb_ep,
+                                                  midi2->info.req_buf_size);
+               if (!usb_ep->reqs[i].req)
+                       return -ENOMEM;
+               usb_ep->reqs[i].req->context = &usb_ep->reqs[i];
+       }
+       return 0;
+}
+
+/* Free allocated requests */
+static void f_midi2_free_ep_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       struct f_midi2 *midi2 = usb_ep->card;
+       int i;
+
+       for (i = 0; i < midi2->info.num_reqs; i++) {
+               if (!usb_ep->reqs[i].req)
+                       continue;
+               free_ep_req(usb_ep->usb_ep, usb_ep->reqs[i].req);
+               usb_ep->reqs[i].req = NULL;
+       }
+}
+
+/* Initialize EP */
+static int f_midi2_init_ep(struct f_midi2 *midi2, struct f_midi2_ep *ep,
+                          struct f_midi2_usb_ep *usb_ep,
+                          void *desc,
+                          void (*complete)(struct usb_ep *usb_ep,
+                                           struct usb_request *req))
+{
+       int i;
+
+       usb_ep->card = midi2;
+       usb_ep->ep = ep;
+       usb_ep->usb_ep = usb_ep_autoconfig(midi2->gadget, desc);
+       if (!usb_ep->usb_ep)
+               return -ENODEV;
+       usb_ep->complete = complete;
+
+       usb_ep->reqs = kcalloc(midi2->info.num_reqs, sizeof(*usb_ep->reqs),
+                              GFP_KERNEL);
+       if (!usb_ep->reqs)
+               return -ENOMEM;
+       for (i = 0; i < midi2->info.num_reqs; i++) {
+               usb_ep->reqs[i].index = i;
+               usb_ep->reqs[i].usb_ep = usb_ep;
+               set_bit(i, &usb_ep->free_reqs);
+               usb_ep->num_reqs++;
+       }
+
+       return 0;
+}
+
+/* Free EP */
+static void f_midi2_free_ep(struct f_midi2_usb_ep *usb_ep)
+{
+       f_midi2_drop_reqs(usb_ep);
+
+       f_midi2_free_ep_reqs(usb_ep);
+
+       kfree(usb_ep->reqs);
+       usb_ep->num_reqs = 0;
+       usb_ep->free_reqs = 0;
+       usb_ep->reqs = NULL;
+}
+
+/* Queue requests for EP-out at start */
+static void f_midi2_queue_out_reqs(struct f_midi2_usb_ep *usb_ep)
+{
+       int i, err;
+
+       if (!usb_ep->usb_ep)
+               return;
+
+       for (i = 0; i < usb_ep->num_reqs; i++) {
+               if (!test_bit(i, &usb_ep->free_reqs) || !usb_ep->reqs[i].req)
+                       continue;
+               usb_ep->reqs[i].req->complete = usb_ep->complete;
+               err = usb_ep_queue(usb_ep->usb_ep, usb_ep->reqs[i].req,
+                                  GFP_ATOMIC);
+               if (!err)
+                       clear_bit(i, &usb_ep->free_reqs);
+       }
+}
+
+/*
+ * Gadget Function callbacks
+ */
+
+/* stop both IN and OUT EPs */
+static void f_midi2_stop_eps(struct f_midi2_usb_ep *ep_in,
+                            struct f_midi2_usb_ep *ep_out)
+{
+       f_midi2_drop_reqs(ep_in);
+       f_midi2_drop_reqs(ep_out);
+       f_midi2_free_ep_reqs(ep_in);
+       f_midi2_free_ep_reqs(ep_out);
+}
+
+/* start/queue both IN and OUT EPs */
+static int f_midi2_start_eps(struct f_midi2_usb_ep *ep_in,
+                            struct f_midi2_usb_ep *ep_out,
+                            struct usb_function *fn)
+{
+       int err;
+
+       err = f_midi2_start_ep(ep_in, fn);
+       if (err)
+               return err;
+       err = f_midi2_start_ep(ep_out, fn);
+       if (err)
+               return err;
+
+       err = f_midi2_alloc_ep_reqs(ep_in);
+       if (err)
+               return err;
+       err = f_midi2_alloc_ep_reqs(ep_out);
+       if (err)
+               return err;
+
+       f_midi2_queue_out_reqs(ep_out);
+       return 0;
+}
+
+/* gadget function set_alt callback */
+static int f_midi2_set_alt(struct usb_function *fn, unsigned int intf,
+                          unsigned int alt)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+       struct f_midi2_ep *ep;
+       int i, op_mode, err;
+
+       if (intf != midi2->midi_if || alt > 1)
+               return 0;
+
+       if (alt == 0)
+               op_mode = MIDI_OP_MODE_MIDI1;
+       else if (alt == 1)
+               op_mode = MIDI_OP_MODE_MIDI2;
+       else
+               op_mode = MIDI_OP_MODE_UNSET;
+
+       if (midi2->operation_mode == op_mode)
+               return 0;
+
+       midi2->operation_mode = op_mode;
+
+       if (op_mode != MIDI_OP_MODE_MIDI1)
+               f_midi2_stop_eps(&midi2->midi1_ep_in, &midi2->midi1_ep_out);
+
+       if (op_mode != MIDI_OP_MODE_MIDI2) {
+               for (i = 0; i < midi2->num_eps; i++) {
+                       ep = &midi2->midi2_eps[i];
+                       f_midi2_stop_eps(&ep->ep_in, &ep->ep_out);
+               }
+       }
+
+       if (op_mode == MIDI_OP_MODE_MIDI1)
+               return f_midi2_start_eps(&midi2->midi1_ep_in,
+                                        &midi2->midi1_ep_out, fn);
+
+       if (op_mode == MIDI_OP_MODE_MIDI2) {
+               for (i = 0; i < midi2->num_eps; i++) {
+                       ep = &midi2->midi2_eps[i];
+
+                       err = f_midi2_start_eps(&ep->ep_in, &ep->ep_out, fn);
+                       if (err)
+                               return err;
+               }
+       }
+
+       return 0;
+}
+
+/* gadget function get_alt callback */
+static int f_midi2_get_alt(struct usb_function *fn, unsigned int intf)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+
+       if (intf == midi2->midi_if &&
+           midi2->operation_mode == MIDI_OP_MODE_MIDI2)
+               return 1;
+       return 0;
+}
+
+/* convert UMP direction to USB MIDI 2.0 direction */
+static unsigned int ump_to_usb_dir(unsigned int ump_dir)
+{
+       switch (ump_dir) {
+       case SNDRV_UMP_DIR_INPUT:
+               return USB_MS_GR_TRM_BLOCK_TYPE_INPUT_ONLY;
+       case SNDRV_UMP_DIR_OUTPUT:
+               return USB_MS_GR_TRM_BLOCK_TYPE_OUTPUT_ONLY;
+       default:
+               return USB_MS_GR_TRM_BLOCK_TYPE_BIDIRECTIONAL;
+       }
+}
+
+/* assign GTB descriptors (for the given request) */
+static void assign_block_descriptors(struct f_midi2 *midi2,
+                                    struct usb_request *req,
+                                    int max_len)
+{
+       struct usb_ms20_gr_trm_block_header_descriptor header;
+       struct usb_ms20_gr_trm_block_descriptor *desc;
+       struct f_midi2_block_info *b;
+       struct f_midi2_ep *ep;
+       int i, blk, len;
+       char *data;
+
+       len = sizeof(gtb_header_desc) + sizeof(gtb_desc) * midi2->total_blocks;
+       if (WARN_ON(len > midi2->info.req_buf_size))
+               return;
+
+       header = gtb_header_desc;
+       header.wTotalLength = cpu_to_le16(len);
+       if (max_len < len) {
+               len = min_t(int, len, sizeof(header));
+               memcpy(req->buf, &header, len);
+               req->length = len;
+               req->zero = len < max_len;
+               return;
+       }
+
+       memcpy(req->buf, &header, sizeof(header));
+       data = req->buf + sizeof(header);
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               for (blk = 0; blk < ep->num_blks; blk++) {
+                       b = &ep->blks[blk].info;
+                       desc = (struct usb_ms20_gr_trm_block_descriptor *)data;
+
+                       *desc = gtb_desc;
+                       desc->bGrpTrmBlkID = ep->blks[blk].gtb_id;
+                       desc->bGrpTrmBlkType = ump_to_usb_dir(b->direction);
+                       desc->nGroupTrm = b->first_group;
+                       desc->nNumGroupTrm = b->num_groups;
+                       desc->iBlockItem = ep->blks[blk].string_id;
+
+                       if (ep->info.protocol & SNDRV_UMP_EP_INFO_PROTO_MIDI2)
+                               desc->bMIDIProtocol = USB_MS_MIDI_PROTO_2_0;
+                       else
+                               desc->bMIDIProtocol = USB_MS_MIDI_PROTO_1_0_128;
+
+                       if (b->is_midi1 == 2) {
+                               desc->wMaxInputBandwidth = cpu_to_le16(1);
+                               desc->wMaxOutputBandwidth = cpu_to_le16(1);
+                       }
+
+                       data += sizeof(*desc);
+               }
+       }
+
+       req->length = len;
+       req->zero = len < max_len;
+}
+
+/* gadget function setup callback: handle GTB requests */
+static int f_midi2_setup(struct usb_function *fn,
+                        const struct usb_ctrlrequest *ctrl)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+       struct usb_composite_dev *cdev = fn->config->cdev;
+       struct usb_request *req = cdev->req;
+       u16 value, length;
+
+       if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD ||
+           ctrl->bRequest != USB_REQ_GET_DESCRIPTOR)
+               return -EOPNOTSUPP;
+
+       value = le16_to_cpu(ctrl->wValue);
+       length = le16_to_cpu(ctrl->wLength);
+
+       if ((value >> 8) != USB_DT_CS_GR_TRM_BLOCK)
+               return -EOPNOTSUPP;
+
+       /* handle only altset 1 */
+       if ((value & 0xff) != 1)
+               return -EOPNOTSUPP;
+
+       assign_block_descriptors(midi2, req, length);
+       return usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+}
+
+/* gadget function disable callback */
+static void f_midi2_disable(struct usb_function *fn)
+{
+       struct f_midi2 *midi2 = func_to_midi2(fn);
+
+       midi2->operation_mode = MIDI_OP_MODE_UNSET;
+}
+
+/*
+ * ALSA UMP ops: most of them are NOPs, only trigger for write is needed
+ */
+static int f_midi2_ump_open(struct snd_ump_endpoint *ump, int dir)
+{
+       return 0;
+}
+
+static void f_midi2_ump_close(struct snd_ump_endpoint *ump, int dir)
+{
+}
+
+static void f_midi2_ump_trigger(struct snd_ump_endpoint *ump, int dir, int up)
+{
+       struct f_midi2_ep *ep = ump->private_data;
+       struct f_midi2 *midi2 = ep->card;
+
+       if (up && dir == SNDRV_RAWMIDI_STREAM_OUTPUT) {
+               switch (midi2->operation_mode) {
+               case MIDI_OP_MODE_MIDI1:
+                       process_midi1_transmit(midi2);
+                       break;
+               case MIDI_OP_MODE_MIDI2:
+                       process_ump_transmit(ep);
+                       break;
+               }
+       }
+}
+
+static void f_midi2_ump_drain(struct snd_ump_endpoint *ump, int dir)
+{
+}
+
+static const struct snd_ump_ops f_midi2_ump_ops = {
+       .open = f_midi2_ump_open,
+       .close = f_midi2_ump_close,
+       .trigger = f_midi2_ump_trigger,
+       .drain = f_midi2_ump_drain,
+};
+
+/*
+ * "Operation Mode" control element
+ */
+static int f_midi2_operation_mode_info(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = MIDI_OP_MODE_UNSET;
+       uinfo->value.integer.max = MIDI_OP_MODE_MIDI2;
+       return 0;
+}
+
+static int f_midi2_operation_mode_get(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct f_midi2 *midi2 = snd_kcontrol_chip(kcontrol);
+
+       ucontrol->value.integer.value[0] = midi2->operation_mode;
+       return 0;
+}
+
+static const struct snd_kcontrol_new operation_mode_ctl = {
+       .iface = SNDRV_CTL_ELEM_IFACE_RAWMIDI,
+       .name = "Operation Mode",
+       .access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+       .info = f_midi2_operation_mode_info,
+       .get = f_midi2_operation_mode_get,
+};
+
+/*
+ * ALSA UMP instance creation / deletion
+ */
+static void f_midi2_free_card(struct f_midi2 *midi2)
+{
+       if (midi2->card) {
+               snd_card_free_when_closed(midi2->card);
+               midi2->card = NULL;
+       }
+}
+
+/* use a reverse direction for the gadget host */
+static int reverse_dir(int dir)
+{
+       if (!dir || dir == SNDRV_UMP_DIR_BIDIRECTION)
+               return dir;
+       return (dir == SNDRV_UMP_DIR_OUTPUT) ?
+               SNDRV_UMP_DIR_INPUT : SNDRV_UMP_DIR_OUTPUT;
+}
+
+static int f_midi2_create_card(struct f_midi2 *midi2)
+{
+       struct snd_card *card;
+       struct snd_ump_endpoint *ump;
+       struct f_midi2_ep *ep;
+       int i, id, blk, err;
+       __be32 sw;
+
+       err = snd_card_new(&midi2->gadget->dev, -1, NULL, THIS_MODULE, 0,
+                          &card);
+       if (err < 0)
+               return err;
+       midi2->card = card;
+
+       strcpy(card->driver, "f_midi2");
+       strcpy(card->shortname, "MIDI 2.0 Gadget");
+       strcpy(card->longname, "MIDI 2.0 Gadget");
+
+       id = 0;
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               err = snd_ump_endpoint_new(card, "MIDI 2.0 Gadget", id,
+                                          1, 1, &ump);
+               if (err < 0)
+                       goto error;
+               id++;
+
+               ep->ump = ump;
+               ump->no_process_stream = true;
+               ump->private_data = ep;
+               ump->ops = &f_midi2_ump_ops;
+               if (midi2->info.static_block)
+                       ump->info.flags |= SNDRV_UMP_EP_INFO_STATIC_BLOCKS;
+               ump->info.protocol_caps = (ep->info.protocol_caps & 3) << 8;
+               ump->info.protocol = (ep->info.protocol & 3) << 8;
+               ump->info.version = 0x0101;
+               ump->info.family_id = ep->info.family;
+               ump->info.model_id = ep->info.model;
+               ump->info.manufacturer_id = ep->info.manufacturer & 0xffffff;
+               sw = cpu_to_be32(ep->info.sw_revision);
+               memcpy(ump->info.sw_revision, &sw, 4);
+
+               strscpy(ump->info.name, ump_ep_name(ep),
+                       sizeof(ump->info.name));
+               strscpy(ump->info.product_id, ump_product_id(ep),
+                       sizeof(ump->info.product_id));
+               strscpy(ump->core.name, ump->info.name, sizeof(ump->core.name));
+
+               for (blk = 0; blk < ep->num_blks; blk++) {
+                       const struct f_midi2_block_info *b = &ep->blks[blk].info;
+                       struct snd_ump_block *fb;
+
+                       err = snd_ump_block_new(ump, blk,
+                                               reverse_dir(b->direction),
+                                               b->first_group, b->num_groups,
+                                               &ep->blks[blk].fb);
+                       if (err < 0)
+                               goto error;
+                       fb = ep->blks[blk].fb;
+                       fb->info.active = !!b->active;
+                       fb->info.midi_ci_version = b->midi_ci_version;
+                       fb->info.ui_hint = reverse_dir(b->ui_hint);
+                       fb->info.sysex8_streams = b->sysex8_streams;
+                       fb->info.flags |= b->is_midi1;
+                       strscpy(fb->info.name, ump_fb_name(b),
+                               sizeof(fb->info.name));
+               }
+       }
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               err = snd_ump_attach_legacy_rawmidi(midi2->midi2_eps[i].ump,
+                                                   "Legacy MIDI", id);
+               if (err < 0)
+                       goto error;
+               id++;
+       }
+
+       err = snd_ctl_add(card, snd_ctl_new1(&operation_mode_ctl, midi2));
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       return 0;
+
+ error:
+       f_midi2_free_card(midi2);
+       return err;
+}
+
+/*
+ * Creation of USB descriptors
+ */
+struct f_midi2_usb_config {
+       struct usb_descriptor_header **list;
+       unsigned int size;
+       unsigned int alloc;
+
+       /* MIDI 1.0 jacks */
+       unsigned char jack_in, jack_out, jack_id;
+       struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES];
+       struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES];
+};
+
+static int append_config(struct f_midi2_usb_config *config, void *d)
+{
+       unsigned int size;
+       void *buf;
+
+       if (config->size + 2 >= config->alloc) {
+               size = config->size + 16;
+               buf = krealloc(config->list, size * sizeof(void *), GFP_KERNEL);
+               if (!buf)
+                       return -ENOMEM;
+               config->list = buf;
+               config->alloc = size;
+       }
+
+       config->list[config->size] = d;
+       config->size++;
+       config->list[config->size] = NULL;
+       return 0;
+}
+
+static int append_configs(struct f_midi2_usb_config *config, void **d)
+{
+       int err;
+
+       for (; *d; d++) {
+               err = append_config(config, *d);
+               if (err)
+                       return err;
+       }
+       return 0;
+}
+
+static int append_midi1_in_jack(struct f_midi2 *midi2,
+                               struct f_midi2_usb_config *config,
+                               struct midi1_cable_mapping *map,
+                               unsigned int type)
+{
+       struct usb_midi_in_jack_descriptor *jack =
+               &config->jack_ins[config->jack_in++];
+       int id = ++config->jack_id;
+       int err;
+
+       jack->bLength = 0x06;
+       jack->bDescriptorType = USB_DT_CS_INTERFACE;
+       jack->bDescriptorSubtype = USB_MS_MIDI_IN_JACK;
+       jack->bJackType = type;
+       jack->bJackID = id;
+       /* use the corresponding block name as jack name */
+       if (map->ep)
+               jack->iJack = map->ep->blks[map->block].string_id;
+
+       err = append_config(config, jack);
+       if (err < 0)
+               return err;
+       return id;
+}
+
+static int append_midi1_out_jack(struct f_midi2 *midi2,
+                                struct f_midi2_usb_config *config,
+                                struct midi1_cable_mapping *map,
+                                unsigned int type, unsigned int source)
+{
+       struct usb_midi_out_jack_descriptor_1 *jack =
+               &config->jack_outs[config->jack_out++];
+       int id = ++config->jack_id;
+       int err;
+
+       jack->bLength = 0x09;
+       jack->bDescriptorType = USB_DT_CS_INTERFACE;
+       jack->bDescriptorSubtype = USB_MS_MIDI_OUT_JACK;
+       jack->bJackType = type;
+       jack->bJackID = id;
+       jack->bNrInputPins = 1;
+       jack->pins[0].baSourceID = source;
+       jack->pins[0].baSourcePin = 0x01;
+       /* use the corresponding block name as jack name */
+       if (map->ep)
+               jack->iJack = map->ep->blks[map->block].string_id;
+
+       err = append_config(config, jack);
+       if (err < 0)
+               return err;
+       return id;
+}
+
+static int f_midi2_create_usb_configs(struct f_midi2 *midi2,
+                                     struct f_midi2_usb_config *config,
+                                     int speed)
+{
+       void **midi1_in_eps, **midi1_out_eps;
+       int i, jack, total;
+       int err;
+
+       switch (speed) {
+       default:
+       case USB_SPEED_HIGH:
+               midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(512);
+               midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(512);
+               for (i = 0; i < midi2->num_eps; i++)
+                       midi2_midi2_ep_out_desc[i].wMaxPacketSize =
+                               cpu_to_le16(512);
+               fallthrough;
+       case USB_SPEED_FULL:
+               midi1_in_eps = midi2_midi1_ep_in_descs;
+               midi1_out_eps = midi2_midi1_ep_out_descs;
+               break;
+       case USB_SPEED_SUPER:
+       case USB_SPEED_SUPER_PLUS:
+               midi2_midi1_ep_out_desc.wMaxPacketSize = cpu_to_le16(1024);
+               midi2_midi1_ep_in_desc.wMaxPacketSize = cpu_to_le16(1024);
+               for (i = 0; i < midi2->num_eps; i++)
+                       midi2_midi2_ep_out_desc[i].wMaxPacketSize =
+                               cpu_to_le16(1024);
+               midi1_in_eps = midi2_midi1_ep_in_ss_descs;
+               midi1_out_eps = midi2_midi1_ep_out_ss_descs;
+               break;
+       }
+
+       err = append_configs(config, midi2_audio_descs);
+       if (err < 0)
+               return err;
+
+       if (midi2->num_midi1_in && midi2->num_midi1_out)
+               midi2_midi1_if_desc.bNumEndpoints = 2;
+       else
+               midi2_midi1_if_desc.bNumEndpoints = 1;
+
+       err = append_configs(config, midi2_midi1_descs);
+       if (err < 0)
+               return err;
+
+       total = USB_DT_MS_HEADER_SIZE;
+       if (midi2->num_midi1_out) {
+               midi2_midi1_ep_out_class_desc.bLength =
+                       USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_out);
+               total += midi2_midi1_ep_out_class_desc.bLength;
+               midi2_midi1_ep_out_class_desc.bNumEmbMIDIJack =
+                       midi2->num_midi1_out;
+               total += midi2->num_midi1_out *
+                       (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+               for (i = 0; i < midi2->num_midi1_out; i++) {
+                       jack = append_midi1_in_jack(midi2, config,
+                                                   &midi2->in_cable_mapping[i],
+                                                   USB_MS_EMBEDDED);
+                       if (jack < 0)
+                               return jack;
+                       midi2_midi1_ep_out_class_desc.baAssocJackID[i] = jack;
+                       jack = append_midi1_out_jack(midi2, config,
+                                                    &midi2->in_cable_mapping[i],
+                                                    USB_MS_EXTERNAL, jack);
+                       if (jack < 0)
+                               return jack;
+               }
+       }
+
+       if (midi2->num_midi1_in) {
+               midi2_midi1_ep_in_class_desc.bLength =
+                       USB_DT_MS_ENDPOINT_SIZE(midi2->num_midi1_in);
+               total += midi2_midi1_ep_in_class_desc.bLength;
+               midi2_midi1_ep_in_class_desc.bNumEmbMIDIJack =
+                       midi2->num_midi1_in;
+               total += midi2->num_midi1_in *
+                       (USB_DT_MIDI_IN_SIZE + USB_DT_MIDI_OUT_SIZE(1));
+               for (i = 0; i < midi2->num_midi1_in; i++) {
+                       jack = append_midi1_in_jack(midi2, config,
+                                                   &midi2->out_cable_mapping[i],
+                                                   USB_MS_EXTERNAL);
+                       if (jack < 0)
+                               return jack;
+                       jack = append_midi1_out_jack(midi2, config,
+                                                    &midi2->out_cable_mapping[i],
+                                                    USB_MS_EMBEDDED, jack);
+                       if (jack < 0)
+                               return jack;
+                       midi2_midi1_ep_in_class_desc.baAssocJackID[i] = jack;
+               }
+       }
+
+       midi2_midi1_class_desc.wTotalLength = cpu_to_le16(total);
+
+       if (midi2->num_midi1_out) {
+               err = append_configs(config, midi1_out_eps);
+               if (err < 0)
+                       return err;
+       }
+       if (midi2->num_midi1_in) {
+               err = append_configs(config, midi1_in_eps);
+               if (err < 0)
+                       return err;
+       }
+
+       err = append_configs(config, midi2_midi2_descs);
+       if (err < 0)
+               return err;
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               err = append_config(config, &midi2_midi2_ep_out_desc[i]);
+               if (err < 0)
+                       return err;
+               if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+                       err = append_config(config, &midi2_midi2_ep_out_ss_comp_desc);
+                       if (err < 0)
+                               return err;
+               }
+               err = append_config(config, &midi2_midi2_ep_out_class_desc[i]);
+               if (err < 0)
+                       return err;
+               err = append_config(config, &midi2_midi2_ep_in_desc[i]);
+               if (err < 0)
+                       return err;
+               if (speed == USB_SPEED_SUPER || speed == USB_SPEED_SUPER_PLUS) {
+                       err = append_config(config, &midi2_midi2_ep_in_ss_comp_desc);
+                       if (err < 0)
+                               return err;
+               }
+               err = append_config(config, &midi2_midi2_ep_in_class_desc[i]);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static void f_midi2_free_usb_configs(struct f_midi2_usb_config *config)
+{
+       kfree(config->list);
+       memset(config, 0, sizeof(*config));
+}
+
+/* as we use the static descriptors for simplicity, serialize bind call */
+static DEFINE_MUTEX(f_midi2_desc_mutex);
+
+/* fill MIDI2 EP class-specific descriptor */
+static void fill_midi2_class_desc(struct f_midi2_ep *ep,
+                                 struct usb_ms20_endpoint_descriptor_32 *cdesc)
+{
+       int blk;
+
+       cdesc->bLength = USB_DT_MS20_ENDPOINT_SIZE(ep->num_blks);
+       cdesc->bDescriptorType = USB_DT_CS_ENDPOINT;
+       cdesc->bDescriptorSubtype = USB_MS_GENERAL_2_0;
+       cdesc->bNumGrpTrmBlock = ep->num_blks;
+       for (blk = 0; blk < ep->num_blks; blk++)
+               cdesc->baAssoGrpTrmBlkID[blk] = ep->blks[blk].gtb_id;
+}
+
+/* initialize MIDI2 EP-in */
+static int f_midi2_init_midi2_ep_in(struct f_midi2 *midi2, int index)
+{
+       struct f_midi2_ep *ep = &midi2->midi2_eps[index];
+       struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_in_desc[index];
+
+       desc->bLength = USB_DT_ENDPOINT_SIZE;
+       desc->bDescriptorType = USB_DT_ENDPOINT;
+       desc->bEndpointAddress = USB_DIR_IN;
+       desc->bmAttributes = USB_ENDPOINT_XFER_INT;
+       desc->wMaxPacketSize = cpu_to_le16(EP_MAX_PACKET_INT);
+       desc->bInterval = 1;
+
+       fill_midi2_class_desc(ep, &midi2_midi2_ep_in_class_desc[index]);
+
+       return f_midi2_init_ep(midi2, ep, &ep->ep_in, desc,
+                              f_midi2_ep_in_complete);
+}
+
+/* initialize MIDI2 EP-out */
+static int f_midi2_init_midi2_ep_out(struct f_midi2 *midi2, int index)
+{
+       struct f_midi2_ep *ep = &midi2->midi2_eps[index];
+       struct usb_endpoint_descriptor *desc = &midi2_midi2_ep_out_desc[index];
+
+       desc->bLength = USB_DT_ENDPOINT_SIZE;
+       desc->bDescriptorType = USB_DT_ENDPOINT;
+       desc->bEndpointAddress = USB_DIR_OUT;
+       desc->bmAttributes = USB_ENDPOINT_XFER_BULK;
+
+       fill_midi2_class_desc(ep, &midi2_midi2_ep_out_class_desc[index]);
+
+       return f_midi2_init_ep(midi2, ep, &ep->ep_out, desc,
+                              f_midi2_ep_out_complete);
+}
+
+/* gadget function bind callback */
+static int f_midi2_bind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct usb_composite_dev *cdev = c->cdev;
+       struct f_midi2 *midi2 = func_to_midi2(f);
+       struct f_midi2_ep *ep;
+       struct f_midi2_usb_config config = {};
+       struct usb_gadget_strings string_fn = {
+               .language = 0x0409,     /* en-us */
+               .strings = midi2->string_defs,
+       };
+       struct usb_gadget_strings *strings[] = {
+               &string_fn,
+               NULL,
+       };
+       int i, blk, status;
+
+       midi2->gadget = cdev->gadget;
+       midi2->operation_mode = MIDI_OP_MODE_UNSET;
+
+       status = f_midi2_create_card(midi2);
+       if (status < 0)
+               goto fail_register;
+
+       /* maybe allocate device-global string ID */
+       midi2->strings = usb_gstrings_attach(c->cdev, strings,
+                                            midi2->total_blocks + 1);
+       if (IS_ERR(midi2->strings)) {
+               status = PTR_ERR(midi2->strings);
+               goto fail_string;
+       }
+
+       mutex_lock(&f_midi2_desc_mutex);
+       midi2_midi1_if_desc.iInterface = midi2->strings[STR_IFACE].id;
+       midi2_midi2_if_desc.iInterface = midi2->strings[STR_IFACE].id;
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               for (blk = 0; blk < ep->num_blks; blk++)
+                       ep->blks[blk].string_id =
+                               midi2->strings[gtb_to_str_id(ep->blks[blk].gtb_id)].id;
+       }
+
+       midi2_midi2_if_desc.bNumEndpoints = midi2->num_eps * 2;
+
+       /* audio interface */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       midi2_audio_if_desc.bInterfaceNumber = status;
+
+       /* MIDI streaming */
+       status = usb_interface_id(c, f);
+       if (status < 0)
+               goto fail;
+       midi2->midi_if = status;
+       midi2_midi1_if_desc.bInterfaceNumber = status;
+       midi2_midi2_if_desc.bInterfaceNumber = status;
+       midi2_audio_class_desc.baInterfaceNr[0] = status;
+
+       /* allocate instance-specific endpoints */
+       if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_OUTPUT) {
+               status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_in,
+                                        &midi2_midi1_ep_in_desc,
+                                        f_midi2_midi1_ep_in_complete);
+               if (status)
+                       goto fail;
+       }
+
+       if (midi2->midi2_eps[0].blks[0].info.direction != SNDRV_UMP_DIR_INPUT) {
+               status = f_midi2_init_ep(midi2, NULL, &midi2->midi1_ep_out,
+                                        &midi2_midi1_ep_out_desc,
+                                        f_midi2_midi1_ep_out_complete);
+               if (status)
+                       goto fail;
+       }
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               status = f_midi2_init_midi2_ep_in(midi2, i);
+               if (status)
+                       goto fail;
+               status = f_midi2_init_midi2_ep_out(midi2, i);
+               if (status)
+                       goto fail;
+       }
+
+       status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_FULL);
+       if (status < 0)
+               goto fail;
+       f->fs_descriptors = usb_copy_descriptors(config.list);
+       if (!f->fs_descriptors) {
+               status = -ENOMEM;
+               goto fail;
+       }
+       f_midi2_free_usb_configs(&config);
+
+       if (gadget_is_dualspeed(midi2->gadget)) {
+               status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_HIGH);
+               if (status < 0)
+                       goto fail;
+               f->hs_descriptors = usb_copy_descriptors(config.list);
+               if (!f->hs_descriptors) {
+                       status = -ENOMEM;
+                       goto fail;
+               }
+               f_midi2_free_usb_configs(&config);
+       }
+
+       if (gadget_is_superspeed(midi2->gadget)) {
+               status = f_midi2_create_usb_configs(midi2, &config, USB_SPEED_SUPER);
+               if (status < 0)
+                       goto fail;
+               f->ss_descriptors = usb_copy_descriptors(config.list);
+               if (!f->ss_descriptors) {
+                       status = -ENOMEM;
+                       goto fail;
+               }
+               if (gadget_is_superspeed_plus(midi2->gadget)) {
+                       f->ssp_descriptors = usb_copy_descriptors(config.list);
+                       if (!f->ssp_descriptors) {
+                               status = -ENOMEM;
+                               goto fail;
+                       }
+               }
+               f_midi2_free_usb_configs(&config);
+       }
+
+       mutex_unlock(&f_midi2_desc_mutex);
+       return 0;
+
+fail:
+       f_midi2_free_usb_configs(&config);
+       mutex_unlock(&f_midi2_desc_mutex);
+       usb_free_all_descriptors(f);
+fail_string:
+       f_midi2_free_card(midi2);
+fail_register:
+       ERROR(midi2, "%s: can't bind, err %d\n", f->name, status);
+       return status;
+}
+
+/* gadget function unbind callback */
+static void f_midi2_unbind(struct usb_configuration *c, struct usb_function *f)
+{
+       struct f_midi2 *midi2 = func_to_midi2(f);
+       int i;
+
+       f_midi2_free_card(midi2);
+
+       f_midi2_free_ep(&midi2->midi1_ep_in);
+       f_midi2_free_ep(&midi2->midi1_ep_out);
+       for (i = 0; i < midi2->num_eps; i++) {
+               f_midi2_free_ep(&midi2->midi2_eps[i].ep_in);
+               f_midi2_free_ep(&midi2->midi2_eps[i].ep_out);
+       }
+
+       usb_free_all_descriptors(f);
+}
+
+/*
+ * ConfigFS interface
+ */
+
+/* type conversion helpers */
+static inline struct f_midi2_opts *to_f_midi2_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_midi2_opts,
+                           func_inst.group);
+}
+
+static inline struct f_midi2_ep_opts *
+to_f_midi2_ep_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_midi2_ep_opts,
+                           group);
+}
+
+static inline struct f_midi2_block_opts *
+to_f_midi2_block_opts(struct config_item *item)
+{
+       return container_of(to_config_group(item), struct f_midi2_block_opts,
+                           group);
+}
+
+/* trim the string to be usable for EP and FB name strings */
+static void make_name_string(char *s)
+{
+       char *p;
+
+       p = strchr(s, '\n');
+       if (p)
+               *p = 0;
+
+       p = s + strlen(s);
+       for (; p > s && isspace(*p); p--)
+               *p = 0;
+}
+
+/* configfs helpers: generic show/store for unisnged int */
+static ssize_t f_midi2_opts_uint_show(struct f_midi2_opts *opts,
+                                     u32 val, const char *format, char *page)
+{
+       int result;
+
+       mutex_lock(&opts->lock);
+       result = sprintf(page, format, val);
+       mutex_unlock(&opts->lock);
+       return result;
+}
+
+static ssize_t f_midi2_opts_uint_store(struct f_midi2_opts *opts,
+                                      u32 *valp, u32 minval, u32 maxval,
+                                      const char *page, size_t len)
+{
+       int ret;
+       u32 val;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtou32(page, 0, &val);
+       if (ret)
+               goto end;
+       if (val < minval || val > maxval) {
+               ret = -EINVAL;
+               goto end;
+       }
+
+       *valp = val;
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+/* generic store for bool */
+static ssize_t f_midi2_opts_bool_store(struct f_midi2_opts *opts,
+                                      bool *valp, const char *page, size_t len)
+{
+       int ret;
+       bool val;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       ret = kstrtobool(page, &val);
+       if (ret)
+               goto end;
+       *valp = val;
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+/* generic show/store for string */
+static ssize_t f_midi2_opts_str_show(struct f_midi2_opts *opts,
+                                    const char *str, char *page)
+{
+       int result = 0;
+
+       mutex_lock(&opts->lock);
+       if (str)
+               result = scnprintf(page, PAGE_SIZE, "%s\n", str);
+       mutex_unlock(&opts->lock);
+       return result;
+}
+
+static ssize_t f_midi2_opts_str_store(struct f_midi2_opts *opts,
+                                     const char **strp, size_t maxlen,
+                                     const char *page, size_t len)
+{
+       char *c;
+       int ret;
+
+       mutex_lock(&opts->lock);
+       if (opts->refcnt) {
+               ret = -EBUSY;
+               goto end;
+       }
+
+       c = kstrndup(page, min(len, maxlen), GFP_KERNEL);
+       if (!c) {
+               ret = -ENOMEM;
+               goto end;
+       }
+
+       kfree(*strp);
+       make_name_string(c);
+       *strp = c;
+       ret = len;
+
+end:
+       mutex_unlock(&opts->lock);
+       return ret;
+}
+
+/*
+ * Definitions for UMP Block config
+ */
+
+/* define an uint option for block */
+#define F_MIDI2_BLOCK_OPT(name, format, minval, maxval)                        \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+                                         char *page)                   \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name,  \
+                                     format "\n", page);               \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_uint_store(opts->ep->opts, &opts->info.name,\
+                                      minval, maxval, page, len);      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+/* define a boolean option for block */
+#define F_MIDI2_BLOCK_BOOL_OPT(name)                                   \
+static ssize_t f_midi2_block_opts_##name##_show(struct config_item *item,\
+                                         char *page)                   \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_uint_show(opts->ep->opts, opts->info.name,  \
+                                     "%u\n", page);                    \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_block_opts_##name##_store(struct config_item *item,\
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);  \
+       return f_midi2_opts_bool_store(opts->ep->opts, &opts->info.name,\
+                                      page, len);                      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_block_opts_, name)
+
+F_MIDI2_BLOCK_OPT(direction, "0x%x", 1, 3);
+F_MIDI2_BLOCK_OPT(first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(num_groups, "0x%x", 1, 16);
+F_MIDI2_BLOCK_OPT(midi1_first_group, "0x%x", 0, 15);
+F_MIDI2_BLOCK_OPT(midi1_num_groups, "0x%x", 0, 16);
+F_MIDI2_BLOCK_OPT(ui_hint, "0x%x", 0, 3);
+F_MIDI2_BLOCK_OPT(midi_ci_version, "%u", 0, 1);
+F_MIDI2_BLOCK_OPT(sysex8_streams, "%u", 0, 255);
+F_MIDI2_BLOCK_OPT(is_midi1, "%u", 0, 2);
+F_MIDI2_BLOCK_BOOL_OPT(active);
+
+static ssize_t f_midi2_block_opts_name_show(struct config_item *item,
+                                           char *page)
+{
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+       return f_midi2_opts_str_show(opts->ep->opts, opts->info.name, page);
+}
+
+static ssize_t f_midi2_block_opts_name_store(struct config_item *item,
+                                            const char *page, size_t len)
+{
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+       return f_midi2_opts_str_store(opts->ep->opts, &opts->info.name, 128,
+                                     page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_block_opts_, name);
+
+static struct configfs_attribute *f_midi2_block_attrs[] = {
+       &f_midi2_block_opts_attr_direction,
+       &f_midi2_block_opts_attr_first_group,
+       &f_midi2_block_opts_attr_num_groups,
+       &f_midi2_block_opts_attr_midi1_first_group,
+       &f_midi2_block_opts_attr_midi1_num_groups,
+       &f_midi2_block_opts_attr_ui_hint,
+       &f_midi2_block_opts_attr_midi_ci_version,
+       &f_midi2_block_opts_attr_sysex8_streams,
+       &f_midi2_block_opts_attr_is_midi1,
+       &f_midi2_block_opts_attr_active,
+       &f_midi2_block_opts_attr_name,
+       NULL,
+};
+
+static void f_midi2_block_opts_release(struct config_item *item)
+{
+       struct f_midi2_block_opts *opts = to_f_midi2_block_opts(item);
+
+       kfree(opts->info.name);
+       kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_block_item_ops = {
+       .release        = f_midi2_block_opts_release,
+};
+
+static const struct config_item_type f_midi2_block_type = {
+       .ct_item_ops    = &f_midi2_block_item_ops,
+       .ct_attrs       = f_midi2_block_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* create a f_midi2_block_opts instance for the given block number */
+static int f_midi2_block_opts_create(struct f_midi2_ep_opts *ep_opts,
+                                    unsigned int blk,
+                                    struct f_midi2_block_opts **block_p)
+{
+       struct f_midi2_block_opts *block_opts;
+       int ret = 0;
+
+       mutex_lock(&ep_opts->opts->lock);
+       if (ep_opts->opts->refcnt || ep_opts->blks[blk]) {
+               ret = -EBUSY;
+               goto out;
+       }
+
+       block_opts = kzalloc(sizeof(*block_opts), GFP_KERNEL);
+       if (!block_opts) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       block_opts->ep = ep_opts;
+       block_opts->id = blk;
+
+       /* set up the default values */
+       block_opts->info.direction = SNDRV_UMP_DIR_BIDIRECTION;
+       block_opts->info.first_group = 0;
+       block_opts->info.num_groups = 1;
+       block_opts->info.ui_hint = SNDRV_UMP_BLOCK_UI_HINT_BOTH;
+       block_opts->info.active = 1;
+
+       ep_opts->blks[blk] = block_opts;
+       *block_p = block_opts;
+
+ out:
+       mutex_unlock(&ep_opts->opts->lock);
+       return ret;
+}
+
+/* make_group callback for a block */
+static struct config_group *
+f_midi2_opts_block_make(struct config_group *group, const char *name)
+{
+       struct f_midi2_ep_opts *ep_opts;
+       struct f_midi2_block_opts *block_opts;
+       unsigned int blk;
+       int ret;
+
+       if (strncmp(name, "block.", 6))
+               return ERR_PTR(-EINVAL);
+       ret = kstrtouint(name + 6, 10, &blk);
+       if (ret)
+               return ERR_PTR(ret);
+
+       ep_opts = to_f_midi2_ep_opts(&group->cg_item);
+
+       if (blk >= SNDRV_UMP_MAX_BLOCKS)
+               return ERR_PTR(-EINVAL);
+       if (ep_opts->blks[blk])
+               return ERR_PTR(-EBUSY);
+       ret = f_midi2_block_opts_create(ep_opts, blk, &block_opts);
+       if (ret)
+               return ERR_PTR(ret);
+
+       config_group_init_type_name(&block_opts->group, name,
+                                   &f_midi2_block_type);
+       return &block_opts->group;
+}
+
+/* drop_item callback for a block */
+static void
+f_midi2_opts_block_drop(struct config_group *group, struct config_item *item)
+{
+       struct f_midi2_block_opts *block_opts = to_f_midi2_block_opts(item);
+
+       mutex_lock(&block_opts->ep->opts->lock);
+       block_opts->ep->blks[block_opts->id] = NULL;
+       mutex_unlock(&block_opts->ep->opts->lock);
+       config_item_put(item);
+}
+
+/*
+ * Definitions for UMP Endpoint config
+ */
+
+/* define an uint option for EP */
+#define F_MIDI2_EP_OPT(name, format, minval, maxval)                   \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+                                            char *page)                \
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_uint_show(opts->opts, opts->info.name,      \
+                                     format "\n", page);               \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item,        \
+                                          const char *page, size_t len)\
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_uint_store(opts->opts, &opts->info.name,    \
+                                      minval, maxval, page, len);      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+/* define a string option for EP */
+#define F_MIDI2_EP_STR_OPT(name, maxlen)                               \
+static ssize_t f_midi2_ep_opts_##name##_show(struct config_item *item, \
+                                            char *page)                \
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_str_show(opts->opts, opts->info.name, page);\
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_ep_opts_##name##_store(struct config_item *item,        \
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);        \
+       return f_midi2_opts_str_store(opts->opts, &opts->info.name, maxlen,\
+                                     page, len);                       \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_ep_opts_, name)
+
+F_MIDI2_EP_OPT(protocol, "0x%x", 1, 2);
+F_MIDI2_EP_OPT(protocol_caps, "0x%x", 1, 3);
+F_MIDI2_EP_OPT(manufacturer, "0x%x", 0, 0xffffff);
+F_MIDI2_EP_OPT(family, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(model, "0x%x", 0, 0xffff);
+F_MIDI2_EP_OPT(sw_revision, "0x%x", 0, 0xffffffff);
+F_MIDI2_EP_STR_OPT(ep_name, 128);
+F_MIDI2_EP_STR_OPT(product_id, 128);
+
+static struct configfs_attribute *f_midi2_ep_attrs[] = {
+       &f_midi2_ep_opts_attr_protocol,
+       &f_midi2_ep_opts_attr_protocol_caps,
+       &f_midi2_ep_opts_attr_ep_name,
+       &f_midi2_ep_opts_attr_product_id,
+       &f_midi2_ep_opts_attr_manufacturer,
+       &f_midi2_ep_opts_attr_family,
+       &f_midi2_ep_opts_attr_model,
+       &f_midi2_ep_opts_attr_sw_revision,
+       NULL,
+};
+
+static void f_midi2_ep_opts_release(struct config_item *item)
+{
+       struct f_midi2_ep_opts *opts = to_f_midi2_ep_opts(item);
+
+       kfree(opts->info.ep_name);
+       kfree(opts->info.product_id);
+       kfree(opts);
+}
+
+static struct configfs_item_operations f_midi2_ep_item_ops = {
+       .release        = f_midi2_ep_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_ep_group_ops = {
+       .make_group     = f_midi2_opts_block_make,
+       .drop_item      = f_midi2_opts_block_drop,
+};
+
+static const struct config_item_type f_midi2_ep_type = {
+       .ct_item_ops    = &f_midi2_ep_item_ops,
+       .ct_group_ops   = &f_midi2_ep_group_ops,
+       .ct_attrs       = f_midi2_ep_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+/* create a f_midi2_ep_opts instance */
+static int f_midi2_ep_opts_create(struct f_midi2_opts *opts,
+                                 unsigned int index,
+                                 struct f_midi2_ep_opts **ep_p)
+{
+       struct f_midi2_ep_opts *ep_opts;
+
+       ep_opts = kzalloc(sizeof(*ep_opts), GFP_KERNEL);
+       if (!ep_opts)
+               return -ENOMEM;
+
+       ep_opts->opts = opts;
+       ep_opts->index = index;
+
+       /* set up the default values */
+       ep_opts->info.protocol = 2;
+       ep_opts->info.protocol_caps = 3;
+
+       opts->eps[index] = ep_opts;
+       *ep_p = ep_opts;
+       return 0;
+}
+
+/* make_group callback for an EP */
+static struct config_group *
+f_midi2_opts_ep_make(struct config_group *group, const char *name)
+{
+       struct f_midi2_opts *opts;
+       struct f_midi2_ep_opts *ep_opts;
+       unsigned int index;
+       int ret;
+
+       if (strncmp(name, "ep.", 3))
+               return ERR_PTR(-EINVAL);
+       ret = kstrtouint(name + 3, 10, &index);
+       if (ret)
+               return ERR_PTR(ret);
+
+       opts = to_f_midi2_opts(&group->cg_item);
+       if (index >= MAX_UMP_EPS)
+               return ERR_PTR(-EINVAL);
+       if (opts->eps[index])
+               return ERR_PTR(-EBUSY);
+       ret = f_midi2_ep_opts_create(opts, index, &ep_opts);
+       if (ret)
+               return ERR_PTR(ret);
+
+       config_group_init_type_name(&ep_opts->group, name, &f_midi2_ep_type);
+       return &ep_opts->group;
+}
+
+/* drop_item callback for an EP */
+static void
+f_midi2_opts_ep_drop(struct config_group *group, struct config_item *item)
+{
+       struct f_midi2_ep_opts *ep_opts = to_f_midi2_ep_opts(item);
+
+       mutex_lock(&ep_opts->opts->lock);
+       ep_opts->opts->eps[ep_opts->index] = NULL;
+       mutex_unlock(&ep_opts->opts->lock);
+       config_item_put(item);
+}
+
+/*
+ * Definitions for card config
+ */
+
+/* define a bool option for card */
+#define F_MIDI2_BOOL_OPT(name)                                         \
+static ssize_t f_midi2_opts_##name##_show(struct config_item *item,    \
+                                         char *page)                   \
+{                                                                      \
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);              \
+       return f_midi2_opts_uint_show(opts, opts->info.name,            \
+                                     "%u\n", page);                    \
+}                                                                      \
+                                                                       \
+static ssize_t f_midi2_opts_##name##_store(struct config_item *item,   \
+                                        const char *page, size_t len)  \
+{                                                                      \
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);              \
+       return f_midi2_opts_bool_store(opts, &opts->info.name,          \
+                                      page, len);                      \
+}                                                                      \
+                                                                       \
+CONFIGFS_ATTR(f_midi2_opts_, name)
+
+F_MIDI2_BOOL_OPT(process_ump);
+F_MIDI2_BOOL_OPT(static_block);
+
+static ssize_t f_midi2_opts_iface_name_show(struct config_item *item,
+                                           char *page)
+{
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+       return f_midi2_opts_str_show(opts, opts->info.iface_name, page);
+}
+
+static ssize_t f_midi2_opts_iface_name_store(struct config_item *item,
+                                            const char *page, size_t len)
+{
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+       return f_midi2_opts_str_store(opts, &opts->info.iface_name, 128,
+                                     page, len);
+}
+
+CONFIGFS_ATTR(f_midi2_opts_, iface_name);
+
+static struct configfs_attribute *f_midi2_attrs[] = {
+       &f_midi2_opts_attr_process_ump,
+       &f_midi2_opts_attr_static_block,
+       &f_midi2_opts_attr_iface_name,
+       NULL
+};
+
+static void f_midi2_opts_release(struct config_item *item)
+{
+       struct f_midi2_opts *opts = to_f_midi2_opts(item);
+
+       usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations f_midi2_item_ops = {
+       .release        = f_midi2_opts_release,
+};
+
+static struct configfs_group_operations f_midi2_group_ops = {
+       .make_group     = f_midi2_opts_ep_make,
+       .drop_item      = f_midi2_opts_ep_drop,
+};
+
+static const struct config_item_type f_midi2_func_type = {
+       .ct_item_ops    = &f_midi2_item_ops,
+       .ct_group_ops   = &f_midi2_group_ops,
+       .ct_attrs       = f_midi2_attrs,
+       .ct_owner       = THIS_MODULE,
+};
+
+static void f_midi2_free_inst(struct usb_function_instance *f)
+{
+       struct f_midi2_opts *opts;
+
+       opts = container_of(f, struct f_midi2_opts, func_inst);
+
+       kfree(opts->info.iface_name);
+       kfree(opts);
+}
+
+/* gadget alloc_inst */
+static struct usb_function_instance *f_midi2_alloc_inst(void)
+{
+       struct f_midi2_opts *opts;
+       struct f_midi2_ep_opts *ep_opts;
+       struct f_midi2_block_opts *block_opts;
+       int ret;
+
+       opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+       if (!opts)
+               return ERR_PTR(-ENOMEM);
+
+       mutex_init(&opts->lock);
+       opts->func_inst.free_func_inst = f_midi2_free_inst;
+       opts->info.process_ump = true;
+       opts->info.static_block = true;
+       opts->info.num_reqs = 32;
+       opts->info.req_buf_size = 512;
+
+       /* create the default ep */
+       ret = f_midi2_ep_opts_create(opts, 0, &ep_opts);
+       if (ret) {
+               kfree(opts);
+               return ERR_PTR(ret);
+       }
+
+       /* create the default block */
+       ret = f_midi2_block_opts_create(ep_opts, 0, &block_opts);
+       if (ret) {
+               kfree(ep_opts);
+               kfree(opts);
+               return ERR_PTR(ret);
+       }
+
+       /* set up the default MIDI1 (that is mandatory) */
+       block_opts->info.midi1_num_groups = 1;
+
+       config_group_init_type_name(&opts->func_inst.group, "",
+                                   &f_midi2_func_type);
+
+       config_group_init_type_name(&ep_opts->group, "ep.0",
+                                   &f_midi2_ep_type);
+       configfs_add_default_group(&ep_opts->group, &opts->func_inst.group);
+
+       config_group_init_type_name(&block_opts->group, "block.0",
+                                   &f_midi2_block_type);
+       configfs_add_default_group(&block_opts->group, &ep_opts->group);
+
+       return &opts->func_inst;
+}
+
+static void do_f_midi2_free(struct f_midi2 *midi2, struct f_midi2_opts *opts)
+{
+       mutex_lock(&opts->lock);
+       --opts->refcnt;
+       mutex_unlock(&opts->lock);
+       kfree(midi2->string_defs);
+       kfree(midi2);
+}
+
+static void f_midi2_free(struct usb_function *f)
+{
+       do_f_midi2_free(func_to_midi2(f),
+                       container_of(f->fi, struct f_midi2_opts, func_inst));
+}
+
+/* verify the parameters set up via configfs;
+ * return the number of EPs or a negative error
+ */
+static int verify_parameters(struct f_midi2_opts *opts)
+{
+       int i, j, num_eps, num_blks;
+       struct f_midi2_ep_info *ep;
+       struct f_midi2_block_info *bp;
+
+       for (num_eps = 0; num_eps < MAX_UMP_EPS && opts->eps[num_eps];
+            num_eps++)
+               ;
+       if (!num_eps) {
+               pr_err("f_midi2: No EP is defined\n");
+               return -EINVAL;
+       }
+
+       num_blks = 0;
+       for (i = 0; i < num_eps; i++) {
+               ep = &opts->eps[i]->info;
+               if (!(ep->protocol_caps & ep->protocol)) {
+                       pr_err("f_midi2: Invalid protocol 0x%x (caps 0x%x) for EP %d\n",
+                              ep->protocol, ep->protocol_caps, i);
+                       return -EINVAL;
+               }
+
+               for (j = 0; j < SNDRV_UMP_MAX_BLOCKS && opts->eps[i]->blks[j];
+                    j++, num_blks++) {
+                       bp = &opts->eps[i]->blks[j]->info;
+                       if (bp->first_group + bp->num_groups > SNDRV_UMP_MAX_GROUPS) {
+                               pr_err("f_midi2: Invalid group definitions for block %d:%d\n",
+                                      i, j);
+                               return -EINVAL;
+                       }
+
+                       if (bp->midi1_num_groups) {
+                               if (bp->midi1_first_group < bp->first_group ||
+                                   bp->midi1_first_group + bp->midi1_num_groups >
+                                   bp->first_group + bp->num_groups) {
+                                       pr_err("f_midi2: Invalid MIDI1 group definitions for block %d:%d\n",
+                                              i, j);
+                                       return -EINVAL;
+                               }
+                       }
+               }
+       }
+       if (!num_blks) {
+               pr_err("f_midi2: No block is defined\n");
+               return -EINVAL;
+       }
+
+       return num_eps;
+}
+
+/* fill mapping between MIDI 1.0 cable and UMP EP/group */
+static void fill_midi1_cable_mapping(struct f_midi2 *midi2,
+                                    struct f_midi2_ep *ep,
+                                    int blk)
+{
+       const struct f_midi2_block_info *binfo = &ep->blks[blk].info;
+       struct midi1_cable_mapping *map;
+       int i, group;
+
+       if (!binfo->midi1_num_groups)
+               return;
+       if (binfo->direction != SNDRV_UMP_DIR_OUTPUT) {
+               group = binfo->midi1_first_group;
+               map = midi2->in_cable_mapping + midi2->num_midi1_in;
+               for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
+                       if (midi2->num_midi1_in >= MAX_CABLES)
+                               break;
+                       map->ep = ep;
+                       map->block = blk;
+                       map->group = group;
+                       midi2->num_midi1_in++;
+                       /* store 1-based cable number */
+                       ep->in_group_to_cable[group] = midi2->num_midi1_in;
+               }
+       }
+
+       if (binfo->direction != SNDRV_UMP_DIR_INPUT) {
+               group = binfo->midi1_first_group;
+               map = midi2->out_cable_mapping + midi2->num_midi1_out;
+               for (i = 0; i < binfo->midi1_num_groups; i++, group++, map++) {
+                       if (midi2->num_midi1_out >= MAX_CABLES)
+                               break;
+                       map->ep = ep;
+                       map->block = blk;
+                       map->group = group;
+                       midi2->num_midi1_out++;
+               }
+       }
+}
+
+/* gadget alloc callback */
+static struct usb_function *f_midi2_alloc(struct usb_function_instance *fi)
+{
+       struct f_midi2 *midi2;
+       struct f_midi2_opts *opts;
+       struct f_midi2_ep *ep;
+       struct f_midi2_block *bp;
+       int i, num_eps, blk;
+
+       midi2 = kzalloc(sizeof(*midi2), GFP_KERNEL);
+       if (!midi2)
+               return ERR_PTR(-ENOMEM);
+
+       opts = container_of(fi, struct f_midi2_opts, func_inst);
+       mutex_lock(&opts->lock);
+       num_eps = verify_parameters(opts);
+       if (num_eps < 0) {
+               mutex_unlock(&opts->lock);
+               kfree(midi2);
+               return ERR_PTR(num_eps);
+       }
+       ++opts->refcnt;
+       mutex_unlock(&opts->lock);
+
+       spin_lock_init(&midi2->queue_lock);
+
+       midi2->func.name = "midi2_func";
+       midi2->func.bind = f_midi2_bind;
+       midi2->func.unbind = f_midi2_unbind;
+       midi2->func.get_alt = f_midi2_get_alt;
+       midi2->func.set_alt = f_midi2_set_alt;
+       midi2->func.setup = f_midi2_setup;
+       midi2->func.disable = f_midi2_disable;
+       midi2->func.free_func = f_midi2_free;
+
+       midi2->info = opts->info;
+       midi2->num_eps = num_eps;
+
+       for (i = 0; i < num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               ep->info = opts->eps[i]->info;
+               ep->card = midi2;
+               for (blk = 0; blk < SNDRV_UMP_MAX_BLOCKS &&
+                            opts->eps[i]->blks[blk]; blk++) {
+                       bp = &ep->blks[blk];
+                       ep->num_blks++;
+                       bp->info = opts->eps[i]->blks[blk]->info;
+                       bp->gtb_id = ++midi2->total_blocks;
+               }
+       }
+
+       midi2->string_defs = kcalloc(midi2->total_blocks + 1,
+                                    sizeof(*midi2->string_defs), GFP_KERNEL);
+       if (!midi2->string_defs) {
+               do_f_midi2_free(midi2, opts);
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (opts->info.iface_name && *opts->info.iface_name)
+               midi2->string_defs[STR_IFACE].s = opts->info.iface_name;
+       else
+               midi2->string_defs[STR_IFACE].s = ump_ep_name(&midi2->midi2_eps[0]);
+
+       for (i = 0; i < midi2->num_eps; i++) {
+               ep = &midi2->midi2_eps[i];
+               for (blk = 0; blk < ep->num_blks; blk++) {
+                       bp = &ep->blks[blk];
+                       midi2->string_defs[gtb_to_str_id(bp->gtb_id)].s =
+                               ump_fb_name(&bp->info);
+
+                       fill_midi1_cable_mapping(midi2, ep, blk);
+               }
+       }
+
+       if (!midi2->num_midi1_in && !midi2->num_midi1_out) {
+               pr_err("f_midi2: MIDI1 definition is missing\n");
+               do_f_midi2_free(midi2, opts);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return &midi2->func;
+}
+
+DECLARE_USB_FUNCTION_INIT(midi2, f_midi2_alloc_inst, f_midi2_alloc);
+
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/function/u_midi2.h b/drivers/usb/gadget/function/u_midi2.h
new file mode 100644 (file)
index 0000000..4e7adb4
--- /dev/null
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Utility definitions for MIDI 2.0 function
+ */
+
+#ifndef U_MIDI2_H
+#define U_MIDI2_H
+
+#include <linux/usb/composite.h>
+#include <sound/asound.h>
+
+struct f_midi2_opts;
+struct f_midi2_ep_opts;
+struct f_midi2_block_opts;
+
+/* UMP Function Block info */
+struct f_midi2_block_info {
+       unsigned int direction;         /* FB direction: 1-3 */
+       unsigned int first_group;       /* first UMP group: 0-15 */
+       unsigned int num_groups;        /* number of UMP groups: 1-16 */
+       unsigned int midi1_first_group; /* first UMP group for MIDI 1.0 */
+       unsigned int midi1_num_groups;  /* number of UMP groups for MIDI 1.0 */
+       unsigned int ui_hint;           /* UI-hint: 0-3 */
+       unsigned int midi_ci_version;   /* MIDI-CI version: 0-255 */
+       unsigned int sysex8_streams;    /* number of sysex8 streams: 0-255 */
+       unsigned int is_midi1;          /* MIDI 1.0 port: 0-2 */
+       bool active;                    /* FB active flag: bool */
+       const char *name;               /* FB name */
+};
+
+/* UMP Endpoint info */
+struct f_midi2_ep_info {
+       unsigned int protocol_caps;     /* protocol capabilities: 1-3 */
+       unsigned int protocol;          /* default protocol: 1-2 */
+       unsigned int manufacturer;      /* manufacturer id: 0-0xffffff */
+       unsigned int family;            /* device family id: 0-0xffff */
+       unsigned int model;             /* device model id: 0x-0xffff */
+       unsigned int sw_revision;       /* software revision: 32bit */
+
+       const char *ep_name;            /* Endpoint name */
+       const char *product_id;         /* Product ID */
+};
+
+struct f_midi2_card_info {
+       bool process_ump;               /* process UMP stream: bool */
+       bool static_block;              /* static FBs: bool */
+       unsigned int req_buf_size;      /* request buffer size */
+       unsigned int num_reqs;          /* number of requests */
+       const char *iface_name;         /* interface name */
+};
+
+struct f_midi2_block_opts {
+       struct config_group group;
+       unsigned int id;
+       struct f_midi2_block_info info;
+       struct f_midi2_ep_opts *ep;
+};
+
+struct f_midi2_ep_opts {
+       struct config_group group;
+       unsigned int index;
+       struct f_midi2_ep_info info;
+       struct f_midi2_block_opts *blks[SNDRV_UMP_MAX_BLOCKS];
+       struct f_midi2_opts *opts;
+};
+
+#define MAX_UMP_EPS            4
+#define MAX_CABLES             16
+
+struct f_midi2_opts {
+       struct usb_function_instance func_inst;
+       struct mutex lock;
+       int refcnt;
+
+       struct f_midi2_card_info info;
+
+       unsigned int num_eps;
+       struct f_midi2_ep_opts *eps[MAX_UMP_EPS];
+};
+
+#endif /* U_MIDI2_H */
index 16f2db8..f60a019 100644 (file)
@@ -328,8 +328,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
        vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
                                      VHUB_IRQ_DEV1_BIT);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       vhub->regs = devm_ioremap_resource(&pdev->dev, res);
+       vhub->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(vhub->regs)) {
                dev_err(&pdev->dev, "Failed to map resources\n");
                return PTR_ERR(vhub->regs);
index 01968e2..2ef89a4 100644 (file)
@@ -1468,7 +1468,6 @@ static int ast_udc_probe(struct platform_device *pdev)
        enum usb_device_speed max_speed;
        struct device *dev = &pdev->dev;
        struct ast_udc_dev *udc;
-       struct resource *res;
        int rc;
 
        udc = devm_kzalloc(&pdev->dev, sizeof(struct ast_udc_dev), GFP_KERNEL);
@@ -1484,8 +1483,7 @@ static int ast_udc_probe(struct platform_device *pdev)
        udc->gadget.name = "aspeed-udc";
        udc->gadget.dev.init_name = "gadget";
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       udc->reg = devm_ioremap_resource(&pdev->dev, res);
+       udc->reg = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(udc->reg)) {
                dev_err(&pdev->dev, "Failed to map resources\n");
                return PTR_ERR(udc->reg);
index 6c0ed3f..02b1bef 100644 (file)
@@ -2285,15 +2285,13 @@ static int usba_udc_probe(struct platform_device *pdev)
        udc->gadget = usba_gadget_template;
        INIT_LIST_HEAD(&udc->gadget.ep_list);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID);
-       udc->regs = devm_ioremap_resource(&pdev->dev, res);
+       udc->regs = devm_platform_get_and_ioremap_resource(pdev, CTRL_IOMEM_ID, &res);
        if (IS_ERR(udc->regs))
                return PTR_ERR(udc->regs);
        dev_info(&pdev->dev, "MMIO registers at %pR mapped at %p\n",
                 res, udc->regs);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID);
-       udc->fifo = devm_ioremap_resource(&pdev->dev, res);
+       udc->fifo = devm_platform_get_and_ioremap_resource(pdev, FIFO_IOMEM_ID, &res);
        if (IS_ERR(udc->fifo))
                return PTR_ERR(udc->fifo);
        dev_info(&pdev->dev, "FIFO at %pR mapped at %p\n", res, udc->fifo);
index 9c5dc1c..4aae86b 100644 (file)
@@ -1959,6 +1959,8 @@ static void ch9getstatus(struct qe_udc *udc, u8 request_type, u16 value,
        } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
                /* Get endpoint status */
                int pipe = index & USB_ENDPOINT_NUMBER_MASK;
+               if (pipe >= USB_MAX_ENDPOINTS)
+                       goto stall;
                struct qe_ep *target_ep = &udc->eps[pipe];
                u16 usep;
 
index a67873a..5265ca4 100644 (file)
@@ -36,7 +36,6 @@
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
 #include <linux/dmapool.h>
-#include <linux/of_device.h>
 
 #include <asm/byteorder.h>
 #include <asm/io.h>
index 0976255..0c39693 100644 (file)
@@ -23,6 +23,7 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/errno.h>
@@ -36,9 +37,7 @@
 #include <linux/dmapool.h>
 #include <linux/debugfs.h>
 #include <linux/seq_file.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
-#include <linux/of_address.h>
+#include <linux/of.h>
 
 #include <asm/byteorder.h>
 
index 12c519f..2d57786 100644 (file)
@@ -19,9 +19,7 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/bitfield.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/prefetch.h>
 #include <linux/usb/ch9.h>
index c4e1d95..61424cf 100644 (file)
@@ -23,7 +23,7 @@
 #include <linux/prefetch.h>
 #include <linux/byteorder/generic.h>
 #include <linux/platform_data/pxa2xx_udc.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/of_gpio.h>
 
 #include <linux/usb.h>
index 59bb25d..3b01734 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
index 6cd0af8..3482b41 100644 (file)
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/kfifo.h>
+#include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/types.h>
 #include <linux/usb/composite.h>
index 0ed685d..35c7a14 100644 (file)
@@ -112,8 +112,7 @@ static int udc_plat_probe(struct platform_device *pdev)
        spin_lock_init(&udc->lock);
        udc->dev = dev;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       udc->virt_addr = devm_ioremap_resource(dev, res);
+       udc->virt_addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(udc->virt_addr))
                return PTR_ERR(udc->virt_addr);
 
index df6028f..cb85168 100644 (file)
@@ -16,7 +16,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/tegra/xusb.h>
 #include <linux/pm_domain.h>
index a4a7b90..f301b09 100644 (file)
 #include <linux/interrupt.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/of_platform.h>
-#include <linux/of_irq.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
 #include <linux/prefetch.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
@@ -2080,8 +2078,7 @@ static int xudc_probe(struct platform_device *pdev)
        udc->req->usb_req.buf = buff;
 
        /* Map the registers */
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       udc->addr = devm_ioremap_resource(&pdev->dev, res);
+       udc->addr = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(udc->addr))
                return PTR_ERR(udc->addr);
 
index 61808c5..e14b66d 100644 (file)
@@ -122,8 +122,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
        }
        atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto fail_request_resource;
index 20f8c0e..f644b13 100644 (file)
@@ -173,8 +173,7 @@ static int exynos_ehci_probe(struct platform_device *pdev)
        if (err)
                goto fail_clk;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto fail_io;
index 81d60a6..5b1ce39 100644 (file)
@@ -22,7 +22,7 @@
 #include <linux/usb/otg.h>
 #include <linux/platform_device.h>
 #include <linux/fsl_devices.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
 #include <linux/io.h>
 
 #include "ehci.h"
@@ -87,8 +87,7 @@ static int fsl_ehci_drv_probe(struct platform_device *pdev)
                goto err1;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err2;
index 9320cf0..2f1fc7e 100644 (file)
@@ -142,8 +142,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
                goto err_put_hcd;
        }
 
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       ehci_mv->base = devm_ioremap_resource(&pdev->dev, r);
+       ehci_mv->base = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
        if (IS_ERR(ehci_mv->base)) {
                retval = PTR_ERR(ehci_mv->base);
                goto err_put_hcd;
index ad79ce6..3d3317a 100644 (file)
@@ -53,7 +53,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
        int irq;
        int retval;
 
-       dev_dbg(&pdev->dev,     "initializing npcm7xx ehci USB Controller\n");
+       dev_dbg(&pdev->dev, "initializing npcm7xx ehci USB Controller\n");
 
        if (usb_disabled())
                return -ENODEV;
@@ -79,8 +79,7 @@ static int npcm7xx_ehci_hcd_drv_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err_put_hcd;
index cb6509a..b24f371 100644 (file)
@@ -113,8 +113,7 @@ static int ehci_hcd_omap_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
-       res =  platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       regs = devm_ioremap_resource(dev, res);
+       regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(regs))
                return PTR_ERR(regs);
 
index 2cfb27d..54f74b4 100644 (file)
@@ -13,8 +13,6 @@
 #include <linux/platform_data/usb-ehci-orion.h>
 #include <linux/of.h>
 #include <linux/phy/phy.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 #include <linux/io.h>
@@ -234,8 +232,7 @@ static int ehci_orion_drv_probe(struct platform_device *pdev)
        if (err)
                goto err;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       regs = devm_ioremap_resource(&pdev->dev, res);
+       regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(regs)) {
                err = PTR_ERR(regs);
                goto err;
index 83bf56c..98b0731 100644 (file)
@@ -359,8 +359,7 @@ static int ehci_platform_probe(struct platform_device *dev)
                        goto err_reset;
        }
 
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_power;
index 0520e76..575c513 100644 (file)
@@ -95,8 +95,7 @@ static int ehci_hcd_sh_probe(struct platform_device *pdev)
                goto fail_create_hcd;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto fail_request_resource;
index 1407703..d0e94e4 100644 (file)
@@ -91,8 +91,7 @@ static int spear_ehci_hcd_drv_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err_put_hcd;
index ee0976b..2dbb0d8 100644 (file)
@@ -158,11 +158,6 @@ static int st_ehci_platform_probe(struct platform_device *dev)
        irq = platform_get_irq(dev, 0);
        if (irq < 0)
                return irq;
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       if (!res_mem) {
-               dev_err(&dev->dev, "no memory resource provided");
-               return -ENXIO;
-       }
 
        hcd = usb_create_hcd(&ehci_platform_hc_driver, &dev->dev,
                             dev_name(&dev->dev));
@@ -222,14 +217,13 @@ static int st_ehci_platform_probe(struct platform_device *dev)
                        goto err_put_clks;
        }
 
-       hcd->rsrc_start = res_mem->start;
-       hcd->rsrc_len = resource_size(res_mem);
-
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_put_clks;
        }
+       hcd->rsrc_start = res_mem->start;
+       hcd->rsrc_len = resource_size(res_mem);
 
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err)
index 66a045e..9a1b522 100644 (file)
 #include <linux/io.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
+#include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <linux/gpio/consumer.h>
 #include <soc/fsl/qe/qe.h>
index a9877f2..8508d37 100644 (file)
@@ -10,7 +10,8 @@
 #include <linux/fsl_devices.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/of_platform.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/clk.h>
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
index 606f0a6..a52c3d8 100644 (file)
@@ -2651,8 +2651,7 @@ static int isp1362_probe(struct platform_device *pdev)
        if (IS_ERR(addr_reg))
                return PTR_ERR(addr_reg);
 
-       data = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       data_reg = devm_ioremap_resource(&pdev->dev, data);
+       data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data);
        if (IS_ERR(data_reg))
                return PTR_ERR(data_reg);
 
index b805c4b..f691cd9 100644 (file)
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/gpio/consumer.h>
-#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/platform_data/atmel.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mfd/syscon.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
@@ -190,18 +190,15 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver,
        int irq;
 
        irq = platform_get_irq(pdev, 0);
-       if (irq < 0) {
-               dev_dbg(dev, "hcd probe: missing irq resource\n");
+       if (irq < 0)
                return irq;
-       }
 
        hcd = usb_create_hcd(driver, dev, "at91");
        if (!hcd)
                return -ENOMEM;
        ohci_at91 = hcd_to_ohci_at91_priv(hcd);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err;
index e4191a8..d9adae5 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/jiffies.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_data/usb-davinci.h>
@@ -435,8 +436,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
                        goto err;
        }
 
-       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(dev, mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &mem);
        if (IS_ERR(hcd->regs)) {
                error = PTR_ERR(hcd->regs);
                goto err;
index ab31c45..20e26a4 100644 (file)
@@ -149,8 +149,7 @@ static int exynos_ohci_probe(struct platform_device *pdev)
        if (err)
                goto fail_clk;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto fail_io;
index c04b2af..8264c45 100644 (file)
@@ -202,8 +202,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev)
                goto fail_hcd;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto fail_resource;
index 45a2c98..4a75507 100644 (file)
@@ -200,8 +200,7 @@ static int ohci_platform_probe(struct platform_device *dev)
                        goto err_reset;
        }
 
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_power;
index 35a7ad7..f64bfe5 100644 (file)
  */
 
 #include <linux/signal.h>
+#include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_irq.h>
-#include <linux/of_platform.h>
+#include <linux/platform_device.h>
 
 static int
 ohci_ppc_of_start(struct usb_hcd *hcd)
index 7410442..357d9ae 100644 (file)
@@ -435,8 +435,7 @@ static int ohci_hcd_pxa27x_probe(struct platform_device *pdev)
        if (!hcd)
                return -ENOMEM;
 
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, r);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &r);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err;
index f4b2656..993f347 100644 (file)
@@ -68,8 +68,7 @@ static int spear_ohci_hcd_drv_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                retval = PTR_ERR(hcd->regs);
                goto err_put_hcd;
index 884e447..2143420 100644 (file)
@@ -139,12 +139,6 @@ static int st_ohci_platform_probe(struct platform_device *dev)
        if (irq < 0)
                return irq;
 
-       res_mem = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       if (!res_mem) {
-               dev_err(&dev->dev, "no memory resource provided");
-               return -ENXIO;
-       }
-
        hcd = usb_create_hcd(&ohci_platform_hc_driver, &dev->dev,
                        dev_name(&dev->dev));
        if (!hcd)
@@ -199,14 +193,14 @@ static int st_ohci_platform_probe(struct platform_device *dev)
                        goto err_power;
        }
 
-       hcd->rsrc_start = res_mem->start;
-       hcd->rsrc_len = resource_size(res_mem);
-
-       hcd->regs = devm_ioremap_resource(&dev->dev, res_mem);
+       hcd->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res_mem);
        if (IS_ERR(hcd->regs)) {
                err = PTR_ERR(hcd->regs);
                goto err_power;
        }
+       hcd->rsrc_start = res_mem->start;
+       hcd->rsrc_len = resource_size(res_mem);
+
        err = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (err)
                goto err_power;
index 50c1cca..d467472 100644 (file)
@@ -4230,8 +4230,7 @@ static int oxu_drv_probe(struct platform_device *pdev)
                return irq;
        dev_dbg(&pdev->dev, "IRQ resource %d\n", irq);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(&pdev->dev, res);
+       base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(base)) {
                ret = PTR_ERR(base);
                goto error;
index 71ca532..3dec5dd 100644 (file)
@@ -91,8 +91,7 @@ static int uhci_hcd_platform_probe(struct platform_device *pdev)
 
        uhci = hcd_to_uhci(hcd);
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto err_rmr;
index b26ea7c..28218c8 100644 (file)
@@ -13,7 +13,6 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/usb/phy.h>
 #include <linux/slab.h>
index bf5261f..ab9c596 100644 (file)
@@ -10,7 +10,6 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/usb/phy.h>
 
 #include "xhci.h"
index 4693d83..6246d5a 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/iopoll.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/of_irq.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/tegra/xusb.h>
@@ -1912,6 +1912,15 @@ put_padctl:
        return err;
 }
 
+static void tegra_xusb_disable(struct tegra_xusb *tegra)
+{
+       tegra_xusb_powergate_partitions(tegra);
+       tegra_xusb_powerdomain_remove(tegra->dev, tegra);
+       tegra_xusb_phy_disable(tegra);
+       tegra_xusb_clk_disable(tegra);
+       regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
+}
+
 static void tegra_xusb_remove(struct platform_device *pdev)
 {
        struct tegra_xusb *tegra = platform_get_drvdata(pdev);
@@ -1934,14 +1943,18 @@ static void tegra_xusb_remove(struct platform_device *pdev)
 
        pm_runtime_put(&pdev->dev);
 
-       tegra_xusb_powergate_partitions(tegra);
+       tegra_xusb_disable(tegra);
+       tegra_xusb_padctl_put(tegra->padctl);
+}
 
-       tegra_xusb_powerdomain_remove(&pdev->dev, tegra);
+static void tegra_xusb_shutdown(struct platform_device *pdev)
+{
+       struct tegra_xusb *tegra = platform_get_drvdata(pdev);
 
-       tegra_xusb_phy_disable(tegra);
-       tegra_xusb_clk_disable(tegra);
-       regulator_bulk_disable(tegra->soc->num_supplies, tegra->supplies);
-       tegra_xusb_padctl_put(tegra->padctl);
+       pm_runtime_get_sync(&pdev->dev);
+       disable_irq(tegra->xhci_irq);
+       xhci_shutdown(tegra->hcd);
+       tegra_xusb_disable(tegra);
 }
 
 static bool xhci_hub_ports_suspended(struct xhci_hub *hub)
@@ -2652,6 +2665,7 @@ MODULE_DEVICE_TABLE(of, tegra_xusb_of_match);
 static struct platform_driver tegra_xusb_driver = {
        .probe = tegra_xusb_probe,
        .remove_new = tegra_xusb_remove,
+       .shutdown = tegra_xusb_shutdown,
        .driver = {
                .name = "tegra-xusb",
                .pm = &tegra_xusb_pm_ops,
index 83f14ca..3da1a46 100644 (file)
 
 #include "onboard_usb_hub.h"
 
+/*
+ * Use generic names, as the actual names might differ between hubs. If a new
+ * hub requires more than the currently supported supplies, add a new one here.
+ */
+static const char * const supply_names[] = {
+       "vdd",
+       "vdd2",
+};
+
+#define MAX_SUPPLIES ARRAY_SIZE(supply_names)
+
 static void onboard_hub_attach_usb_driver(struct work_struct *work);
 
 static struct usb_device_driver onboard_hub_usbdev_driver;
@@ -40,7 +51,7 @@ struct usbdev_node {
 };
 
 struct onboard_hub {
-       struct regulator *vdd;
+       struct regulator_bulk_data supplies[MAX_SUPPLIES];
        struct device *dev;
        const struct onboard_hub_pdata *pdata;
        struct gpio_desc *reset_gpio;
@@ -55,9 +66,9 @@ static int onboard_hub_power_on(struct onboard_hub *hub)
 {
        int err;
 
-       err = regulator_enable(hub->vdd);
+       err = regulator_bulk_enable(hub->pdata->num_supplies, hub->supplies);
        if (err) {
-               dev_err(hub->dev, "failed to enable regulator: %d\n", err);
+               dev_err(hub->dev, "failed to enable supplies: %d\n", err);
                return err;
        }
 
@@ -75,9 +86,9 @@ static int onboard_hub_power_off(struct onboard_hub *hub)
 
        gpiod_set_value_cansleep(hub->reset_gpio, 1);
 
-       err = regulator_disable(hub->vdd);
+       err = regulator_bulk_disable(hub->pdata->num_supplies, hub->supplies);
        if (err) {
-               dev_err(hub->dev, "failed to disable regulator: %d\n", err);
+               dev_err(hub->dev, "failed to disable supplies: %d\n", err);
                return err;
        }
 
@@ -232,6 +243,7 @@ static int onboard_hub_probe(struct platform_device *pdev)
        const struct of_device_id *of_id;
        struct device *dev = &pdev->dev;
        struct onboard_hub *hub;
+       unsigned int i;
        int err;
 
        hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL);
@@ -246,9 +258,18 @@ static int onboard_hub_probe(struct platform_device *pdev)
        if (!hub->pdata)
                return -EINVAL;
 
-       hub->vdd = devm_regulator_get(dev, "vdd");
-       if (IS_ERR(hub->vdd))
-               return PTR_ERR(hub->vdd);
+       if (hub->pdata->num_supplies > MAX_SUPPLIES)
+               return dev_err_probe(dev, -EINVAL, "max %zu supplies supported!\n",
+                                    MAX_SUPPLIES);
+
+       for (i = 0; i < hub->pdata->num_supplies; i++)
+               hub->supplies[i].supply = supply_names[i];
+
+       err = devm_regulator_bulk_get(dev, hub->pdata->num_supplies, hub->supplies);
+       if (err) {
+               dev_err(dev, "Failed to get regulator supplies: %d\n", err);
+               return err;
+       }
 
        hub->reset_gpio = devm_gpiod_get_optional(dev, "reset",
                                                  GPIOD_OUT_HIGH);
@@ -329,6 +350,7 @@ static struct platform_driver onboard_hub_driver = {
 
 /************************** USB driver **************************/
 
+#define VENDOR_ID_CYPRESS      0x04b4
 #define VENDOR_ID_GENESYS      0x05e3
 #define VENDOR_ID_MICROCHIP    0x0424
 #define VENDOR_ID_REALTEK      0x0bda
@@ -407,8 +429,11 @@ static void onboard_hub_usbdev_disconnect(struct usb_device *udev)
 }
 
 static const struct usb_device_id onboard_hub_id_table[] = {
+       { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6504) }, /* CYUSB33{0,1,2}x/CYUSB230x 3.0 */
+       { USB_DEVICE(VENDOR_ID_CYPRESS, 0x6506) }, /* CYUSB33{0,1,2}x/CYUSB230x 2.0 */
        { USB_DEVICE(VENDOR_ID_GENESYS, 0x0608) }, /* Genesys Logic GL850G USB 2.0 */
        { USB_DEVICE(VENDOR_ID_GENESYS, 0x0610) }, /* Genesys Logic GL852G USB 2.0 */
+       { USB_DEVICE(VENDOR_ID_GENESYS, 0x0620) }, /* Genesys Logic GL3523 USB 3.1 */
        { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */
        { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2517) }, /* USB2517 USB 2.0 */
        { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */
index aca5f50..4026ba6 100644 (file)
@@ -8,30 +8,42 @@
 
 struct onboard_hub_pdata {
        unsigned long reset_us;         /* reset pulse width in us */
+       unsigned int num_supplies;      /* number of supplies */
 };
 
 static const struct onboard_hub_pdata microchip_usb424_data = {
        .reset_us = 1,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata realtek_rts5411_data = {
        .reset_us = 0,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata ti_tusb8041_data = {
        .reset_us = 3000,
+       .num_supplies = 1,
+};
+
+static const struct onboard_hub_pdata cypress_hx3_data = {
+       .reset_us = 10000,
+       .num_supplies = 2,
 };
 
 static const struct onboard_hub_pdata genesys_gl850g_data = {
        .reset_us = 3,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata genesys_gl852g_data = {
        .reset_us = 50,
+       .num_supplies = 1,
 };
 
 static const struct onboard_hub_pdata vialab_vl817_data = {
        .reset_us = 10,
+       .num_supplies = 1,
 };
 
 static const struct of_device_id onboard_hub_match[] = {
@@ -39,8 +51,11 @@ static const struct of_device_id onboard_hub_match[] = {
        { .compatible = "usb424,2517", .data = &microchip_usb424_data, },
        { .compatible = "usb451,8140", .data = &ti_tusb8041_data, },
        { .compatible = "usb451,8142", .data = &ti_tusb8041_data, },
+       { .compatible = "usb4b4,6504", .data = &cypress_hx3_data, },
+       { .compatible = "usb4b4,6506", .data = &cypress_hx3_data, },
        { .compatible = "usb5e3,608", .data = &genesys_gl850g_data, },
        { .compatible = "usb5e3,610", .data = &genesys_gl852g_data, },
+       { .compatible = "usb5e3,620", .data = &genesys_gl852g_data, },
        { .compatible = "usbbda,411", .data = &realtek_rts5411_data, },
        { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, },
        { .compatible = "usbbda,414", .data = &realtek_rts5411_data, },
index e4edb48..7da404f 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/nls.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 
index b4a7662..c11840b 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/extcon.h>
 #include <linux/interrupt.h>
 #include <linux/list.h>
+#include <linux/of.h>
 #include <linux/phy/phy.h>
 #include <linux/regulator/consumer.h>
 #include <linux/usb.h>
index 177d2ca..9f2be22 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/irq.h>
 #include <linux/kernel.h>
 #include <linux/mfd/syscon.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/regmap.h>
 
index 5aabdd7..b38df92 100644 (file)
@@ -10,7 +10,7 @@
 #include <linux/errno.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/usb/role.h>
index 598ee5c..0a35aab 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/clk.h>
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/usb/role.h>
index 24b9871..f0f56df 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/usb/usb_phy_generic.h>
 #include "musb_core.h"
index 9119b1d..98b42dc 100644 (file)
@@ -26,9 +26,7 @@
 #include <linux/sizes.h>
 
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/of_address.h>
-#include <linux/of_irq.h>
 #include <linux/usb/of.h>
 
 #include <linux/debugfs.h>
index c5c6c4e..d54283f 100644 (file)
@@ -15,7 +15,6 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 #include <linux/phy/phy-sun4i-usb.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
index cbc707f..4615876 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/usb.h>
 #include <linux/irq.h>
 #include <linux/io.h>
+#include <linux/iopoll.h>
 #include <linux/device.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
@@ -1029,7 +1030,7 @@ static int tusb_musb_start(struct musb *musb)
        void __iomem    *tbase = musb->ctrl_base;
        unsigned long   flags;
        u32             reg;
-       int             i;
+       int             ret;
 
        /*
         * Enable or disable power to TUSB6010. When enabling, turn on 3.3 V and
@@ -1037,17 +1038,13 @@ static int tusb_musb_start(struct musb *musb)
         * provide then PGOOD signal to TUSB6010 which will release it from reset.
         */
        gpiod_set_value(glue->enable, 1);
-       msleep(1);
 
        /* Wait for 100ms until TUSB6010 pulls INT pin down */
-       i = 100;
-       while (i && gpiod_get_value(glue->intpin)) {
-               msleep(1);
-               i--;
-       }
-       if (!i) {
-               pr_err("tusb: Powerup respones failed\n");
-               return -ENODEV;
+       ret = read_poll_timeout(gpiod_get_value, reg, !reg, 5000, 100000, true,
+                               glue->intpin);
+       if (ret) {
+               pr_err("tusb: Powerup response failed\n");
+               return ret;
        }
 
        spin_lock_irqsave(&musb->lock, flags);
index e1a2b2e..acd46b7 100644 (file)
@@ -14,7 +14,7 @@
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/io.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <linux/iopoll.h>
@@ -388,19 +388,14 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
 
 static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
 {
-       void __iomem *base = mxs_phy->phy.io_priv;
-       u32 phyctrl = readl(base + HW_USBPHY_CTRL);
-
-       if (IS_ENABLED(CONFIG_USB_OTG) &&
-                       !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
-               return true;
-
-       return false;
+       return IS_ENABLED(CONFIG_USB_OTG) &&
+               mxs_phy->phy.last_event == USB_EVENT_ID;
 }
 
 static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 {
        bool vbus_is_on = false;
+       enum usb_phy_events last_event = mxs_phy->phy.last_event;
 
        /* If the SoCs don't need to disconnect line without vbus, quit */
        if (!(mxs_phy->data->flags & MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS))
@@ -412,7 +407,8 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 
        vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 
-       if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+       if (on && ((!vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
+               || (last_event == USB_EVENT_VBUS)))
                __mxs_phy_disconnect_line(mxs_phy, true);
        else
                __mxs_phy_disconnect_line(mxs_phy, false);
index 8b2ff3a..4ea47e6 100644 (file)
@@ -16,7 +16,7 @@
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/of_platform.h>
 #include <linux/platform_device.h>
 #include <linux/resource.h>
 #include <linux/slab.h>
index 111b7ee..dd1c175 100644 (file)
@@ -11,7 +11,7 @@
 #include <linux/gpio/consumer.h>
 #include <linux/io.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/pm_runtime.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
index 2d77ede..97b5217 100644 (file)
@@ -8,7 +8,7 @@
 
 #include <linux/delay.h>
 #include <linux/io.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include "common.h"
 #include "rza.h"
 
index 3eed333..f079817 100644 (file)
@@ -8,7 +8,6 @@
 
 #include <linux/delay.h>
 #include <linux/io.h>
-#include <linux/of_device.h>
 #include <linux/phy/phy.h>
 #include "common.h"
 #include "rza.h"
index 5e8edf3..60ed1f8 100644 (file)
@@ -59,7 +59,7 @@ enum {
 };
 
 /* Common Mode Data bits */
-#define PMC_USB_ALTMODE_ACTIVE_CABLE   BIT(2)
+#define PMC_USB_ALTMODE_RETIMER_CABLE  BIT(2)
 
 #define PMC_USB_ALTMODE_ORI_SHIFT      1
 #define PMC_USB_ALTMODE_UFP_SHIFT      3
@@ -71,6 +71,7 @@ enum {
 #define PMC_USB_ALTMODE_TBT_TYPE       BIT(17)
 #define PMC_USB_ALTMODE_CABLE_TYPE     BIT(18)
 #define PMC_USB_ALTMODE_ACTIVE_LINK    BIT(20)
+#define PMC_USB_ALTMODE_ACTIVE_CABLE   BIT(22)
 #define PMC_USB_ALTMODE_FORCE_LSR      BIT(23)
 #define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25)
 #define   PMC_USB_ALTMODE_CABLE_USB31  1
@@ -117,6 +118,16 @@ enum {
          IOM_PORT_STATUS_DHPD_HPD_STATUS_SHIFT) &                      \
         IOM_PORT_STATUS_DHPD_HPD_STATUS_ASSERT)
 
+/* IOM port status register */
+#define IOM_PORT_STATUS_REGS(_offset_, _size_) ((_offset_) | (_size_))
+#define IOM_PORT_STATUS_REGS_SZ_MASK           BIT(0)
+#define IOM_PORT_STATUS_REGS_SZ_4              0
+#define IOM_PORT_STATUS_REGS_SZ_8              1
+#define IOM_PORT_STATUS_REGS_OFFSET(_d_)                               \
+       ((_d_) & ~IOM_PORT_STATUS_REGS_SZ_MASK)
+#define IOM_PORT_STATUS_REGS_SIZE(_d_)                                 \
+       (4 << ((_d_) & IOM_PORT_STATUS_REGS_SZ_MASK))
+
 struct pmc_usb;
 
 struct pmc_usb_port {
@@ -145,6 +156,7 @@ struct pmc_usb {
        struct acpi_device *iom_adev;
        void __iomem *iom_base;
        u32 iom_port_status_offset;
+       u8 iom_port_status_size;
 
        struct dentry *dentry;
 };
@@ -160,7 +172,7 @@ static void update_port_status(struct pmc_usb_port *port)
 
        port->iom_status = readl(port->pmc->iom_base +
                                 port->pmc->iom_port_status_offset +
-                                port_num * sizeof(u32));
+                                port_num * port->pmc->iom_port_status_size);
 }
 
 static int sbu_orientation(struct pmc_usb_port *port)
@@ -319,8 +331,18 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state)
        if (data->cable_mode & TBT_CABLE_LINK_TRAINING)
                req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK;
 
-       if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
-               req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+       if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+           acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL)) {
+               if ((data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE) ||
+                   (data->cable_mode & TBT_CABLE_RETIMER))
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+       } else {
+               if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
+                       req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+
+               if (data->cable_mode & TBT_CABLE_RETIMER)
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+       }
 
        req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed);
 
@@ -359,8 +381,17 @@ pmc_usb_mux_usb4(struct pmc_usb_port *port, struct typec_mux_state *state)
        case EUDO_CABLE_TYPE_OPTICAL:
                req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE;
                fallthrough;
+       case EUDO_CABLE_TYPE_RE_TIMER:
+               if (!acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+                   !acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL))
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+               fallthrough;
        default:
-               req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+               if (acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1072", NULL) ||
+                   acpi_dev_hid_uid_match(port->pmc->iom_adev, "INTC1079", NULL))
+                       req.mode_data |= PMC_USB_ALTMODE_RETIMER_CABLE;
+               else
+                       req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
 
                /* Configure data rate to rounded in the case of Active TBT3
                 * and USB4 cables.
@@ -589,13 +620,16 @@ err_unregister_switch:
 /* IOM ACPI IDs and IOM_PORT_STATUS_OFFSET */
 static const struct acpi_device_id iom_acpi_ids[] = {
        /* TigerLake */
-       { "INTC1072", 0x560, },
+       { "INTC1072", IOM_PORT_STATUS_REGS(0x560, IOM_PORT_STATUS_REGS_SZ_4) },
 
        /* AlderLake */
-       { "INTC1079", 0x160, },
+       { "INTC1079", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) },
 
        /* Meteor Lake */
-       { "INTC107A", 0x160, },
+       { "INTC107A", IOM_PORT_STATUS_REGS(0x160, IOM_PORT_STATUS_REGS_SZ_4) },
+
+       /* Lunar Lake */
+       { "INTC10EA", IOM_PORT_STATUS_REGS(0x150, IOM_PORT_STATUS_REGS_SZ_8) },
        {}
 };
 
@@ -615,7 +649,8 @@ static int pmc_usb_probe_iom(struct pmc_usb *pmc)
        if (!adev)
                return -ENODEV;
 
-       pmc->iom_port_status_offset = (u32)dev_id->driver_data;
+       pmc->iom_port_status_offset = IOM_PORT_STATUS_REGS_OFFSET(dev_id->driver_data);
+       pmc->iom_port_status_size = IOM_PORT_STATUS_REGS_SIZE(dev_id->driver_data);
 
        INIT_LIST_HEAD(&resource_list);
        ret = acpi_dev_get_memory_resources(adev, &resource_list);
index 80e580d..9360b65 100644 (file)
@@ -517,7 +517,7 @@ static struct i2c_driver nb7vpq904m_driver = {
                .name = "nb7vpq904m",
                .of_match_table = nb7vpq904m_of_table,
        },
-       .probe_new      = nb7vpq904m_probe,
+       .probe          = nb7vpq904m_probe,
        .remove         = nb7vpq904m_remove,
        .id_table       = nb7vpq904m_table,
 };
index 7fc1ffa..bc21006 100644 (file)
@@ -15,7 +15,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/proc_fs.h>
 #include <linux/regulator/consumer.h>
index 9b467a3..af44ee4 100644 (file)
@@ -8,7 +8,7 @@
 #include <linux/kernel.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
 #include <linux/of_graph.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
index 4e1b846..bb0b847 100644 (file)
@@ -8,8 +8,6 @@
 #include <linux/kernel.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/of_irq.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
index 94285f6..a8f3f4d 100644 (file)
@@ -9,7 +9,6 @@
 #include <linux/kernel.h>
 #include <linux/mod_devicetable.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
@@ -214,6 +213,11 @@ int qcom_pmic_typec_port_get_cc(struct pmic_typec_port *pmic_typec_port,
                if (ret)
                        goto done;
                switch (val & DETECTED_SRC_TYPE_MASK) {
+               case AUDIO_ACCESS_RA_RA:
+                       val = TYPEC_CC_RA;
+                       *cc1 = TYPEC_CC_RA;
+                       *cc2 = TYPEC_CC_RA;
+                       break;
                case SRC_RD_OPEN:
                        val = TYPEC_CC_RD;
                        break;
index 1fe9cb5..bb1854b 100644 (file)
@@ -5,7 +5,6 @@
  */
 #include <linux/auxiliary_bus.h>
 #include <linux/module.h>
-#include <linux/of_device.h>
 #include <linux/mutex.h>
 #include <linux/property.h>
 #include <linux/soc/qcom/pdr.h>
index e4de6bc..b513749 100644 (file)
@@ -144,6 +144,10 @@ struct usb_phy {
         */
        int     (*set_wakeup)(struct usb_phy *x, bool enabled);
 
+       /* notify phy port status change */
+       int     (*notify_port_status)(struct usb_phy *x, int port,
+                                     u16 portstatus, u16 portchange);
+
        /* notify phy connect status change */
        int     (*notify_connect)(struct usb_phy *x,
                        enum usb_device_speed speed);
@@ -317,6 +321,15 @@ usb_phy_set_wakeup(struct usb_phy *x, bool enabled)
 }
 
 static inline int
+usb_phy_notify_port_status(struct usb_phy *x, int port, u16 portstatus, u16 portchange)
+{
+       if (x && x->notify_port_status)
+               return x->notify_port_status(x, port, portstatus, portchange);
+       else
+               return 0;
+}
+
+static inline int
 usb_phy_notify_connect(struct usb_phy *x, enum usb_device_speed speed)
 {
        if (x && x->notify_connect)