Merge tag 'usb-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
authorLinus Torvalds <torvalds@linux-foundation.org>
Sun, 7 Jun 2020 16:42:16 +0000 (09:42 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sun, 7 Jun 2020 16:42:16 +0000 (09:42 -0700)
Pull USB/PHY driver updates from Greg KH:
 "Here are the large set of USB and PHY driver updates for 5.8-rc1.

  Nothing huge, just lots of little things:

   - USB gadget fixes and additions all over the place

   - new PHY drivers

   - PHY driver fixes and updates

   - XHCI driver updates

   - musb driver updates

   - more USB-serial driver ids added

   - various USB quirks added

   - thunderbolt minor updates and fixes

   - typec updates and additions

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-5.8-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (245 commits)
  usb: dwc3: meson-g12a: fix USB2 PHY initialization on G12A and A1 SoCs
  usb: dwc3: meson-g12a: fix error path when fetching the reset line fails
  Revert "dt-bindings: usb: qcom,dwc3: Convert USB DWC3 bindings"
  Revert "dt-bindings: usb: qcom,dwc3: Add compatible for SC7180"
  Revert "dt-bindings: usb: qcom,dwc3: Introduce interconnect properties for Qualcomm DWC3 driver"
  USB: serial: ch341: fix lockup of devices with limited prescaler
  USB: serial: ch341: add basis for quirk detection
  CDC-ACM: heed quirk also in error handling
  USB: serial: option: add Telit LE910C1-EUX compositions
  usb: musb: Fix runtime PM imbalance on error
  usb: musb: jz4740: Prevent lockup when CONFIG_SMP is set
  usb: musb: mediatek: add reset FADDR to zero in reset interrupt handle
  usb: musb: use true for 'use_dma'
  usb: musb: start session in resume for host port
  usb: musb: return -ESHUTDOWN in urb when three-strikes error happened
  USB: serial: qcserial: add DW5816e QDL support
  thunderbolt: Add trivial .shutdown
  usb: dwc3: keystone: Turn on USB3 PHY before controller
  dt-bindings: usb: ti,keystone-dwc3.yaml: Add USB3.0 PHY property
  dt-bindings: usb: convert keystone-usb.txt to YAML
  ...

298 files changed:
Documentation/devicetree/bindings/phy/amlogic,meson8b-usb2-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/cdns,salvo-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/intel,combo-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt [deleted file]
Documentation/devicetree/bindings/phy/meson8b-usb2-phy.txt [deleted file]
Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom,qmp-usb3-dp-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt [deleted file]
Documentation/devicetree/bindings/phy/qcom-usb-ipq4019-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt [deleted file]
Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt [deleted file]
Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/amlogic,dwc3.txt [deleted file]
Documentation/devicetree/bindings/usb/amlogic,meson-g12a-usb-ctrl.yaml
Documentation/devicetree/bindings/usb/aspeed,usb-vhub.yaml
Documentation/devicetree/bindings/usb/atmel-usb.txt
Documentation/devicetree/bindings/usb/brcm,bcm7445-ehci.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/dwc3.txt
Documentation/devicetree/bindings/usb/keystone-usb.txt [deleted file]
Documentation/devicetree/bindings/usb/nvidia,tegra-xudc.yaml
Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/ti,tps6598x.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/usb-xhci.txt
Documentation/firmware-guide/acpi/intel-pmc-mux.rst [new file with mode: 0644]
MAINTAINERS
arch/arm/boot/dts/at91sam9g45.dtsi
arch/arm/boot/dts/at91sam9rl.dtsi
arch/arm/boot/dts/at91sam9x5.dtsi
arch/arm/boot/dts/sama5d2.dtsi
arch/arm/boot/dts/sama5d3.dtsi
arch/arm/boot/dts/sama5d4.dtsi
arch/arm64/boot/dts/qcom/sc7180.dtsi
arch/arm64/boot/dts/qcom/sdm845.dtsi
drivers/gpio/gpiolib-of.c
drivers/phy/amlogic/Kconfig
drivers/phy/amlogic/Makefile
drivers/phy/amlogic/phy-meson-gxl-usb3.c [deleted file]
drivers/phy/amlogic/phy-meson8b-usb2.c
drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c
drivers/phy/broadcom/phy-bcm-sr-usb.c
drivers/phy/broadcom/phy-brcm-usb.c
drivers/phy/cadence/Kconfig
drivers/phy/cadence/Makefile
drivers/phy/cadence/phy-cadence-salvo.c [new file with mode: 0644]
drivers/phy/cadence/phy-cadence-sierra.c
drivers/phy/intel/Kconfig
drivers/phy/intel/Makefile
drivers/phy/intel/phy-intel-combo.c [new file with mode: 0644]
drivers/phy/motorola/phy-cpcap-usb.c
drivers/phy/qualcomm/Kconfig
drivers/phy/qualcomm/Makefile
drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c [new file with mode: 0644]
drivers/phy/qualcomm/phy-qcom-qmp.c
drivers/phy/qualcomm/phy-qcom-qmp.h
drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c [new file with mode: 0644]
drivers/phy/samsung/phy-s5pv210-usb2.c
drivers/phy/ti/phy-am654-serdes.c
drivers/phy/ti/phy-j721e-wiz.c
drivers/phy/ti/phy-omap-usb2.c
drivers/thunderbolt/Kconfig
drivers/thunderbolt/icm.c
drivers/thunderbolt/nhi.c
drivers/thunderbolt/nhi.h
drivers/thunderbolt/switch.c
drivers/usb/cdns3/cdns3-ti.c
drivers/usb/cdns3/core.c
drivers/usb/cdns3/core.h
drivers/usb/cdns3/drd.c
drivers/usb/cdns3/ep0.c
drivers/usb/cdns3/gadget.c
drivers/usb/chipidea/Kconfig
drivers/usb/chipidea/Makefile
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci_hdrc_imx.c
drivers/usb/chipidea/ci_hdrc_imx.h
drivers/usb/chipidea/ci_hdrc_usb2.c
drivers/usb/chipidea/ci_hdrc_zevio.c [deleted file]
drivers/usb/chipidea/core.c
drivers/usb/chipidea/udc.c
drivers/usb/chipidea/udc.h
drivers/usb/chipidea/usbmisc_imx.c
drivers/usb/class/cdc-acm.c
drivers/usb/class/usblp.c
drivers/usb/core/hcd-pci.c
drivers/usb/core/hub.c
drivers/usb/core/hub.h
drivers/usb/core/otg_whitelist.h
drivers/usb/core/sysfs.c
drivers/usb/core/usb.h
drivers/usb/dwc2/core.c
drivers/usb/dwc2/core.h
drivers/usb/dwc2/core_intr.c
drivers/usb/dwc2/debug.h
drivers/usb/dwc2/hcd.h
drivers/usb/dwc2/hw.h
drivers/usb/dwc2/params.c
drivers/usb/dwc2/platform.c
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/debug.h
drivers/usb/dwc3/debugfs.c
drivers/usb/dwc3/drd.c
drivers/usb/dwc3/dwc3-keystone.c
drivers/usb/dwc3/dwc3-meson-g12a.c
drivers/usb/dwc3/dwc3-of-simple.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/gadget.h
drivers/usb/dwc3/host.c
drivers/usb/dwc3/io.h
drivers/usb/dwc3/trace.h
drivers/usb/early/xhci-dbc.c
drivers/usb/early/xhci-dbc.h
drivers/usb/gadget/composite.c
drivers/usb/gadget/configfs.c
drivers/usb/gadget/function/f_acm.c
drivers/usb/gadget/function/f_eem.c
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/function/f_serial.c
drivers/usb/gadget/function/f_tcm.c
drivers/usb/gadget/function/f_uvc.h
drivers/usb/gadget/function/rndis.h
drivers/usb/gadget/function/u_audio.h
drivers/usb/gadget/function/u_ecm.h
drivers/usb/gadget/function/u_eem.h
drivers/usb/gadget/function/u_ether.h
drivers/usb/gadget/function/u_ether_configfs.h
drivers/usb/gadget/function/u_fs.h
drivers/usb/gadget/function/u_gether.h
drivers/usb/gadget/function/u_hid.h
drivers/usb/gadget/function/u_midi.h
drivers/usb/gadget/function/u_ncm.h
drivers/usb/gadget/function/u_phonet.h
drivers/usb/gadget/function/u_printer.h
drivers/usb/gadget/function/u_rndis.h
drivers/usb/gadget/function/u_serial.c
drivers/usb/gadget/function/u_serial.h
drivers/usb/gadget/function/u_tcm.h
drivers/usb/gadget/function/u_uac1.h
drivers/usb/gadget/function/u_uac1_legacy.h
drivers/usb/gadget/function/u_uac2.h
drivers/usb/gadget/function/u_uvc.h
drivers/usb/gadget/function/uvc.h
drivers/usb/gadget/function/uvc_configfs.h
drivers/usb/gadget/function/uvc_v4l2.c
drivers/usb/gadget/function/uvc_v4l2.h
drivers/usb/gadget/function/uvc_video.c
drivers/usb/gadget/function/uvc_video.h
drivers/usb/gadget/legacy/mass_storage.c
drivers/usb/gadget/udc/aspeed-vhub/core.c
drivers/usb/gadget/udc/aspeed-vhub/hub.c
drivers/usb/gadget/udc/aspeed-vhub/vhub.h
drivers/usb/gadget/udc/atmel_usba_udc.c
drivers/usb/gadget/udc/atmel_usba_udc.h
drivers/usb/gadget/udc/core.c
drivers/usb/gadget/udc/dummy_hcd.c
drivers/usb/gadget/udc/fsl_udc_core.c
drivers/usb/gadget/udc/gr_udc.c
drivers/usb/gadget/udc/lpc32xx_udc.c
drivers/usb/gadget/udc/m66592-udc.c
drivers/usb/gadget/udc/max3420_udc.c
drivers/usb/gadget/udc/mv_u3d_core.c
drivers/usb/gadget/udc/net2272.c
drivers/usb/gadget/udc/omap_udc.c
drivers/usb/gadget/udc/s3c2410_udc.c
drivers/usb/gadget/udc/tegra-xudc.c
drivers/usb/gadget/udc/udc-xilinx.c
drivers/usb/gadget/usbstring.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/ehci-brcm.c [new file with mode: 0644]
drivers/usb/host/ehci-fsl.h
drivers/usb/host/ehci-mv.c
drivers/usb/host/ehci-mxc.c
drivers/usb/host/ehci-pci.c
drivers/usb/host/ehci-platform.c
drivers/usb/host/ehci-tegra.c
drivers/usb/host/ehci.h
drivers/usb/host/fhci.h
drivers/usb/host/imx21-hcd.h
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci-platform.c
drivers/usb/host/ohci-sm501.c
drivers/usb/host/ohci.h
drivers/usb/host/pci-quirks.c
drivers/usb/host/r8a66597.h
drivers/usb/host/u132-hcd.c
drivers/usb/host/uhci-pci.c
drivers/usb/host/xhci-debugfs.h
drivers/usb/host/xhci-ext-caps.h
drivers/usb/host/xhci-mtk.h
drivers/usb/host/xhci-mvebu.h
drivers/usb/host/xhci-pci-renesas.c [new file with mode: 0644]
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-pci.h [new file with mode: 0644]
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-plat.h
drivers/usb/host/xhci-rcar.h
drivers/usb/host/xhci-trace.h
drivers/usb/host/xhci.h
drivers/usb/isp1760/isp1760-core.h
drivers/usb/isp1760/isp1760-regs.h
drivers/usb/isp1760/isp1760-udc.h
drivers/usb/misc/sisusbvga/sisusb.h
drivers/usb/misc/sisusbvga/sisusb_init.h
drivers/usb/misc/sisusbvga/sisusb_struct.h
drivers/usb/misc/usb_u132.h
drivers/usb/mtu3/mtu3.h
drivers/usb/mtu3/mtu3_debug.h
drivers/usb/mtu3/mtu3_dr.h
drivers/usb/mtu3/mtu3_hw_regs.h
drivers/usb/mtu3/mtu3_qmu.h
drivers/usb/mtu3/mtu3_trace.h
drivers/usb/musb/davinci.h
drivers/usb/musb/jz4740.c
drivers/usb/musb/mediatek.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_core.h
drivers/usb/musb/musb_debug.h
drivers/usb/musb/musb_debugfs.c
drivers/usb/musb/musb_dma.h
drivers/usb/musb/musb_gadget.h
drivers/usb/musb/musb_host.c
drivers/usb/musb/musb_host.h
drivers/usb/musb/musb_io.h
drivers/usb/musb/musb_regs.h
drivers/usb/musb/musb_trace.h
drivers/usb/musb/omap2430.h
drivers/usb/musb/tusb6010.h
drivers/usb/phy/phy-fsl-usb.h
drivers/usb/phy/phy-jz4770.c
drivers/usb/phy/phy-mv-usb.h
drivers/usb/renesas_usbhs/common.h
drivers/usb/renesas_usbhs/fifo.h
drivers/usb/renesas_usbhs/mod.h
drivers/usb/renesas_usbhs/pipe.h
drivers/usb/renesas_usbhs/rcar2.h
drivers/usb/renesas_usbhs/rcar3.h
drivers/usb/renesas_usbhs/rza.h
drivers/usb/roles/class.c
drivers/usb/serial/belkin_sa.h
drivers/usb/serial/ch341.c
drivers/usb/serial/io_16654.h
drivers/usb/serial/io_edgeport.h
drivers/usb/serial/io_ionsp.h
drivers/usb/serial/io_ti.h
drivers/usb/serial/io_usbvend.h
drivers/usb/serial/iuu_phoenix.h
drivers/usb/serial/mct_u232.h
drivers/usb/serial/option.c
drivers/usb/serial/oti6858.h
drivers/usb/serial/pl2303.h
drivers/usb/serial/qcserial.c
drivers/usb/serial/usb_wwan.c
drivers/usb/serial/visor.h
drivers/usb/serial/whiteheat.h
drivers/usb/storage/debug.h
drivers/usb/storage/initializers.h
drivers/usb/storage/protocol.h
drivers/usb/storage/scsiglue.h
drivers/usb/storage/sierra_ms.c
drivers/usb/storage/transport.h
drivers/usb/storage/unusual_alauda.h
drivers/usb/storage/unusual_cypress.h
drivers/usb/storage/unusual_datafab.h
drivers/usb/storage/unusual_devs.h
drivers/usb/storage/unusual_ene_ub6250.h
drivers/usb/storage/unusual_freecom.h
drivers/usb/storage/unusual_isd200.h
drivers/usb/storage/unusual_jumpshot.h
drivers/usb/storage/unusual_karma.h
drivers/usb/storage/unusual_onetouch.h
drivers/usb/storage/unusual_realtek.h
drivers/usb/storage/unusual_sddr09.h
drivers/usb/storage/unusual_sddr55.h
drivers/usb/storage/unusual_uas.h
drivers/usb/storage/unusual_usbat.h
drivers/usb/storage/usb.h
drivers/usb/typec/Kconfig
drivers/usb/typec/class.c
drivers/usb/typec/mux/intel_pmc_mux.c
drivers/usb/typec/tcpm/fusb302.c
drivers/usb/typec/tcpm/fusb302_reg.h
drivers/usb/typec/tps6598x.c
drivers/usb/typec/ucsi/Makefile
drivers/usb/typec/ucsi/psy.c [new file with mode: 0644]
drivers/usb/typec/ucsi/trace.c
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h
include/dt-bindings/phy/phy.h
include/linux/phy/omap_usb.h
include/linux/thunderbolt.h
include/linux/usb/chipidea.h
include/linux/usb/composite.h
include/linux/usb/gadget.h
include/linux/usb/hcd.h
include/linux/usb/typec.h

diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson8b-usb2-phy.yaml b/Documentation/devicetree/bindings/phy/amlogic,meson8b-usb2-phy.yaml
new file mode 100644 (file)
index 0000000..03c4809
--- /dev/null
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/amlogic,meson8b-usb2-phy.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Amlogic Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY
+
+maintainers:
+  - Martin Blumenstingl <martin.blumenstingl@googlemail.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - enum:
+              - amlogic,meson8-usb2-phy
+              - amlogic,meson8b-usb2-phy
+              - amlogic,meson8m2-usb2-phy
+          - const: amlogic,meson-mx-usb2-phy
+      - const: amlogic,meson-gxbb-usb2-phy
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 2
+
+  clock-names:
+    items:
+      - const: usb_general
+      - const: usb
+
+  resets:
+    minItems: 1
+
+  "#phy-cells":
+    const: 0
+
+  phy-supply:
+    description:
+      Phandle to a regulator that provides power to the PHY. This
+      regulator will be managed during the PHY power on/off sequence.
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    usb-phy@c0000000 {
+      compatible = "amlogic,meson-gxbb-usb2-phy";
+      reg = <0xc0000000 0x20>;
+      resets = <&reset_usb_phy>;
+      clocks = <&clk_usb_general>, <&reset_usb>;
+      clock-names = "usb_general", "usb";
+      phy-supply = <&usb_vbus>;
+      #phy-cells = <0>;
+    };
diff --git a/Documentation/devicetree/bindings/phy/cdns,salvo-phy.yaml b/Documentation/devicetree/bindings/phy/cdns,salvo-phy.yaml
new file mode 100644 (file)
index 0000000..3a07285
--- /dev/null
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+# Copyright (c) 2020 NXP
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/cdns,salvo-phy.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Cadence SALVO PHY
+
+maintainers:
+  - Peter Chen <peter.chen@nxp.com>
+
+properties:
+  compatible:
+    enum:
+      - nxp,salvo-phy
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: salvo_phy_clk
+
+  power-domains:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/firmware/imx/rsrc.h>
+
+    usb3phy: usb3-phy@5b160000 {
+        compatible = "nxp,salvo-phy";
+        reg = <0x5b160000 0x40000>;
+        clocks = <&usb3_lpcg 4>;
+        clock-names = "salvo_phy_clk";
+        power-domains = <&pd IMX_SC_R_USB_2_PHY>;
+        #phy-cells = <0>;
+    };
diff --git a/Documentation/devicetree/bindings/phy/intel,combo-phy.yaml b/Documentation/devicetree/bindings/phy/intel,combo-phy.yaml
new file mode 100644 (file)
index 0000000..347d0cd
--- /dev/null
@@ -0,0 +1,101 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/intel,combo-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel ComboPhy Subsystem
+
+maintainers:
+  - Dilip Kota <eswara.kota@linux.intel.com>
+
+description: |
+  Intel Combophy subsystem supports PHYs for PCIe, EMAC and SATA
+  controllers. A single Combophy provides two PHY instances.
+
+properties:
+  $nodename:
+    pattern: "combophy(@.*|-[0-9a-f])*$"
+
+  compatible:
+    items:
+      - const: intel,combophy-lgm
+      - const: intel,combo-phy
+
+  clocks:
+    maxItems: 1
+
+  reg:
+    items:
+      - description: ComboPhy core registers
+      - description: PCIe app core control registers
+
+  reg-names:
+    items:
+      - const: core
+      - const: app
+
+  resets:
+    maxItems: 4
+
+  reset-names:
+    items:
+      - const: phy
+      - const: core
+      - const: iphy0
+      - const: iphy1
+
+  intel,syscfg:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: Chip configuration registers handle and ComboPhy instance id
+
+  intel,hsio:
+    $ref: /schemas/types.yaml#/definitions/phandle-array
+    description: HSIO registers handle and ComboPhy instance id on NOC
+
+  intel,aggregation:
+    type: boolean
+    description: |
+      Specify the flag to configure ComboPHY in dual lane mode.
+
+  intel,phy-mode:
+    $ref: /schemas/types.yaml#/definitions/uint32
+    description: |
+      Mode of the two phys in ComboPhy.
+      See dt-bindings/phy/phy.h for values.
+
+  "#phy-cells":
+    const: 1
+
+required:
+  - compatible
+  - clocks
+  - reg
+  - reg-names
+  - intel,syscfg
+  - intel,hsio
+  - intel,phy-mode
+  - "#phy-cells"
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/phy/phy.h>
+    combophy@d0a00000 {
+        compatible = "intel,combophy-lgm", "intel,combo-phy";
+        clocks = <&cgu0 1>;
+        #phy-cells = <1>;
+        reg = <0xd0a00000 0x40000>,
+              <0xd0a40000 0x1000>;
+        reg-names = "core", "app";
+        resets = <&rcu0 0x50 6>,
+                 <&rcu0 0x50 17>,
+                 <&rcu0 0x50 23>,
+                 <&rcu0 0x50 24>;
+        reset-names = "phy", "core", "iphy0", "iphy1";
+        intel,syscfg = <&sysconf 0>;
+        intel,hsio = <&hsiol 0>;
+        intel,phy-mode = <PHY_TYPE_PCIE>;
+        intel,aggregation;
+    };
diff --git a/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt b/Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
deleted file mode 100644 (file)
index 114947e..0000000
+++ /dev/null
@@ -1,31 +0,0 @@
-* Amlogic Meson GXL and GXM USB3 PHY and OTG detection binding
-
-Required properties:
-- compatible:  Should be "amlogic,meson-gxl-usb3-phy"
-- #phys-cells: must be 0 (see phy-bindings.txt in this directory)
-- reg:         The base address and length of the registers
-- interrupts:  the interrupt specifier for the OTG detection
-- clocks:      phandles to the clocks for
-               - the USB3 PHY
-               - and peripheral mode/OTG detection
-- clock-names: must contain "phy" and "peripheral"
-- resets:      phandle to the reset lines for:
-               - the USB3 PHY and
-               - peripheral mode/OTG detection
-- reset-names: must contain "phy" and "peripheral"
-
-Optional properties:
-- phy-supply:  see phy-bindings.txt in this directory
-
-
-Example:
-       usb3_phy0: phy@78080 {
-               compatible = "amlogic,meson-gxl-usb3-phy";
-               #phy-cells = <0>;
-               reg = <0x0 0x78080 0x0 0x20>;
-               interrupts = <GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
-               clocks = <&clkc CLKID_USB_OTG>, <&clkc_AO CLKID_AO_CEC_32K>;
-               clock-names = "phy", "peripheral";
-               resets = <&reset RESET_USB_OTG>, <&reset RESET_USB_OTG>;
-               reset-names = "phy", "peripheral";
-       };
diff --git a/Documentation/devicetree/bindings/phy/meson8b-usb2-phy.txt b/Documentation/devicetree/bindings/phy/meson8b-usb2-phy.txt
deleted file mode 100644 (file)
index d81d73a..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-* Amlogic Meson8, Meson8b and GXBB USB2 PHY
-
-Required properties:
-- compatible:  Depending on the platform this should be one of:
-       "amlogic,meson8-usb2-phy"
-       "amlogic,meson8b-usb2-phy"
-       "amlogic,meson-gxbb-usb2-phy"
-- reg:         The base address and length of the registers
-- #phys-cells: should be 0 (see phy-bindings.txt in this directory)
-- clocks:      phandle and clock identifier for the phy clocks
-- clock-names: "usb_general" and "usb"
-
-Optional properties:
-- resets:      reference to the reset controller
-- phy-supply:  see phy-bindings.txt in this directory
-
-
-Example:
-
-usb0_phy: usb-phy@c0000000 {
-       compatible = "amlogic,meson-gxbb-usb2-phy";
-       #phy-cells = <0>;
-       reg = <0x0 0xc0000000 0x0 0x20>;
-       resets = <&reset RESET_USB_OTG>;
-       clocks = <&clkc CLKID_USB>, <&clkc CLKID_USB0>;
-       clock-names = "usb_general", "usb";
-       phy-supply = <&usb_vbus>;
-};
diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-phy.yaml
new file mode 100644 (file)
index 0000000..973b2d1
--- /dev/null
@@ -0,0 +1,313 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/qcom,qmp-phy.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcomm QMP PHY controller
+
+maintainers:
+  - Manu Gautam <mgautam@codeaurora.org>
+
+description:
+  QMP phy controller supports physical layer functionality for a number of
+  controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
+
+properties:
+  compatible:
+    enum:
+      - qcom,ipq8074-qmp-pcie-phy
+      - qcom,msm8996-qmp-pcie-phy
+      - qcom,msm8996-qmp-ufs-phy
+      - qcom,msm8996-qmp-usb3-phy
+      - qcom,msm8998-qmp-pcie-phy
+      - qcom,msm8998-qmp-ufs-phy
+      - qcom,msm8998-qmp-usb3-phy
+      - qcom,sdm845-qhp-pcie-phy
+      - qcom,sdm845-qmp-pcie-phy
+      - qcom,sdm845-qmp-ufs-phy
+      - qcom,sdm845-qmp-usb3-uni-phy
+      - qcom,sm8150-qmp-ufs-phy
+      - qcom,sm8250-qmp-ufs-phy
+
+  reg:
+    items:
+      - description: Address and length of PHY's common serdes block.
+
+  "#clock-cells":
+     enum: [ 1, 2 ]
+
+  "#address-cells":
+    enum: [ 1, 2 ]
+
+  "#size-cells":
+    enum: [ 1, 2 ]
+
+  clocks:
+    minItems: 1
+    maxItems: 4
+
+  clock-names:
+    minItems: 1
+    maxItems: 4
+
+  resets:
+    minItems: 1
+    maxItems: 3
+
+  reset-names:
+    minItems: 1
+    maxItems: 3
+
+  vdda-phy-supply:
+    description:
+        Phandle to a regulator supply to PHY core block.
+
+  vdda-pll-supply:
+    description:
+        Phandle to 1.8V regulator supply to PHY refclk pll block.
+
+  vddp-ref-clk-supply:
+    description:
+        Phandle to a regulator supply to any specific refclk
+        pll block.
+
+#Required nodes:
+patternProperties:
+  "^phy@[0-9a-f]+$":
+    type: object
+    description:
+      Each device node of QMP phy is required to have as many child nodes as
+      the number of lanes the PHY has.
+
+required:
+  - compatible
+  - reg
+  - "#clock-cells"
+  - "#address-cells"
+  - "#size-cells"
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - vdda-phy-supply
+  - vdda-pll-supply
+
+additionalProperties: false
+
+allOf:
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sdm845-qmp-usb3-uni-phy
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Phy aux clock.
+            - description: Phy config clock.
+            - description: 19.2 MHz ref clk.
+            - description: Phy common block aux clock.
+        clock-names:
+          items:
+            - const: aux
+            - const: cfg_ahb
+            - const: ref
+            - const: com_aux
+        resets:
+          items:
+            - description: reset of phy block.
+            - description: phy common block reset.
+        reset-names:
+          items:
+            - const: phy
+            - const: common
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,msm8996-qmp-pcie-phy
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Phy aux clock.
+            - description: Phy config clock.
+            - description: 19.2 MHz ref clk.
+        clock-names:
+          items:
+            - const: aux
+            - const: cfg_ahb
+            - const: ref
+        resets:
+          items:
+            - description: reset of phy block.
+            - description: phy common block reset.
+            - description: phy's ahb cfg block reset.
+        reset-names:
+          items:
+            - const: phy
+            - const: common
+            - const: cfg
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,msm8996-qmp-usb3-phy
+              - qcom,msm8998-qmp-pcie-phy
+              - qcom,msm8998-qmp-usb3-phy
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Phy aux clock.
+            - description: Phy config clock.
+            - description: 19.2 MHz ref clk.
+        clock-names:
+          items:
+            - const: aux
+            - const: cfg_ahb
+            - const: ref
+        resets:
+          items:
+            - description: reset of phy block.
+            - description: phy common block reset.
+        reset-names:
+          items:
+             - const: phy
+             - const: common
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,msm8996-qmp-ufs-phy
+    then:
+      properties:
+        clocks:
+          items:
+            - description: 19.2 MHz ref clk.
+        clock-names:
+          items:
+            - const: ref
+        resets:
+          items:
+            - description: PHY reset in the UFS controller.
+        reset-names:
+          items:
+            - const: ufsphy
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,msm8998-qmp-ufs-phy
+              - qcom,sdm845-qmp-ufs-phy
+              - qcom,sm8150-qmp-ufs-phy
+              - qcom,sm8250-qmp-ufs-phy
+    then:
+      properties:
+        clocks:
+          items:
+            - description: 19.2 MHz ref clk.
+            - description: Phy reference aux clock.
+        clock-names:
+          items:
+            - const: ref
+            - const: ref_aux
+        resets:
+          items:
+            - description: PHY reset in the UFS controller.
+        reset-names:
+          items:
+            - const: ufsphy
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,ipq8074-qmp-pcie-phy
+    then:
+      properties:
+        clocks:
+          items:
+            - description: pipe clk.
+        clock-names:
+          items:
+            - const: pipe_clk
+        resets:
+          items:
+            - description: reset of phy block.
+            - description: phy common block reset.
+        reset-names:
+          items:
+            - const: phy
+            - const: common
+  - if:
+      properties:
+        compatible:
+          contains:
+            enum:
+              - qcom,sdm845-qhp-pcie-phy
+              - qcom,sdm845-qmp-pcie-phy
+    then:
+      properties:
+        clocks:
+          items:
+            - description: Phy aux clock.
+            - description: Phy config clock.
+            - description: 19.2 MHz ref clk.
+            - description: Phy refgen clk.
+        clock-names:
+          items:
+            - const: aux
+            - const: cfg_ahb
+            - const: ref
+            - const: refgen
+        resets:
+          items:
+            - description: reset of phy block.
+        reset-names:
+          items:
+            - const: phy
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-sdm845.h>
+    usb_2_qmpphy: phy-wrapper@88eb000 {
+        compatible = "qcom,sdm845-qmp-usb3-uni-phy";
+        reg = <0 0x088eb000 0 0x18c>;
+        #clock-cells = <1>;
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK >,
+                 <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+                 <&gcc GCC_USB3_SEC_CLKREF_CLK>,
+                 <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>;
+        clock-names = "aux", "cfg_ahb", "ref", "com_aux";
+
+        resets = <&gcc GCC_USB3PHY_PHY_SEC_BCR>,
+                 <&gcc GCC_USB3_PHY_SEC_BCR>;
+        reset-names = "phy", "common";
+
+        vdda-phy-supply = <&vdda_usb2_ss_1p2>;
+        vdda-pll-supply = <&vdda_usb2_ss_core>;
+
+        usb_2_ssphy: phy@88eb200 {
+                reg = <0 0x088eb200 0 0x128>,
+                      <0 0x088eb400 0 0x1fc>,
+                      <0 0x088eb800 0 0x218>,
+                      <0 0x088eb600 0 0x70>;
+                #clock-cells = <0>;
+                #phy-cells = <0>;
+                clocks = <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
+                clock-names = "pipe0";
+                clock-output-names = "usb3_uni_phy_pipe_clk_src";
+            };
+        };
diff --git a/Documentation/devicetree/bindings/phy/qcom,qmp-usb3-dp-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qmp-usb3-dp-phy.yaml
new file mode 100644 (file)
index 0000000..b770e63
--- /dev/null
@@ -0,0 +1,136 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/qcom,qmp-usb3-dp-phy.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcomm QMP USB3 DP PHY controller
+
+maintainers:
+  - Manu Gautam <mgautam@codeaurora.org>
+
+properties:
+  compatible:
+    enum:
+      - qcom,sc7180-qmp-usb3-phy
+      - qcom,sdm845-qmp-usb3-phy
+  reg:
+    items:
+      - description: Address and length of PHY's common serdes block.
+      - description: Address and length of the DP_COM control block.
+
+  reg-names:
+    items:
+      - const: reg-base
+      - const: dp_com
+
+  "#clock-cells":
+     enum: [ 1, 2 ]
+
+  "#address-cells":
+    enum: [ 1, 2 ]
+
+  "#size-cells":
+    enum: [ 1, 2 ]
+
+  clocks:
+    items:
+      - description: Phy aux clock.
+      - description: Phy config clock.
+      - description: 19.2 MHz ref clk.
+      - description: Phy common block aux clock.
+
+  clock-names:
+    items:
+      - const: aux
+      - const: cfg_ahb
+      - const: ref
+      - const: com_aux
+
+  resets:
+    items:
+      - description: reset of phy block.
+      - description: phy common block reset.
+
+  reset-names:
+    items:
+      - const: phy
+      - const: common
+
+  vdda-phy-supply:
+    description:
+        Phandle to a regulator supply to PHY core block.
+
+  vdda-pll-supply:
+    description:
+        Phandle to 1.8V regulator supply to PHY refclk pll block.
+
+  vddp-ref-clk-supply:
+    description:
+        Phandle to a regulator supply to any specific refclk
+        pll block.
+
+#Required nodes:
+patternProperties:
+  "^phy@[0-9a-f]+$":
+    type: object
+    description:
+      Each device node of QMP phy is required to have as many child nodes as
+      the number of lanes the PHY has.
+
+required:
+  - compatible
+  - reg
+  - reg-names
+  - "#clock-cells"
+  - "#address-cells"
+  - "#size-cells"
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - vdda-phy-supply
+  - vdda-pll-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-sdm845.h>
+    usb_1_qmpphy: phy-wrapper@88e9000 {
+        compatible = "qcom,sdm845-qmp-usb3-phy";
+        reg = <0 0x088e9000 0 0x18c>,
+              <0 0x088e8000 0 0x10>;
+        reg-names = "reg-base", "dp_com";
+        #clock-cells = <1>;
+        #address-cells = <2>;
+        #size-cells = <2>;
+
+        clocks = <&gcc GCC_USB3_PRIM_PHY_AUX_CLK>,
+                 <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+                 <&gcc GCC_USB3_PRIM_CLKREF_CLK>,
+                 <&gcc GCC_USB3_PRIM_PHY_COM_AUX_CLK>;
+        clock-names = "aux", "cfg_ahb", "ref", "com_aux";
+
+        resets = <&gcc GCC_USB3_PHY_PRIM_BCR>,
+                 <&gcc GCC_USB3_DP_PHY_PRIM_BCR>;
+        reset-names = "phy", "common";
+
+        vdda-phy-supply = <&vdda_usb2_ss_1p2>;
+        vdda-pll-supply = <&vdda_usb2_ss_core>;
+
+        usb_1_ssphy: phy@88e9200 {
+                reg = <0 0x088e9200 0 0x128>,
+                      <0 0x088e9400 0 0x200>,
+                      <0 0x088e9c00 0 0x218>,
+                      <0 0x088e9600 0 0x128>,
+                      <0 0x088e9800 0 0x200>,
+                      <0 0x088e9a00 0 0x100>;
+                #clock-cells = <0>;
+                #phy-cells = <0>;
+                clocks = <&gcc GCC_USB3_PRIM_PHY_PIPE_CLK>;
+                clock-names = "pipe0";
+                clock-output-names = "usb3_phy_pipe_clk_src";
+            };
+        };
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-snps-femto-v2.yaml
new file mode 100644 (file)
index 0000000..574f890
--- /dev/null
@@ -0,0 +1,80 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/qcom,usb-snps-femto-v2.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcomm Synopsys Femto High-Speed USB PHY V2
+
+maintainers:
+  - Wesley Cheng <wcheng@codeaurora.org>
+
+description: |
+  Qualcomm High-Speed USB PHY
+
+properties:
+  compatible:
+    enum:
+      - qcom,usb-snps-hs-7nm-phy
+      - qcom,sm8150-usb-hs-phy
+      - qcom,usb-snps-femto-v2-phy
+
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+  clocks:
+    items:
+      - description: rpmhcc ref clock
+
+  clock-names:
+    items:
+      - const: ref
+
+  resets:
+    items:
+      - description: PHY core reset
+
+  vdda-pll-supply:
+    description: phandle to the regulator VDD supply node.
+
+  vdda18-supply:
+    description: phandle to the regulator 1.8V supply node.
+
+  vdda33-supply:
+    description: phandle to the regulator 3.3V supply node.
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+  - clocks
+  - clock-names
+  - resets
+  - vdda-pll-supply
+  - vdda18-supply
+  - vdda33-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,rpmh.h>
+    #include <dt-bindings/clock/qcom,gcc-sm8150.h>
+    phy@88e2000 {
+        compatible = "qcom,sm8150-usb-hs-phy";
+        reg = <0 0x088e2000 0 0x400>;
+        #phy-cells = <0>;
+
+        clocks = <&rpmhcc RPMH_CXO_CLK>;
+        clock-names = "ref";
+
+        resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>;
+
+        vdda-pll-supply = <&vdd_usb_hs_core>;
+        vdda33-supply = <&vdda_usb_hs_3p1>;
+        vdda18-supply = <&vdda_usb_hs_1p8>;
+    };
+...
diff --git a/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
deleted file mode 100644 (file)
index 54d6f8d..0000000
+++ /dev/null
@@ -1,242 +0,0 @@
-Qualcomm QMP PHY controller
-===========================
-
-QMP phy controller supports physical layer functionality for a number of
-controllers on Qualcomm chipsets, such as, PCIe, UFS, and USB.
-
-Required properties:
- - compatible: compatible list, contains:
-              "qcom,ipq8074-qmp-pcie-phy" for PCIe phy on IPQ8074
-              "qcom,msm8996-qmp-pcie-phy" for 14nm PCIe phy on msm8996,
-              "qcom,msm8996-qmp-ufs-phy" for 14nm UFS phy on msm8996,
-              "qcom,msm8996-qmp-usb3-phy" for 14nm USB3 phy on msm8996,
-              "qcom,msm8998-qmp-usb3-phy" for USB3 QMP V3 phy on msm8998,
-              "qcom,msm8998-qmp-ufs-phy" for UFS QMP phy on msm8998,
-              "qcom,msm8998-qmp-pcie-phy" for PCIe QMP phy on msm8998,
-              "qcom,sdm845-qhp-pcie-phy" for QHP PCIe phy on sdm845,
-              "qcom,sdm845-qmp-pcie-phy" for QMP PCIe phy on sdm845,
-              "qcom,sdm845-qmp-usb3-phy" for USB3 QMP V3 phy on sdm845,
-              "qcom,sdm845-qmp-usb3-uni-phy" for USB3 QMP V3 UNI phy on sdm845,
-              "qcom,sdm845-qmp-ufs-phy" for UFS QMP phy on sdm845,
-              "qcom,sm8150-qmp-ufs-phy" for UFS QMP phy on sm8150.
-
-- reg:
-  - index 0: address and length of register set for PHY's common
-             serdes block.
-  - index 1: address and length of the DP_COM control block (for
-             "qcom,sdm845-qmp-usb3-phy" only).
-
-- reg-names:
-  - For "qcom,sdm845-qmp-usb3-phy":
-    - Should be: "reg-base", "dp_com"
-  - For all others:
-    - The reg-names property shouldn't be defined.
-
- - #address-cells: must be 1
- - #size-cells: must be 1
- - ranges: must be present
-
- - clocks: a list of phandles and clock-specifier pairs,
-          one for each entry in clock-names.
- - clock-names: "cfg_ahb" for phy config clock,
-               "aux" for phy aux clock,
-               "ref" for 19.2 MHz ref clk,
-               "com_aux" for phy common block aux clock,
-               "ref_aux" for phy reference aux clock,
-
-               For "qcom,ipq8074-qmp-pcie-phy": no clocks are listed.
-               For "qcom,msm8996-qmp-pcie-phy" must contain:
-                       "aux", "cfg_ahb", "ref".
-               For "qcom,msm8996-qmp-ufs-phy" must contain:
-                       "ref".
-               For "qcom,msm8996-qmp-usb3-phy" must contain:
-                       "aux", "cfg_ahb", "ref".
-               For "qcom,msm8998-qmp-usb3-phy" must contain:
-                       "aux", "cfg_ahb", "ref".
-               For "qcom,msm8998-qmp-ufs-phy" must contain:
-                       "ref", "ref_aux".
-               For "qcom,msm8998-qmp-pcie-phy" must contain:
-                       "aux", "cfg_ahb", "ref".
-               For "qcom,sdm845-qhp-pcie-phy" must contain:
-                       "aux", "cfg_ahb", "ref", "refgen".
-               For "qcom,sdm845-qmp-pcie-phy" must contain:
-                       "aux", "cfg_ahb", "ref", "refgen".
-               For "qcom,sdm845-qmp-usb3-phy" must contain:
-                       "aux", "cfg_ahb", "ref", "com_aux".
-               For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
-                       "aux", "cfg_ahb", "ref", "com_aux".
-               For "qcom,sdm845-qmp-ufs-phy" must contain:
-                       "ref", "ref_aux".
-               For "qcom,sm8150-qmp-ufs-phy" must contain:
-                       "ref", "ref_aux".
-
- - resets: a list of phandles and reset controller specifier pairs,
-          one for each entry in reset-names.
- - reset-names: "phy" for reset of phy block,
-               "common" for phy common block reset,
-               "cfg" for phy's ahb cfg block reset,
-               "ufsphy" for the PHY reset in the UFS controller.
-
-               For "qcom,ipq8074-qmp-pcie-phy" must contain:
-                       "phy", "common".
-               For "qcom,msm8996-qmp-pcie-phy" must contain:
-                       "phy", "common", "cfg".
-               For "qcom,msm8996-qmp-ufs-phy": must contain:
-                       "ufsphy".
-               For "qcom,msm8996-qmp-usb3-phy" must contain
-                       "phy", "common".
-               For "qcom,msm8998-qmp-usb3-phy" must contain
-                       "phy", "common".
-               For "qcom,msm8998-qmp-ufs-phy": must contain:
-                       "ufsphy".
-               For "qcom,msm8998-qmp-pcie-phy" must contain:
-                       "phy", "common".
-               For "qcom,sdm845-qhp-pcie-phy" must contain:
-                       "phy".
-               For "qcom,sdm845-qmp-pcie-phy" must contain:
-                       "phy".
-               For "qcom,sdm845-qmp-usb3-phy" must contain:
-                       "phy", "common".
-               For "qcom,sdm845-qmp-usb3-uni-phy" must contain:
-                       "phy", "common".
-               For "qcom,sdm845-qmp-ufs-phy": must contain:
-                       "ufsphy".
-               For "qcom,sm8150-qmp-ufs-phy": must contain:
-                       "ufsphy".
-
- - vdda-phy-supply: Phandle to a regulator supply to PHY core block.
- - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
-
-Optional properties:
- - vddp-ref-clk-supply: Phandle to a regulator supply to any specific refclk
-                       pll block.
-
-Required nodes:
- - Each device node of QMP phy is required to have as many child nodes as
-   the number of lanes the PHY has.
-
-Required properties for child nodes of PCIe PHYs (one child per lane):
- - reg: list of offset and length pairs of register sets for PHY blocks -
-       tx, rx, pcs, and pcs_misc (optional).
- - #phy-cells: must be 0
-
-Required properties for a single "lanes" child node of non-PCIe PHYs:
- - reg: list of offset and length pairs of register sets for PHY blocks
-       For 1-lane devices:
-               tx, rx, pcs, and (optionally) pcs_misc
-       For 2-lane devices:
-               tx0, rx0, pcs, tx1, rx1, and (optionally) pcs_misc
- - #phy-cells: must be 0
-
-Required properties for child node of PCIe and USB3 qmp phys:
- - clocks: a list of phandles and clock-specifier pairs,
-          one for each entry in clock-names.
- - clock-names: Must contain following:
-                "pipe<lane-number>" for pipe clock specific to each lane.
- - clock-output-names: Name of the PHY clock that will be the parent for
-                      the above pipe clock.
-       For "qcom,ipq8074-qmp-pcie-phy":
-               - "pcie20_phy0_pipe_clk"        Pipe Clock parent
-                       (or)
-                 "pcie20_phy1_pipe_clk"
- - #clock-cells: must be 0
-    - Phy pll outputs pipe clocks for pipe based PHYs. These clocks are then
-      gate-controlled by the gcc.
-
-Required properties for child node of PHYs with lane reset, AKA:
-       "qcom,msm8996-qmp-pcie-phy"
- - resets: a list of phandles and reset controller specifier pairs,
-          one for each entry in reset-names.
- - reset-names: Must contain following:
-                "lane<lane-number>" for reset specific to each lane.
-
-Example:
-       phy@34000 {
-               compatible = "qcom,msm8996-qmp-pcie-phy";
-               reg = <0x34000 0x488>;
-               #address-cells = <1>;
-               #size-cells = <1>;
-               ranges;
-
-               clocks = <&gcc GCC_PCIE_PHY_AUX_CLK>,
-                       <&gcc GCC_PCIE_PHY_CFG_AHB_CLK>,
-                       <&gcc GCC_PCIE_CLKREF_CLK>;
-               clock-names = "aux", "cfg_ahb", "ref";
-
-               vdda-phy-supply = <&pm8994_l28>;
-               vdda-pll-supply = <&pm8994_l12>;
-
-               resets = <&gcc GCC_PCIE_PHY_BCR>,
-                       <&gcc GCC_PCIE_PHY_COM_BCR>,
-                       <&gcc GCC_PCIE_PHY_COM_NOCSR_BCR>;
-               reset-names = "phy", "common", "cfg";
-
-               pciephy_0: lane@35000 {
-                       reg = <0x35000 0x130>,
-                               <0x35200 0x200>,
-                               <0x35400 0x1dc>;
-                       #clock-cells = <0>;
-                       #phy-cells = <0>;
-
-                       clocks = <&gcc GCC_PCIE_0_PIPE_CLK>;
-                       clock-names = "pipe0";
-                       clock-output-names = "pcie_0_pipe_clk_src";
-                       resets = <&gcc GCC_PCIE_0_PHY_BCR>;
-                       reset-names = "lane0";
-               };
-
-               pciephy_1: lane@36000 {
-               ...
-               ...
-       };
-
-       phy@88eb000 {
-               compatible = "qcom,sdm845-qmp-usb3-uni-phy";
-               reg = <0x88eb000 0x18c>;
-               #address-cells = <1>;
-               #size-cells = <1>;
-               ranges;
-
-               clocks = <&gcc GCC_USB3_SEC_PHY_AUX_CLK>,
-                        <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
-                        <&gcc GCC_USB3_SEC_CLKREF_CLK>,
-                        <&gcc GCC_USB3_SEC_PHY_COM_AUX_CLK>;
-               clock-names = "aux", "cfg_ahb", "ref", "com_aux";
-
-               resets = <&gcc GCC_USB3PHY_PHY_SEC_BCR>,
-                        <&gcc GCC_USB3_PHY_SEC_BCR>;
-               reset-names = "phy", "common";
-
-               lane@88eb200 {
-                       reg = <0x88eb200 0x128>,
-                             <0x88eb400 0x1fc>,
-                             <0x88eb800 0x218>,
-                             <0x88eb600 0x70>;
-                       #clock-cells = <0>;
-                       #phy-cells = <0>;
-                       clocks = <&gcc GCC_USB3_SEC_PHY_PIPE_CLK>;
-                       clock-names = "pipe0";
-                       clock-output-names = "usb3_uni_phy_pipe_clk_src";
-               };
-       };
-
-       phy@1d87000 {
-               compatible = "qcom,sdm845-qmp-ufs-phy";
-               reg = <0x1d87000 0x18c>;
-               #address-cells = <1>;
-               #size-cells = <1>;
-               ranges;
-               clock-names = "ref",
-                             "ref_aux";
-               clocks = <&gcc GCC_UFS_MEM_CLKREF_CLK>,
-                        <&gcc GCC_UFS_PHY_PHY_AUX_CLK>;
-
-               lanes@1d87400 {
-                       reg = <0x1d87400 0x108>,
-                             <0x1d87600 0x1e0>,
-                             <0x1d87c00 0x1dc>,
-                             <0x1d87800 0x108>,
-                             <0x1d87a00 0x1e0>;
-                       #phy-cells = <0>;
-               };
-       };
diff --git a/Documentation/devicetree/bindings/phy/qcom-usb-ipq4019-phy.yaml b/Documentation/devicetree/bindings/phy/qcom-usb-ipq4019-phy.yaml
new file mode 100644 (file)
index 0000000..1118fe6
--- /dev/null
@@ -0,0 +1,50 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/qcom-usb-ipq4019-phy.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcom IPQ40xx Dakota HS/SS USB PHY
+
+maintainers:
+  - Robert Marko <robert.marko@sartura.hr>
+
+properties:
+  compatible:
+    enum:
+      - qcom,usb-ss-ipq4019-phy
+      - qcom,usb-hs-ipq4019-phy
+
+  reg:
+    maxItems: 1
+
+  resets:
+    maxItems: 2
+
+  reset-names:
+    items:
+      - const: por_rst
+      - const: srif_rst
+
+  "#phy-cells":
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - resets
+  - reset-names
+  - "#phy-cells"
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-ipq4019.h>
+
+    hsphy@a8000 {
+      #phy-cells = <0>;
+      compatible = "qcom,usb-hs-ipq4019-phy";
+      reg = <0xa8000 0x40>;
+      resets = <&gcc USB2_HSPHY_POR_ARES>,
+               <&gcc USB2_HSPHY_S_ARES>;
+      reset-names = "por_rst", "srif_rst";
+    };
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
deleted file mode 100644 (file)
index 7734b21..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-* Renesas R-Car generation 3 USB 2.0 PHY
-
-This file provides information on what the device node for the R-Car generation
-3, RZ/G1C, RZ/G2 and RZ/A2 USB 2.0 PHY contain.
-
-Required properties:
-- compatible: "renesas,usb2-phy-r7s9210" if the device is a part of an R7S9210
-             SoC.
-             "renesas,usb2-phy-r8a77470" if the device is a part of an R8A77470
-             SoC.
-             "renesas,usb2-phy-r8a774a1" if the device is a part of an R8A774A1
-             SoC.
-             "renesas,usb2-phy-r8a774b1" if the device is a part of an R8A774B1
-             SoC.
-             "renesas,usb2-phy-r8a774c0" if the device is a part of an R8A774C0
-             SoC.
-             "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
-             SoC.
-             "renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
-             SoC.
-             "renesas,usb2-phy-r8a77965" if the device is a part of an
-             R8A77965 SoC.
-             "renesas,usb2-phy-r8a77990" if the device is a part of an
-             R8A77990 SoC.
-             "renesas,usb2-phy-r8a77995" if the device is a part of an
-             R8A77995 SoC.
-             "renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3, RZ/G2 or
-             RZ/A2 compatible device.
-
-             When compatible with the generic version, nodes must list the
-             SoC-specific version corresponding to the platform first
-             followed by the generic version.
-
-- reg: offset and length of the partial USB 2.0 Host register block.
-- clocks: clock phandle and specifier pair(s).
-- #phy-cells: see phy-bindings.txt in the same directory, must be <1> (and
-             using <0> is deprecated).
-
-The phandle's argument in the PHY specifier is the INT_STATUS bit of controller:
-- 1 = USBH_INTA (OHCI)
-- 2 = USBH_INTB (EHCI)
-- 3 = UCOM_INT (OTG and BC)
-
-Optional properties:
-To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
-combined, the device tree node should set interrupt properties to use the
-channel as USB OTG:
-- interrupts: interrupt specifier for the PHY.
-- vbus-supply: Phandle to a regulator that provides power to the VBUS. This
-              regulator will be managed during the PHY power on/off sequence.
-- renesas,no-otg-pins: boolean, specify when a board does not provide proper
-                      otg pins.
-- dr_mode: string, indicates the working mode for the PHY. Can be "host",
-           "peripheral", or "otg". Should be set if otg controller is not used.
-
-
-Example (R-Car H3):
-
-       usb-phy@ee080200 {
-               compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
-               reg = <0 0xee080200 0 0x700>;
-               interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
-               clocks = <&cpg CPG_MOD 703>;
-       };
-
-       usb-phy@ee0a0200 {
-               compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
-               reg = <0 0xee0a0200 0 0x700>;
-               clocks = <&cpg CPG_MOD 702>;
-       };
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb3.txt
deleted file mode 100644 (file)
index 0fe433b..0000000
+++ /dev/null
@@ -1,52 +0,0 @@
-* Renesas R-Car generation 3 USB 3.0 PHY
-
-This file provides information on what the device node for the R-Car generation
-3 and RZ/G2 USB 3.0 PHY contain.
-If you want to enable spread spectrum clock (ssc), you should use USB_EXTAL
-instead of USB3_CLK. However, if you don't want to these features, you don't
-need this driver.
-
-Required properties:
-- compatible: "renesas,r8a774a1-usb3-phy" if the device is a part of an R8A774A1
-             SoC.
-             "renesas,r8a774b1-usb3-phy" if the device is a part of an R8A774B1
-             SoC.
-             "renesas,r8a7795-usb3-phy" if the device is a part of an R8A7795
-             SoC.
-             "renesas,r8a7796-usb3-phy" if the device is a part of an R8A7796
-             SoC.
-             "renesas,r8a77965-usb3-phy" if the device is a part of an
-             R8A77965 SoC.
-             "renesas,rcar-gen3-usb3-phy" for a generic R-Car Gen3 or RZ/G2
-             compatible device.
-
-             When compatible with the generic version, nodes must list the
-             SoC-specific version corresponding to the platform first
-             followed by the generic version.
-
-- reg: offset and length of the USB 3.0 PHY register block.
-- clocks: A list of phandles and clock-specifier pairs.
-- clock-names: Name of the clocks.
-  - The funcional clock must be "usb3-if".
-  - The usb3's external clock must be "usb3s_clk".
-  - The usb2's external clock must be "usb_extal". If you want to use the ssc,
-    the clock-frequency must not be 0.
-- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
-
-Optional properties:
-- renesas,ssc-range: Enable/disable spread spectrum clock (ssc) by using
-                    the following values as u32:
-                       - 0 (or the property doesn't exist): disable the ssc
-                       - 4980: enable the ssc as -4980 ppm
-                       - 4492: enable the ssc as -4492 ppm
-                       - 4003: enable the ssc as -4003 ppm
-
-Example (R-Car H3):
-
-       usb-phy@e65ee000 {
-               compatible = "renesas,r8a7795-usb3-phy",
-                            "renesas,rcar-gen3-usb3-phy";
-               reg = <0 0xe65ee000 0 0x90>;
-               clocks = <&cpg CPG_MOD 328>, <&usb3s0_clk>, <&usb_extal>;
-               clock-names = "usb3-if", "usb3s_clk", "usb_extal";
-       };
diff --git a/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb2-phy.yaml
new file mode 100644 (file)
index 0000000..440f09f
--- /dev/null
@@ -0,0 +1,117 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/renesas,usb2-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas R-Car generation 3 USB 2.0 PHY
+
+maintainers:
+  - Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+          - const: renesas,usb2-phy-r8a77470 # RZ/G1C
+
+      - items:
+          - enum:
+              - renesas,usb2-phy-r7s9210  # RZ/A2
+              - renesas,usb2-phy-r8a774a1 # RZ/G2M
+              - renesas,usb2-phy-r8a774b1 # RZ/G2N
+              - renesas,usb2-phy-r8a774c0 # RZ/G2E
+              - renesas,usb2-phy-r8a7795  # R-Car H3
+              - renesas,usb2-phy-r8a7796  # R-Car M3-W
+              - renesas,usb2-phy-r8a77961 # R-Car M3-W+
+              - renesas,usb2-phy-r8a77965 # R-Car M3-N
+              - renesas,usb2-phy-r8a77990 # R-Car E3
+              - renesas,usb2-phy-r8a77995 # R-Car D3
+          - const: renesas,rcar-gen3-usb2-phy
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 1
+    maxItems: 2
+
+  clock-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      - const: fck
+      - const: usb_x1
+
+  '#phy-cells':
+    enum: [0, 1]  # and 0 is deprecated.
+    description: |
+      The phandle's argument in the PHY specifier is the INT_STATUS bit of
+      controller.
+      - 1 = USBH_INTA (OHCI)
+      - 2 = USBH_INTB (EHCI)
+      - 3 = UCOM_INT (OTG and BC)
+
+  interrupts:
+    maxItems: 1
+
+  power-domains:
+    maxItems: 1
+
+  resets:
+    minItems: 1
+    maxItems: 2
+    items:
+      - description: reset of USB 2.0 host side
+      - description: reset of USB 2.0 peripheral side
+
+  vbus-supply:
+    description: |
+      Phandle to a regulator that provides power to the VBUS. This regulator
+      will be managed during the PHY power on/off sequence.
+
+  renesas,no-otg-pins:
+    $ref: /schemas/types.yaml#/definitions/flag
+    description: |
+      specify when a board does not provide proper otg pins.
+
+  dr_mode: true
+
+if:
+  properties:
+    compatible:
+      items:
+        enum:
+          - renesas,usb2-phy-r7s9210
+then:
+  required:
+    - clock-names
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - '#phy-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/r8a7795-cpg-mssr.h>
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+    #include <dt-bindings/power/r8a7795-sysc.h>
+
+    usb-phy@ee080200 {
+        compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
+        reg = <0xee080200 0x700>;
+        interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
+        clocks = <&cpg CPG_MOD 703>;
+        #phy-cells = <1>;
+    };
+
+    usb-phy@ee0a0200 {
+        compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
+        reg = <0xee0a0200 0x700>;
+        clocks = <&cpg CPG_MOD 702>;
+        #phy-cells = <1>;
+    };
diff --git a/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml b/Documentation/devicetree/bindings/phy/renesas,usb3-phy.yaml
new file mode 100644 (file)
index 0000000..f459eaf
--- /dev/null
@@ -0,0 +1,79 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/renesas,usb3-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas R-Car generation 3 USB 3.0 PHY
+
+maintainers:
+  - Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
+
+properties:
+  compatible:
+    items:
+      - enum:
+          - renesas,r8a774a1-usb3-phy # RZ/G2M
+          - renesas,r8a774b1-usb3-phy # RZ/G2N
+          - renesas,r8a7795-usb3-phy  # R-Car H3
+          - renesas,r8a7796-usb3-phy  # R-Car M3-W
+          - renesas,r8a77961-usb3-phy # R-Car M3-W+
+          - renesas,r8a77965-usb3-phy # R-Car M3-N
+      - const: renesas,rcar-gen3-usb3-phy
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    minItems: 2
+    maxItems: 3
+
+  clock-names:
+    # If you want to use the ssc, the clock-frequency of usb_extal
+    # must not be 0.
+    minItems: 2
+    maxItems: 3
+    items:
+      - const: usb3-if # The funcional clock
+      - const: usb3s_clk # The usb3's external clock
+      - const: usb_extal # The usb2's external clock
+
+  '#phy-cells':
+    # see phy-bindings.txt in the same directory
+    const: 0
+
+  power-domains:
+    maxItems: 1
+
+  resets:
+    maxItems: 1
+
+  renesas,ssc-range:
+    description: |
+      Enable/disable spread spectrum clock (ssc). 0 or the property doesn't
+      exist means disabling the ssc. The actual value will be -<value> ppm.
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - enum: [ 0, 4003, 4492, 4980 ]
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - '#phy-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/r8a7795-cpg-mssr.h>
+    #include <dt-bindings/power/r8a7795-sysc.h>
+
+    usb-phy@e65ee000 {
+        compatible = "renesas,r8a7795-usb3-phy", "renesas,rcar-gen3-usb3-phy";
+        reg = <0xe65ee000 0x90>;
+        clocks = <&cpg CPG_MOD 328>, <&usb3s0_clk>, <&usb_extal>;
+        clock-names = "usb3-if", "usb3s_clk", "usb_extal";
+        #phy-cells = <0>;
+    };
diff --git a/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt b/Documentation/devicetree/bindings/usb/amlogic,dwc3.txt
deleted file mode 100644 (file)
index 9a8b631..0000000
+++ /dev/null
@@ -1,42 +0,0 @@
-Amlogic Meson GX DWC3 USB SoC controller
-
-Required properties:
-- compatible:  depending on the SoC this should contain one of:
-                       * amlogic,meson-axg-dwc3
-                       * amlogic,meson-gxl-dwc3
-- clocks:      a handle for the "USB general" clock
-- clock-names: must be "usb_general"
-- resets:      a handle for the shared "USB OTG" reset line
-- reset-names: must be "usb_otg"
-
-Required child node:
-A child node must exist to represent the core DWC3 IP block. The name of
-the node is not important. The content of the node is defined in dwc3.txt.
-
-PHY documentation is provided in the following places:
-- Documentation/devicetree/bindings/phy/meson-gxl-usb2-phy.txt
-- Documentation/devicetree/bindings/phy/meson-gxl-usb3-phy.txt
-
-Example device nodes:
-               usb0: usb@ff500000 {
-                       compatible = "amlogic,meson-axg-dwc3";
-                       #address-cells = <2>;
-                       #size-cells = <2>;
-                       ranges;
-
-                       clocks = <&clkc CLKID_USB>;
-                       clock-names = "usb_general";
-                       resets = <&reset RESET_USB_OTG>;
-                       reset-names = "usb_otg";
-
-                       dwc3: dwc3@ff500000 {
-                               compatible = "snps,dwc3";
-                               reg = <0x0 0xff500000 0x0 0x100000>;
-                               interrupts = <GIC_SPI 30 IRQ_TYPE_LEVEL_HIGH>;
-                               dr_mode = "host";
-                               maximum-speed = "high-speed";
-                               snps,dis_u2_susphy_quirk;
-                               phys = <&usb3_phy>, <&usb2_phy0>;
-                               phy-names = "usb2-phy", "usb3-phy";
-                       };
-               };
index f6d48f4..5b04a7d 100644 (file)
@@ -25,9 +25,13 @@ description: |
   The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in
   host-only mode.
 
+  The Amlogic GXL & GXM SoCs doesn't embed an USB3 PHY.
+
 properties:
   compatible:
     enum:
+      - amlogic,meson-gxl-usb-ctrl
+      - amlogic,meson-gxm-usb-ctrl
       - amlogic,meson-g12a-usb-ctrl
       - amlogic,meson-a1-usb-ctrl
 
@@ -41,6 +45,11 @@ properties:
 
   clocks:
     minItems: 1
+    maxItems: 3
+
+  clock-names:
+    minItems: 1
+    maxItems: 3
 
   resets:
     minItems: 1
@@ -52,10 +61,8 @@ properties:
     maxItems: 1
 
   phy-names:
-    items:
-      - const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
-      - const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
-      - const: usb3-phy0 # USB3 PHY if USB3_0 is used
+    minItems: 1
+    maxItems: 3
 
   phys:
     minItems: 1
@@ -93,10 +100,68 @@ allOf:
       properties:
         compatible:
           enum:
+            - amlogic,meson-g12a-usb-ctrl
+
+    then:
+      properties:
+        phy-names:
+          items:
+            - const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
+            - const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
+            - const: usb3-phy0 # USB3 PHY if USB3_0 is used
+  - if:
+      properties:
+        compatible:
+          enum:
+            - amlogic,meson-gxl-usb-ctrl
+
+    then:
+      properties:
+        clocks:
+          minItems: 2
+        clock-names:
+          items:
+            - const: usb_ctrl
+            - const: ddr
+        phy-names:
+          items:
+            - const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
+            - const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
+      required:
+        - clock-names
+  - if:
+      properties:
+        compatible:
+          enum:
+            - amlogic,meson-gxm-usb-ctrl
+
+    then:
+      properties:
+        clocks:
+          minItems: 2
+        clock-names:
+          items:
+            - const: usb_ctrl
+            - const: ddr
+        phy-names:
+          items:
+            - const: usb2-phy0 # USB2 PHY0 if USBHOST_A port is used
+            - const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
+            - const: usb2-phy2 # USB2 PHY2 if USBOTG_C port is used
+
+      required:
+        - clock-names
+  - if:
+      properties:
+        compatible:
+          enum:
             - amlogic,meson-a1-usb-ctrl
 
     then:
       properties:
+        phy-names:
+          items:
+            - const: usb2-phy1 # USB2 PHY1 if USBOTG_B port is used
         clocks:
           minItems: 3
         clock-names:
index 297fb77..ccc67d0 100644 (file)
@@ -50,6 +50,59 @@ properties:
     minimum: 1
     maximum: 21
 
+  vhub-vendor-id:
+    description: vhub Vendor ID
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - maximum: 65535
+
+  vhub-product-id:
+    description: vhub Product ID
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - maximum: 65535
+
+  vhub-device-revision:
+    description: vhub Device Revision in binary-coded decimal
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - maximum: 65535
+
+  vhub-strings:
+    type: object
+
+    properties:
+      '#address-cells':
+        const: 1
+
+      '#size-cells':
+        const: 0
+
+    patternProperties:
+      '^string@[0-9a-f]+$':
+        type: object
+        description: string descriptors of the specific language
+
+        properties:
+          reg:
+            maxItems: 1
+            description: 16-bit Language Identifier defined by USB-IF
+
+          manufacturer:
+            description: vhub manufacturer
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/string
+
+          product:
+            description: vhub product name
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/string
+
+          serial-number:
+            description: vhub device serial number
+            allOf:
+              - $ref: /schemas/types.yaml#/definitions/string
+
 required:
   - compatible
   - reg
@@ -72,4 +125,19 @@ examples:
             aspeed,vhub-generic-endpoints = <15>;
             pinctrl-names = "default";
             pinctrl-0 = <&pinctrl_usb2ad_default>;
+
+            vhub-vendor-id = <0x1d6b>;
+            vhub-product-id = <0x0107>;
+            vhub-device-revision = <0x0100>;
+            vhub-strings {
+                #address-cells = <1>;
+                #size-cells = <0>;
+
+                string@0409 {
+                        reg = <0x0409>;
+                        manufacturer = "ASPEED";
+                        product = "USB Virtual Hub";
+                        serial-number = "0000";
+                };
+            };
     };
index 44e8015..423b99a 100644 (file)
@@ -88,13 +88,15 @@ Required properties:
  - clock-names: Should contain two strings
                "pclk" for the peripheral clock
                "hclk" for the host clock
+
+Deprecated property:
  - ep childnode: To specify the number of endpoints and their properties.
 
 Optional properties:
  - atmel,vbus-gpio: If present, specifies a gpio that allows to detect whether
    vbus is present (USB is connected).
 
-Required child node properties:
+Deprecated child node properties:
  - name: Name of the endpoint.
  - reg: Num of the endpoint.
  - atmel,fifo-size: Size of the fifo.
@@ -112,56 +114,4 @@ usb2: gadget@fff78000 {
        clocks = <&utmi>, <&udphs_clk>;
        clock-names = "hclk", "pclk";
        atmel,vbus-gpio = <&pioB 19 0>;
-
-       ep@0 {
-               reg = <0>;
-               atmel,fifo-size = <64>;
-               atmel,nb-banks = <1>;
-       };
-
-       ep@1 {
-               reg = <1>;
-               atmel,fifo-size = <1024>;
-               atmel,nb-banks = <2>;
-               atmel,can-dma;
-               atmel,can-isoc;
-       };
-
-       ep@2 {
-               reg = <2>;
-               atmel,fifo-size = <1024>;
-               atmel,nb-banks = <2>;
-               atmel,can-dma;
-               atmel,can-isoc;
-       };
-
-       ep@3 {
-               reg = <3>;
-               atmel,fifo-size = <1024>;
-               atmel,nb-banks = <3>;
-               atmel,can-dma;
-       };
-
-       ep@4 {
-               reg = <4>;
-               atmel,fifo-size = <1024>;
-               atmel,nb-banks = <3>;
-               atmel,can-dma;
-       };
-
-       ep@5 {
-               reg = <5>;
-               atmel,fifo-size = <1024>;
-               atmel,nb-banks = <3>;
-               atmel,can-dma;
-               atmel,can-isoc;
-       };
-
-       ep@6 {
-               reg = <6>;
-               atmel,fifo-size = <1024>;
-               atmel,nb-banks = <3>;
-               atmel,can-dma;
-               atmel,can-isoc;
-       };
 };
diff --git a/Documentation/devicetree/bindings/usb/brcm,bcm7445-ehci.yaml b/Documentation/devicetree/bindings/usb/brcm,bcm7445-ehci.yaml
new file mode 100644 (file)
index 0000000..2a9acf2
--- /dev/null
@@ -0,0 +1,59 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/brcm,bcm7445-ehci.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Broadcom STB USB EHCI Controller Device Tree Bindings
+
+allOf:
+  - $ref: "usb-hcd.yaml"
+
+maintainers:
+  - Al Cooper <alcooperx@gmail.com>
+
+properties:
+  compatible:
+    const: brcm,bcm7445-ehci
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+    description: Clock specifier for the EHCI clock
+
+  clock-names:
+    const: sw_usb
+
+  phys:
+    maxItems: 1
+
+  phy-names:
+    const: usbphy
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - phys
+  - clocks
+
+additionalProperties: false
+
+examples:
+  - |
+    usb@f0b00300 {
+        compatible = "brcm,bcm7445-ehci";
+        reg = <0xf0b00300 0xa8>;
+        interrupts = <0x0 0x5a 0x0>;
+        phys = <&usbphy_0 0x0>;
+        phy-names = "usbphy";
+        clocks = <&usb20>;
+        clock-names = "sw_usb";
+    };
+
+...
index 9946ff9..d03edf9 100644 (file)
@@ -15,8 +15,6 @@ Required properties:
 Exception for clocks:
   clocks are optional if the parent node (i.e. glue-layer) is compatible to
   one of the following:
-    "amlogic,meson-axg-dwc3"
-    "amlogic,meson-gxl-dwc3"
     "cavium,octeon-7130-usb-uctl"
     "qcom,dwc3"
     "samsung,exynos5250-dwusb3"
diff --git a/Documentation/devicetree/bindings/usb/keystone-usb.txt b/Documentation/devicetree/bindings/usb/keystone-usb.txt
deleted file mode 100644 (file)
index 77df82e..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-TI Keystone Soc USB Controller
-
-DWC3 GLUE
-
-Required properties:
- - compatible: should be
-               "ti,keystone-dwc3" for Keystone 2 SoCs
-               "ti,am654-dwc3" for AM654 SoC
- - #address-cells, #size-cells : should be '1' if the device has sub-nodes
-   with 'reg' property.
- - reg : Address and length of the register set for the USB subsystem on
-   the SOC.
- - interrupts : The irq number of this device that is used to interrupt the
-   MPU.
- - ranges: allows valid 1:1 translation between child's address space and
-   parent's address space.
-
-SoC-specific Required Properties:
-The following are mandatory properties for Keystone 2 66AK2HK, 66AK2L and 66AK2E
-SoCs only:
-
-- clocks:              Clock ID for USB functional clock.
-- clock-names:         Must be "usb".
-
-
-The following are mandatory properties for 66AK2G and AM654:
-
-- power-domains:       Should contain a phandle to a PM domain provider node
-                       and an args specifier containing the USB device id
-                       value. This property is as per the binding,
-                       Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
-
-Sub-nodes:
-The dwc3 core should be added as subnode to Keystone DWC3 glue.
-- dwc3 :
-   The binding details of dwc3 can be found in:
-   Documentation/devicetree/bindings/usb/dwc3.txt
-
-Example:
-       usb: usb@2680000 {
-               compatible = "ti,keystone-dwc3";
-               #address-cells = <1>;
-               #size-cells = <1>;
-               reg = <0x2680000 0x10000>;
-               clocks = <&clkusb>;
-               clock-names = "usb";
-               interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
-               ranges;
-
-               dwc3@2690000 {
-                       compatible = "synopsys,dwc3";
-                       reg = <0x2690000 0x70000>;
-                       interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
-                       usb-phy = <&usb_phy>, <&usb_phy>;
-               };
-       };
index d814d2d..c4ddc0a 100644 (file)
@@ -21,6 +21,7 @@ properties:
       - enum:
           - nvidia,tegra210-xudc # For Tegra210
           - nvidia,tegra186-xudc # For Tegra186
+          - nvidia,tegra194-xudc # For Tegra194
 
   reg:
     minItems: 2
@@ -144,6 +145,7 @@ allOf:
           contains:
             enum:
               - nvidia,tegra186-xudc
+              - nvidia,tegra194-xudc
     then:
       properties:
         reg:
diff --git a/Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml b/Documentation/devicetree/bindings/usb/ti,keystone-dwc3.yaml
new file mode 100644 (file)
index 0000000..f127535
--- /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/ti,keystone-dwc3.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: TI Keystone Soc USB Controller
+
+maintainers:
+  - Roger Quadros <rogerq@ti.com>
+
+properties:
+  compatible:
+    oneOf:
+      - const: "ti,keystone-dwc3"
+      - const: "ti,am654-dwc3"
+
+  reg:
+    maxItems: 1
+    description: Address and length of the register set for the USB subsystem on
+      the SOC.
+
+  interrupts:
+    maxItems: 1
+    description: The irq number of this device that is used to interrupt the MPU.
+
+
+  clocks:
+    description: Clock ID for USB functional clock.
+
+  power-domains:
+    description: Should contain a phandle to a PM domain provider node
+      and an args specifier containing the USB device id
+      value. This property is as per the binding,
+      Documentation/devicetree/bindings/soc/ti/sci-pm-domain.txt
+
+  phys:
+    description:
+      PHY specifier for the USB3.0 PHY. Some SoCs need the USB3.0 PHY
+      to be turned on before the controller.
+      Documentation/devicetree/bindings/phy/phy-bindings.txt
+
+  phy-names:
+    items:
+      - const: "usb3-phy"
+
+  dwc3:
+    description: This is the node representing the DWC3 controller instance
+      Documentation/devicetree/bindings/usb/dwc3.txt
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
+    usb: usb@2680000 {
+      compatible = "ti,keystone-dwc3";
+      #address-cells = <1>;
+      #size-cells = <1>;
+      reg = <0x2680000 0x10000>;
+      clocks = <&clkusb>;
+      clock-names = "usb";
+      interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
+      ranges;
+
+      dwc3@2690000 {
+        compatible = "synopsys,dwc3";
+        reg = <0x2690000 0x70000>;
+        interrupts = <GIC_SPI 393 IRQ_TYPE_EDGE_RISING>;
+        usb-phy = <&usb_phy>, <&usb_phy>;
+      };
+    };
diff --git a/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml b/Documentation/devicetree/bindings/usb/ti,tps6598x.yaml
new file mode 100644 (file)
index 0000000..8eaf4b6
--- /dev/null
@@ -0,0 +1,64 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/usb/ti,tps6598x.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Texas Instruments 6598x Type-C Port Switch and Power Delivery controller DT bindings
+
+maintainers:
+  - Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+
+description: |
+  Texas Instruments 6598x Type-C Port Switch and Power Delivery controller
+
+properties:
+  compatible:
+    enum:
+      - ti,tps6598x
+  reg:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-names:
+    items:
+      - const: irq
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-names
+
+examples:
+  - |
+    #include <dt-bindings/interrupt-controller/irq.h>
+    i2c0 {
+        #address-cells = <1>;
+        #size-cells = <0>;
+
+        tps6598x: tps6598x@38 {
+            compatible = "ti,tps6598x";
+            reg = <0x38>;
+
+            interrupt-parent = <&msmgpio>;
+            interrupts = <107 IRQ_TYPE_LEVEL_LOW>;
+            interrupt-names = "irq";
+
+            pinctrl-names = "default";
+            pinctrl-0 = <&typec_pins>;
+
+            typec_con: connector {
+                compatible = "usb-c-connector";
+                label = "USB-C";
+                port {
+                    typec_ep: endpoint {
+                        remote-endpoint = <&otg_ep>;
+                    };
+                };
+            };
+        };
+    };
+...
index 4473c98..b120dd6 100644 (file)
@@ -25,6 +25,7 @@ Required properties:
       device
     - "renesas,rcar-gen3-xhci" for a generic R-Car Gen3 or RZ/G2 compatible
       device
+    - "brcm,bcm7445-xhci" for Broadcom STB SoCs with XHCI
     - "xhci-platform" (deprecated)
 
     When compatible with the generic version, nodes must list the
diff --git a/Documentation/firmware-guide/acpi/intel-pmc-mux.rst b/Documentation/firmware-guide/acpi/intel-pmc-mux.rst
new file mode 100644 (file)
index 0000000..99b8671
--- /dev/null
@@ -0,0 +1,153 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=====================
+Intel North Mux-Agent
+=====================
+
+Introduction
+============
+
+North Mux-Agent is a function of the Intel PMC firmware that is supported on
+most Intel based platforms that have the PMC microcontroller. It's used for
+configuring the various USB Multiplexer/DeMultiplexers on the system. The
+platforms that allow the mux-agent to be configured from the operating system
+have an ACPI device object (node) with HID "INTC105C" that represents it.
+
+The North Mux-Agent (aka. Intel PMC Mux Control, or just mux-agent) driver
+communicates with the PMC microcontroller by using the PMC IPC method
+(drivers/platform/x86/intel_scu_ipc.c). The driver registers with the USB Type-C
+Mux Class which allows the USB Type-C Controller and Interface drivers to
+configure the cable plug orientation and mode (with Alternate Modes). The driver
+also registers with the USB Role Class in order to support both USB Host and
+Device modes. The driver is located here: drivers/usb/typec/mux/intel_pmc_mux.c.
+
+Port nodes
+==========
+
+General
+-------
+
+For every USB Type-C connector under the mux-agent control on the system, there
+is a separate child node under the PMC mux-agent device node. Those nodes do not
+represent the actual connectors, but instead the "channels" in the mux-agent
+that are associated with the connectors::
+
+       Scope (_SB.PCI0.PMC.MUX)
+       {
+           Device (CH0)
+           {
+               Name (_ADR, 0)
+           }
+
+           Device (CH1)
+           {
+               Name (_ADR, 1)
+           }
+       }
+
+_PLD (Physical Location of Device)
+----------------------------------
+
+The optional _PLD object can be used with the port (the channel) nodes. If _PLD
+is supplied, it should match the connector node _PLD::
+
+       Scope (_SB.PCI0.PMC.MUX)
+       {
+           Device (CH0)
+           {
+               Name (_ADR, 0)
+               Method (_PLD, 0, NotSerialized)
+                {
+                   /* Consider this as pseudocode. */
+                   Return (\_SB.USBC.CON0._PLD())
+               }
+           }
+       }
+
+Mux-agent specific _DSD Device Properties
+-----------------------------------------
+
+Port Numbers
+~~~~~~~~~~~~
+
+In order to configure the muxes behind a USB Type-C connector, the PMC firmware
+needs to know the USB2 port and the USB3 port that is associated with the
+connector. The driver extracts the correct port numbers by reading specific _DSD
+device properties named "usb2-port-number" and "usb3-port-number". These
+properties have integer value that means the port index. The port index number
+is 1's based, and value 0 is illegal. The driver uses the numbers extracted from
+these device properties as-is when sending the mux-agent specific messages to
+the PMC::
+
+       Name (_DSD, Package () {
+           ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+           Package() {
+               Package () {"usb2-port-number", 6},
+               Package () {"usb3-port-number", 3},
+           },
+       })
+
+Orientation
+~~~~~~~~~~~
+
+Depending on the platform, the data and SBU lines coming from the connector may
+be "fixed" from the mux-agent's point of view, which means the mux-agent driver
+should not configure them according to the cable plug orientation. This can
+happen for example if a retimer on the platform handles the cable plug
+orientation. The driver uses a specific device properties "sbu-orientation"
+(SBU) and "hsl-orientation" (data) to know if those lines are "fixed", and to
+which orientation. The value that these properties have is a string value, and
+it can be one that is defined for the USB Type-C connector orientation: "normal"
+or "reversed"::
+
+       Name (_DSD, Package () {
+           ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+           Package() {
+               Package () {"sbu-orientation", "normal"},
+               Package () {"hsl-orientation", "normal"},
+           },
+       })
+
+Example ASL
+===========
+
+The following ASL is an example that shows the mux-agent node, and two
+connectors under its control::
+
+       Scope (_SB.PCI0.PMC)
+       {
+           Device (MUX)
+           {
+               Name (_HID, "INTC105C")
+
+               Device (CH0)
+               {
+                   Name (_ADR, 0)
+
+                   Name (_DSD, Package () {
+                       ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+                       Package() {
+                           Package () {"usb2-port-number", 6},
+                           Package () {"usb3-port-number", 3},
+                           Package () {"sbu-orientation", "normal"},
+                           Package () {"hsl-orientation", "normal"},
+                       },
+                   })
+               }
+
+               Device (CH1)
+               {
+                   Name (_ADR, 1)
+
+                   Name (_DSD, Package () {
+                       ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
+                       Package() {
+                           Package () {"usb2-port-number", 5},
+                           Package () {"usb3-port-number", 2},
+                           Package () {"sbu-orientation", "normal"},
+                           Package () {"hsl-orientation", "normal"},
+                       },
+                   })
+               }
+           }
+       }
index c62837c..71b61ac 100644 (file)
@@ -3500,6 +3500,14 @@ S:       Supported
 F:     Documentation/devicetree/bindings/i2c/brcm,brcmstb-i2c.yaml
 F:     drivers/i2c/busses/i2c-brcmstb.c
 
+BROADCOM BRCMSTB USB EHCI DRIVER
+M:     Al Cooper <alcooperx@gmail.com>
+L:     linux-usb@vger.kernel.org
+L:     bcm-kernel-feedback-list@broadcom.com
+S:     Maintained
+F:     Documentation/devicetree/bindings/usb/brcm,bcm7445-ehci.yaml
+F:     drivers/usb/host/ehci-brcm.*
+
 BROADCOM BRCMSTB USB2 and USB3 PHY DRIVER
 M:     Al Cooper <alcooperx@gmail.com>
 L:     linux-kernel@vger.kernel.org
@@ -17759,6 +17767,13 @@ F:     Documentation/driver-api/usb/typec.rst
 F:     drivers/usb/typec/
 F:     include/linux/usb/typec.h
 
+USB TYPEC INTEL PMC MUX DRIVER
+M:     Heikki Krogerus <heikki.krogerus@linux.intel.com>
+L:     linux-usb@vger.kernel.org
+S:     Maintained
+F:     Documentation/firmware-guide/acpi/intel-pmc-mux.rst
+F:     drivers/usb/typec/mux/intel_pmc_mux.c
+
 USB TYPEC PI3USB30532 MUX DRIVER
 M:     Hans de Goede <hdegoede@redhat.com>
 L:     linux-usb@vger.kernel.org
index 3034c23..1fbee2a 100644 (file)
                        };
 
                        usb2: gadget@fff78000 {
-                               #address-cells = <1>;
-                               #size-cells = <0>;
                                compatible = "atmel,at91sam9g45-udc";
                                reg = <0x00600000 0x80000
                                       0xfff78000 0x400>;
                                clocks = <&pmc PMC_TYPE_PERIPHERAL 27>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
                                clock-names = "pclk", "hclk";
                                status = "disabled";
-
-                               ep@0 {
-                                       reg = <0>;
-                                       atmel,fifo-size = <64>;
-                                       atmel,nb-banks = <1>;
-                               };
-
-                               ep@1 {
-                                       reg = <1>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <2>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@2 {
-                                       reg = <2>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <2>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@3 {
-                                       reg = <3>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                               };
-
-                               ep@4 {
-                                       reg = <4>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                               };
-
-                               ep@5 {
-                                       reg = <5>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@6 {
-                                       reg = <6>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
                        };
 
                        clk32k: sckc@fffffd50 {
index ea024e4..4d70194 100644 (file)
                        };
 
                        usb0: gadget@fffd4000 {
-                               #address-cells = <1>;
-                               #size-cells = <0>;
                                compatible = "atmel,at91sam9rl-udc";
                                reg = <0x00600000 0x100000>,
                                      <0xfffd4000 0x4000>;
                                clocks = <&pmc PMC_TYPE_PERIPHERAL 22>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
                                clock-names = "pclk", "hclk";
                                status = "disabled";
-
-                               ep@0 {
-                                       reg = <0>;
-                                       atmel,fifo-size = <64>;
-                                       atmel,nb-banks = <1>;
-                               };
-
-                               ep@1 {
-                                       reg = <1>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <2>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@2 {
-                                       reg = <2>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <2>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@3 {
-                                       reg = <3>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                               };
-
-                               ep@4 {
-                                       reg = <4>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                               };
-
-                               ep@5 {
-                                       reg = <5>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@6 {
-                                       reg = <6>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
                        };
 
                        dma0: dma-controller@ffffe600 {
index 7c2eb93..948fe99 100644 (file)
                        };
 
                        usb2: gadget@f803c000 {
-                               #address-cells = <1>;
-                               #size-cells = <0>;
                                compatible = "atmel,at91sam9g45-udc";
                                reg = <0x00500000 0x80000
                                       0xf803c000 0x400>;
                                clocks = <&pmc PMC_TYPE_CORE PMC_UTMI>, <&pmc PMC_TYPE_PERIPHERAL 23>;
                                clock-names = "hclk", "pclk";
                                status = "disabled";
-
-                               ep@0 {
-                                       reg = <0>;
-                                       atmel,fifo-size = <64>;
-                                       atmel,nb-banks = <1>;
-                               };
-
-                               ep@1 {
-                                       reg = <1>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <2>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@2 {
-                                       reg = <2>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <2>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@3 {
-                                       reg = <3>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                               };
-
-                               ep@4 {
-                                       reg = <4>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                               };
-
-                               ep@5 {
-                                       reg = <5>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
-
-                               ep@6 {
-                                       reg = <6>;
-                                       atmel,fifo-size = <1024>;
-                                       atmel,nb-banks = <3>;
-                                       atmel,can-dma;
-                                       atmel,can-isoc;
-                               };
                        };
 
                        watchdog: watchdog@fffffe40 {
index c0a3ca8..31d8766 100644 (file)
                };
 
                usb0: gadget@300000 {
-                       #address-cells = <1>;
-                       #size-cells = <0>;
                        compatible = "atmel,sama5d3-udc";
                        reg = <0x00300000 0x100000
                               0xfc02c000 0x400>;
                        clocks = <&pmc PMC_TYPE_PERIPHERAL 42>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
                        clock-names = "pclk", "hclk";
                        status = "disabled";
-
-                       ep@0 {
-                               reg = <0>;
-                               atmel,fifo-size = <64>;
-                               atmel,nb-banks = <1>;
-                       };
-
-                       ep@1 {
-                               reg = <1>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <3>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@2 {
-                               reg = <2>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <3>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@3 {
-                               reg = <3>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@4 {
-                               reg = <4>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@5 {
-                               reg = <5>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@6 {
-                               reg = <6>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@7 {
-                               reg = <7>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@8 {
-                               reg = <8>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@9 {
-                               reg = <9>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@10 {
-                               reg = <10>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@11 {
-                               reg = <11>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@12 {
-                               reg = <12>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@13 {
-                               reg = <13>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@14 {
-                               reg = <14>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@15 {
-                               reg = <15>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
                };
 
                usb1: ohci@400000 {
index c53e484..0bb5b6f 100644 (file)
                };
 
                usb0: gadget@500000 {
-                       #address-cells = <1>;
-                       #size-cells = <0>;
                        compatible = "atmel,sama5d3-udc";
                        reg = <0x00500000 0x100000
                               0xf8030000 0x4000>;
                        clocks = <&pmc PMC_TYPE_PERIPHERAL 33>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
                        clock-names = "pclk", "hclk";
                        status = "disabled";
-
-                       ep@0 {
-                               reg = <0>;
-                               atmel,fifo-size = <64>;
-                               atmel,nb-banks = <1>;
-                       };
-
-                       ep@1 {
-                               reg = <1>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <3>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@2 {
-                               reg = <2>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <3>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@3 {
-                               reg = <3>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                       };
-
-                       ep@4 {
-                               reg = <4>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                       };
-
-                       ep@5 {
-                               reg = <5>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                       };
-
-                       ep@6 {
-                               reg = <6>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                       };
-
-                       ep@7 {
-                               reg = <7>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                       };
-
-                       ep@8 {
-                               reg = <8>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
-
-                       ep@9 {
-                               reg = <9>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
-
-                       ep@10 {
-                               reg = <10>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
-
-                       ep@11 {
-                               reg = <11>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
-
-                       ep@12 {
-                               reg = <12>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
-
-                       ep@13 {
-                               reg = <13>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
-
-                       ep@14 {
-                               reg = <14>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
-
-                       ep@15 {
-                               reg = <15>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                       };
                };
 
                usb1: ohci@600000 {
index fff6797..2d9f853 100644 (file)
@@ -96,8 +96,6 @@
                };
 
                usb0: gadget@400000 {
-                       #address-cells = <1>;
-                       #size-cells = <0>;
                        compatible = "atmel,sama5d3-udc";
                        reg = <0x00400000 0x100000
                               0xfc02c000 0x4000>;
                        clocks = <&pmc PMC_TYPE_PERIPHERAL 47>, <&pmc PMC_TYPE_CORE PMC_UTMI>;
                        clock-names = "pclk", "hclk";
                        status = "disabled";
-
-                       ep@0 {
-                               reg = <0>;
-                               atmel,fifo-size = <64>;
-                               atmel,nb-banks = <1>;
-                       };
-
-                       ep@1 {
-                               reg = <1>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <3>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@2 {
-                               reg = <2>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <3>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@3 {
-                               reg = <3>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@4 {
-                               reg = <4>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@5 {
-                               reg = <5>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@6 {
-                               reg = <6>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@7 {
-                               reg = <7>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-dma;
-                               atmel,can-isoc;
-                       };
-
-                       ep@8 {
-                               reg = <8>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@9 {
-                               reg = <9>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@10 {
-                               reg = <10>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@11 {
-                               reg = <11>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@12 {
-                               reg = <12>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@13 {
-                               reg = <13>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@14 {
-                               reg = <14>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
-
-                       ep@15 {
-                               reg = <15>;
-                               atmel,fifo-size = <1024>;
-                               atmel,nb-banks = <2>;
-                               atmel,can-isoc;
-                       };
                };
 
                usb1: ohci@500000 {
index 7c2b79d..31b9217 100644 (file)
 
                        resets = <&gcc GCC_USB30_PRIM_BCR>;
 
+                       interconnects = <&aggre2_noc MASTER_USB3 &mc_virt SLAVE_EBI1>,
+                                       <&gem_noc MASTER_APPSS_PROC &config_noc SLAVE_USB3>;
+                       interconnect-names = "usb-ddr", "apps-usb";
+
                        usb_1_dwc3: dwc3@a600000 {
                                compatible = "snps,dwc3";
                                reg = <0 0x0a600000 0 0xe000>;
index 7cce6f1..8eb5a31 100644 (file)
 
                        resets = <&gcc GCC_USB30_PRIM_BCR>;
 
+                       interconnects = <&aggre2_noc MASTER_USB3_0 &mem_noc SLAVE_EBI1>,
+                                       <&gladiator_noc MASTER_APPSS_PROC &config_noc SLAVE_USB3_0>;
+                       interconnect-names = "usb-ddr", "apps-usb";
+
                        usb_1_dwc3: dwc3@a600000 {
                                compatible = "snps,dwc3";
                                reg = <0 0x0a600000 0 0xcd00>;
 
                        resets = <&gcc GCC_USB30_SEC_BCR>;
 
+                       interconnects = <&aggre2_noc MASTER_USB3_1 &mem_noc SLAVE_EBI1>,
+                                       <&gladiator_noc MASTER_APPSS_PROC &config_noc SLAVE_USB3_1>;
+                       interconnect-names = "usb-ddr", "apps-usb";
+
                        usb_2_dwc3: dwc3@a800000 {
                                compatible = "snps,dwc3";
                                reg = <0 0x0a800000 0 0xcd00>;
index 2c5dd13..219eb00 100644 (file)
@@ -466,6 +466,24 @@ static struct gpio_desc *of_find_arizona_gpio(struct device *dev,
        return of_get_named_gpiod_flags(dev->of_node, con_id, 0, of_flags);
 }
 
+static struct gpio_desc *of_find_usb_gpio(struct device *dev,
+                                         const char *con_id,
+                                         enum of_gpio_flags *of_flags)
+{
+       /*
+        * Currently this USB quirk is only for the Fairchild FUSB302 host which is using
+        * an undocumented DT GPIO line named "fcs,int_n" without the compulsory "-gpios"
+        * suffix.
+        */
+       if (!IS_ENABLED(CONFIG_TYPEC_FUSB302))
+               return ERR_PTR(-ENOENT);
+
+       if (!con_id || strcmp(con_id, "fcs,int_n"))
+               return ERR_PTR(-ENOENT);
+
+       return of_get_named_gpiod_flags(dev->of_node, con_id, 0, of_flags);
+}
+
 struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
                               unsigned int idx, unsigned long *flags)
 {
@@ -510,6 +528,9 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id,
        if (PTR_ERR(desc) == -ENOENT)
                desc = of_find_arizona_gpio(dev, con_id, &of_flags);
 
+       if (PTR_ERR(desc) == -ENOENT)
+               desc = of_find_usb_gpio(dev, con_id, &of_flags);
+
        if (IS_ERR(desc))
                return desc;
 
index 71801e3..617cf07 100644 (file)
@@ -3,12 +3,13 @@
 # Phy drivers for Amlogic platforms
 #
 config PHY_MESON8B_USB2
-       tristate "Meson8, Meson8b and GXBB USB2 PHY driver"
+       tristate "Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver"
        default ARCH_MESON
        depends on OF && (ARCH_MESON || COMPILE_TEST)
        depends on USB_SUPPORT
        select USB_COMMON
        select GENERIC_PHY
+       select REGMAP_MMIO
        help
          Enable this to support the Meson USB2 PHYs found in Meson8,
          Meson8b and GXBB SoCs.
@@ -26,18 +27,6 @@ config PHY_MESON_GXL_USB2
          GXL and GXM SoCs.
          If unsure, say N.
 
-config PHY_MESON_GXL_USB3
-       tristate "Meson GXL and GXM USB3 PHY drivers"
-       default ARCH_MESON
-       depends on OF && (ARCH_MESON || COMPILE_TEST)
-       depends on USB_SUPPORT
-       select GENERIC_PHY
-       select REGMAP_MMIO
-       help
-         Enable this to support the Meson USB3 PHY and OTG detection
-         IP block found in Meson GXL and GXM SoCs.
-         If unsure, say N.
-
 config PHY_MESON_G12A_USB2
        tristate "Meson G12A USB2 PHY driver"
        default ARCH_MESON
index e2baa13..99702a4 100644 (file)
@@ -2,7 +2,6 @@
 obj-$(CONFIG_PHY_MESON8B_USB2)                 += phy-meson8b-usb2.o
 obj-$(CONFIG_PHY_MESON_GXL_USB2)               += phy-meson-gxl-usb2.o
 obj-$(CONFIG_PHY_MESON_G12A_USB2)              += phy-meson-g12a-usb2.o
-obj-$(CONFIG_PHY_MESON_GXL_USB3)               += phy-meson-gxl-usb3.o
 obj-$(CONFIG_PHY_MESON_G12A_USB3_PCIE)         += phy-meson-g12a-usb3-pcie.o
 obj-$(CONFIG_PHY_MESON_AXG_PCIE)               += phy-meson-axg-pcie.o
 obj-$(CONFIG_PHY_MESON_AXG_MIPI_PCIE_ANALOG)   += phy-meson-axg-mipi-pcie-analog.o
diff --git a/drivers/phy/amlogic/phy-meson-gxl-usb3.c b/drivers/phy/amlogic/phy-meson-gxl-usb3.c
deleted file mode 100644 (file)
index c0e9e4c..0000000
+++ /dev/null
@@ -1,283 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Meson GXL USB3 PHY and OTG mode detection driver
- *
- * Copyright (C) 2018 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- */
-
-#include <linux/bitfield.h>
-#include <linux/bitops.h>
-#include <linux/clk.h>
-#include <linux/module.h>
-#include <linux/of_device.h>
-#include <linux/phy/phy.h>
-#include <linux/regmap.h>
-#include <linux/reset.h>
-#include <linux/platform_device.h>
-
-#define USB_R0                                                 0x00
-       #define USB_R0_P30_FSEL_MASK                            GENMASK(5, 0)
-       #define USB_R0_P30_PHY_RESET                            BIT(6)
-       #define USB_R0_P30_TEST_POWERDOWN_HSP                   BIT(7)
-       #define USB_R0_P30_TEST_POWERDOWN_SSP                   BIT(8)
-       #define USB_R0_P30_ACJT_LEVEL_MASK                      GENMASK(13, 9)
-       #define USB_R0_P30_TX_BOOST_LEVEL_MASK                  GENMASK(16, 14)
-       #define USB_R0_P30_LANE0_TX2RX_LOOPBACK                 BIT(17)
-       #define USB_R0_P30_LANE0_EXT_PCLK_REQ                   BIT(18)
-       #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK             GENMASK(28, 19)
-       #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK               GENMASK(30, 29)
-       #define USB_R0_U2D_ACT                                  BIT(31)
-
-#define USB_R1                                                 0x04
-       #define USB_R1_U3H_BIGENDIAN_GS                         BIT(0)
-       #define USB_R1_U3H_PME_ENABLE                           BIT(1)
-       #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK            GENMASK(6, 2)
-       #define USB_R1_U3H_HUB_PORT_PERM_ATTACH_MASK            GENMASK(11, 7)
-       #define USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK            GENMASK(15, 12)
-       #define USB_R1_U3H_HOST_U3_PORT_DISABLE                 BIT(16)
-       #define USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT      BIT(17)
-       #define USB_R1_U3H_HOST_MSI_ENABLE                      BIT(18)
-       #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK                 GENMASK(24, 19)
-       #define USB_R1_P30_PCS_TX_SWING_FULL_MASK               GENMASK(31, 25)
-
-#define USB_R2                                                 0x08
-       #define USB_R2_P30_CR_DATA_IN_MASK                      GENMASK(15, 0)
-       #define USB_R2_P30_CR_READ                              BIT(16)
-       #define USB_R2_P30_CR_WRITE                             BIT(17)
-       #define USB_R2_P30_CR_CAP_ADDR                          BIT(18)
-       #define USB_R2_P30_CR_CAP_DATA                          BIT(19)
-       #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK             GENMASK(25, 20)
-       #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK               GENMASK(31, 26)
-
-#define USB_R3                                                 0x0c
-       #define USB_R3_P30_SSC_ENABLE                           BIT(0)
-       #define USB_R3_P30_SSC_RANGE_MASK                       GENMASK(3, 1)
-       #define USB_R3_P30_SSC_REF_CLK_SEL_MASK                 GENMASK(12, 4)
-       #define USB_R3_P30_REF_SSP_EN                           BIT(13)
-       #define USB_R3_P30_LOS_BIAS_MASK                        GENMASK(18, 16)
-       #define USB_R3_P30_LOS_LEVEL_MASK                       GENMASK(23, 19)
-       #define USB_R3_P30_MPLL_MULTIPLIER_MASK                 GENMASK(30, 24)
-
-#define USB_R4                                                 0x10
-       #define USB_R4_P21_PORT_RESET_0                         BIT(0)
-       #define USB_R4_P21_SLEEP_M0                             BIT(1)
-       #define USB_R4_MEM_PD_MASK                              GENMASK(3, 2)
-       #define USB_R4_P21_ONLY                                 BIT(4)
-
-#define USB_R5                                                 0x14
-       #define USB_R5_ID_DIG_SYNC                              BIT(0)
-       #define USB_R5_ID_DIG_REG                               BIT(1)
-       #define USB_R5_ID_DIG_CFG_MASK                          GENMASK(3, 2)
-       #define USB_R5_ID_DIG_EN_0                              BIT(4)
-       #define USB_R5_ID_DIG_EN_1                              BIT(5)
-       #define USB_R5_ID_DIG_CURR                              BIT(6)
-       #define USB_R5_ID_DIG_IRQ                               BIT(7)
-       #define USB_R5_ID_DIG_TH_MASK                           GENMASK(15, 8)
-       #define USB_R5_ID_DIG_CNT_MASK                          GENMASK(23, 16)
-
-/* read-only register */
-#define USB_R6                                                 0x18
-       #define USB_R6_P30_CR_DATA_OUT_MASK                     GENMASK(15, 0)
-       #define USB_R6_P30_CR_ACK                               BIT(16)
-
-struct phy_meson_gxl_usb3_priv {
-       struct regmap           *regmap;
-       enum phy_mode           mode;
-       struct clk              *clk_phy;
-       struct clk              *clk_peripheral;
-       struct reset_control    *reset;
-};
-
-static const struct regmap_config phy_meson_gxl_usb3_regmap_conf = {
-       .reg_bits = 8,
-       .val_bits = 32,
-       .reg_stride = 4,
-       .max_register = USB_R6,
-};
-
-static int phy_meson_gxl_usb3_power_on(struct phy *phy)
-{
-       struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
-       regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0,
-                          USB_R5_ID_DIG_EN_0);
-       regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1,
-                          USB_R5_ID_DIG_EN_1);
-       regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_TH_MASK,
-                          FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
-
-       return 0;
-}
-
-static int phy_meson_gxl_usb3_power_off(struct phy *phy)
-{
-       struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
-       regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_0, 0);
-       regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_EN_1, 0);
-
-       return 0;
-}
-
-static int phy_meson_gxl_usb3_set_mode(struct phy *phy,
-                                      enum phy_mode mode, int submode)
-{
-       struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
-       switch (mode) {
-       case PHY_MODE_USB_HOST:
-               regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT, 0);
-               regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
-                                  0);
-               break;
-
-       case PHY_MODE_USB_DEVICE:
-               regmap_update_bits(priv->regmap, USB_R0, USB_R0_U2D_ACT,
-                                  USB_R0_U2D_ACT);
-               regmap_update_bits(priv->regmap, USB_R4, USB_R4_P21_SLEEP_M0,
-                                  USB_R4_P21_SLEEP_M0);
-               break;
-
-       default:
-               dev_err(&phy->dev, "unsupported PHY mode %d\n", mode);
-               return -EINVAL;
-       }
-
-       priv->mode = mode;
-
-       return 0;
-}
-
-static int phy_meson_gxl_usb3_init(struct phy *phy)
-{
-       struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-       int ret;
-
-       ret = reset_control_reset(priv->reset);
-       if (ret)
-               goto err;
-
-       ret = clk_prepare_enable(priv->clk_phy);
-       if (ret)
-               goto err;
-
-       ret = clk_prepare_enable(priv->clk_peripheral);
-       if (ret)
-               goto err_disable_clk_phy;
-
-       ret = phy_meson_gxl_usb3_set_mode(phy, priv->mode, 0);
-       if (ret)
-               goto err_disable_clk_peripheral;
-
-       regmap_update_bits(priv->regmap, USB_R1,
-                          USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
-                          FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
-
-       return 0;
-
-err_disable_clk_peripheral:
-       clk_disable_unprepare(priv->clk_peripheral);
-err_disable_clk_phy:
-       clk_disable_unprepare(priv->clk_phy);
-err:
-       return ret;
-}
-
-static int phy_meson_gxl_usb3_exit(struct phy *phy)
-{
-       struct phy_meson_gxl_usb3_priv *priv = phy_get_drvdata(phy);
-
-       clk_disable_unprepare(priv->clk_peripheral);
-       clk_disable_unprepare(priv->clk_phy);
-
-       return 0;
-}
-
-static const struct phy_ops phy_meson_gxl_usb3_ops = {
-       .power_on       = phy_meson_gxl_usb3_power_on,
-       .power_off      = phy_meson_gxl_usb3_power_off,
-       .set_mode       = phy_meson_gxl_usb3_set_mode,
-       .init           = phy_meson_gxl_usb3_init,
-       .exit           = phy_meson_gxl_usb3_exit,
-       .owner          = THIS_MODULE,
-};
-
-static int phy_meson_gxl_usb3_probe(struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct device_node *np = dev->of_node;
-       struct phy_meson_gxl_usb3_priv *priv;
-       struct resource *res;
-       struct phy *phy;
-       struct phy_provider *phy_provider;
-       void __iomem *base;
-       int ret;
-
-       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
-       if (!priv)
-               return -ENOMEM;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(base))
-               return PTR_ERR(base);
-
-       priv->regmap = devm_regmap_init_mmio(dev, base,
-                                            &phy_meson_gxl_usb3_regmap_conf);
-       if (IS_ERR(priv->regmap))
-               return PTR_ERR(priv->regmap);
-
-       priv->clk_phy = devm_clk_get(dev, "phy");
-       if (IS_ERR(priv->clk_phy))
-               return PTR_ERR(priv->clk_phy);
-
-       priv->clk_peripheral = devm_clk_get(dev, "peripheral");
-       if (IS_ERR(priv->clk_peripheral))
-               return PTR_ERR(priv->clk_peripheral);
-
-       priv->reset = devm_reset_control_array_get_shared(dev);
-       if (IS_ERR(priv->reset))
-               return PTR_ERR(priv->reset);
-
-       /*
-        * default to host mode as hardware defaults and/or boot-loader
-        * behavior can result in this PHY starting up in device mode. this
-        * default and the initialization in phy_meson_gxl_usb3_init ensure
-        * that we reproducibly start in a known mode on all devices.
-        */
-       priv->mode = PHY_MODE_USB_HOST;
-
-       phy = devm_phy_create(dev, np, &phy_meson_gxl_usb3_ops);
-       if (IS_ERR(phy)) {
-               ret = PTR_ERR(phy);
-               if (ret != -EPROBE_DEFER)
-                       dev_err(dev, "failed to create PHY\n");
-
-               return ret;
-       }
-
-       phy_set_drvdata(phy, priv);
-
-       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
-
-       return PTR_ERR_OR_ZERO(phy_provider);
-}
-
-static const struct of_device_id phy_meson_gxl_usb3_of_match[] = {
-       { .compatible = "amlogic,meson-gxl-usb3-phy", },
-       { },
-};
-MODULE_DEVICE_TABLE(of, phy_meson_gxl_usb3_of_match);
-
-static struct platform_driver phy_meson_gxl_usb3_driver = {
-       .probe  = phy_meson_gxl_usb3_probe,
-       .driver = {
-               .name           = "phy-meson-gxl-usb3",
-               .of_match_table = phy_meson_gxl_usb3_of_match,
-       },
-};
-module_platform_driver(phy_meson_gxl_usb3_driver);
-
-MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
-MODULE_DESCRIPTION("Meson GXL USB3 PHY and OTG detection driver");
-MODULE_LICENSE("GPL v2");
index bd66bd7..03c061d 100644 (file)
@@ -10,6 +10,8 @@
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/of_device.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
 #include <linux/reset.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
        #define REG_ADP_BC_ACA_PIN_FLOAT                BIT(26)
 
 #define REG_DBG_UART                                   0x10
+       #define REG_DBG_UART_BYPASS_SEL                 BIT(0)
+       #define REG_DBG_UART_BYPASS_DM_EN               BIT(1)
+       #define REG_DBG_UART_BYPASS_DP_EN               BIT(2)
+       #define REG_DBG_UART_BYPASS_DM_DATA             BIT(3)
+       #define REG_DBG_UART_BYPASS_DP_DATA             BIT(4)
+       #define REG_DBG_UART_FSV_MINUS                  BIT(5)
+       #define REG_DBG_UART_FSV_PLUS                   BIT(6)
+       #define REG_DBG_UART_FSV_BURN_IN_TEST           BIT(7)
+       #define REG_DBG_UART_LOOPBACK_EN_B              BIT(8)
+       #define REG_DBG_UART_SET_IDDQ                   BIT(9)
+       #define REG_DBG_UART_ATE_RESET                  BIT(10)
 
 #define REG_TEST                                       0x14
        #define REG_TEST_DATA_IN_MASK                   GENMASK(3, 0)
 #define RESET_COMPLETE_TIME                            500
 #define ACA_ENABLE_COMPLETE_TIME                       50
 
-struct phy_meson8b_usb2_priv {
-       void __iomem            *regs;
-       enum usb_dr_mode        dr_mode;
-       struct clk              *clk_usb_general;
-       struct clk              *clk_usb;
-       struct reset_control    *reset;
+struct phy_meson8b_usb2_match_data {
+       bool                    host_enable_aca;
 };
 
-static u32 phy_meson8b_usb2_read(struct phy_meson8b_usb2_priv *phy_priv,
-                                u32 reg)
-{
-       return readl(phy_priv->regs + reg);
-}
-
-static void phy_meson8b_usb2_mask_bits(struct phy_meson8b_usb2_priv *phy_priv,
-                                      u32 reg, u32 mask, u32 value)
-{
-       u32 data;
-
-       data = phy_meson8b_usb2_read(phy_priv, reg);
-       data &= ~mask;
-       data |= (value & mask);
+struct phy_meson8b_usb2_priv {
+       struct regmap                                   *regmap;
+       enum usb_dr_mode                                dr_mode;
+       struct clk                                      *clk_usb_general;
+       struct clk                                      *clk_usb;
+       struct reset_control                            *reset;
+       const struct phy_meson8b_usb2_match_data        *match;
+};
 
-       writel(data, phy_priv->regs + reg);
-}
+static const struct regmap_config phy_meson8b_usb2_regmap_conf = {
+       .reg_bits = 8,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .max_register = REG_TUNE,
+};
 
 static int phy_meson8b_usb2_power_on(struct phy *phy)
 {
        struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy);
+       u32 reg;
        int ret;
 
        if (!IS_ERR_OR_NULL(priv->reset)) {
@@ -156,38 +164,43 @@ static int phy_meson8b_usb2_power_on(struct phy *phy)
                return ret;
        }
 
-       phy_meson8b_usb2_mask_bits(priv, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL,
-                                  REG_CONFIG_CLK_32k_ALTSEL);
+       regmap_update_bits(priv->regmap, REG_CONFIG, REG_CONFIG_CLK_32k_ALTSEL,
+                          REG_CONFIG_CLK_32k_ALTSEL);
 
-       phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK,
-                                  0x2 << REG_CTRL_REF_CLK_SEL_SHIFT);
+       regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_REF_CLK_SEL_MASK,
+                          0x2 << REG_CTRL_REF_CLK_SEL_SHIFT);
 
-       phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_FSEL_MASK,
-                                  0x5 << REG_CTRL_FSEL_SHIFT);
+       regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_FSEL_MASK,
+                          0x5 << REG_CTRL_FSEL_SHIFT);
 
        /* reset the PHY */
-       phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET,
-                                  REG_CTRL_POWER_ON_RESET);
+       regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET,
+                          REG_CTRL_POWER_ON_RESET);
        udelay(RESET_COMPLETE_TIME);
-       phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0);
+       regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_POWER_ON_RESET, 0);
        udelay(RESET_COMPLETE_TIME);
 
-       phy_meson8b_usb2_mask_bits(priv, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT,
-                                  REG_CTRL_SOF_TOGGLE_OUT);
+       regmap_update_bits(priv->regmap, REG_CTRL, REG_CTRL_SOF_TOGGLE_OUT,
+                          REG_CTRL_SOF_TOGGLE_OUT);
 
        if (priv->dr_mode == USB_DR_MODE_HOST) {
-               phy_meson8b_usb2_mask_bits(priv, REG_ADP_BC,
+               regmap_update_bits(priv->regmap, REG_DBG_UART,
+                                  REG_DBG_UART_SET_IDDQ, 0);
+
+               if (priv->match->host_enable_aca) {
+                       regmap_update_bits(priv->regmap, REG_ADP_BC,
                                           REG_ADP_BC_ACA_ENABLE,
                                           REG_ADP_BC_ACA_ENABLE);
 
-               udelay(ACA_ENABLE_COMPLETE_TIME);
+                       udelay(ACA_ENABLE_COMPLETE_TIME);
 
-               if (phy_meson8b_usb2_read(priv, REG_ADP_BC) &
-                       REG_ADP_BC_ACA_PIN_FLOAT) {
-                       dev_warn(&phy->dev, "USB ID detect failed!\n");
-                       clk_disable_unprepare(priv->clk_usb);
-                       clk_disable_unprepare(priv->clk_usb_general);
-                       return -EINVAL;
+                       regmap_read(priv->regmap, REG_ADP_BC, &reg);
+                       if (reg & REG_ADP_BC_ACA_PIN_FLOAT) {
+                               dev_warn(&phy->dev, "USB ID detect failed!\n");
+                               clk_disable_unprepare(priv->clk_usb);
+                               clk_disable_unprepare(priv->clk_usb_general);
+                               return -EINVAL;
+                       }
                }
        }
 
@@ -198,6 +211,11 @@ static int phy_meson8b_usb2_power_off(struct phy *phy)
 {
        struct phy_meson8b_usb2_priv *priv = phy_get_drvdata(phy);
 
+       if (priv->dr_mode == USB_DR_MODE_HOST)
+               regmap_update_bits(priv->regmap, REG_DBG_UART,
+                                  REG_DBG_UART_SET_IDDQ,
+                                  REG_DBG_UART_SET_IDDQ);
+
        clk_disable_unprepare(priv->clk_usb);
        clk_disable_unprepare(priv->clk_usb_general);
 
@@ -213,18 +231,26 @@ static const struct phy_ops phy_meson8b_usb2_ops = {
 static int phy_meson8b_usb2_probe(struct platform_device *pdev)
 {
        struct phy_meson8b_usb2_priv *priv;
-       struct resource *res;
        struct phy *phy;
        struct phy_provider *phy_provider;
+       void __iomem *base;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->regs = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(priv->regs))
-               return PTR_ERR(priv->regs);
+       base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       priv->match = device_get_match_data(&pdev->dev);
+       if (!priv->match)
+               return -ENODEV;
+
+       priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                            &phy_meson8b_usb2_regmap_conf);
+       if (IS_ERR(priv->regmap))
+               return PTR_ERR(priv->regmap);
 
        priv->clk_usb_general = devm_clk_get(&pdev->dev, "usb_general");
        if (IS_ERR(priv->clk_usb_general))
@@ -259,11 +285,32 @@ static int phy_meson8b_usb2_probe(struct platform_device *pdev)
        return PTR_ERR_OR_ZERO(phy_provider);
 }
 
+static const struct phy_meson8b_usb2_match_data phy_meson8_usb2_match_data = {
+       .host_enable_aca = false,
+};
+
+static const struct phy_meson8b_usb2_match_data phy_meson8b_usb2_match_data = {
+       .host_enable_aca = true,
+};
+
 static const struct of_device_id phy_meson8b_usb2_of_match[] = {
-       { .compatible = "amlogic,meson8-usb2-phy", },
-       { .compatible = "amlogic,meson8b-usb2-phy", },
-       { .compatible = "amlogic,meson-gxbb-usb2-phy", },
-       { },
+       {
+               .compatible = "amlogic,meson8-usb2-phy",
+               .data = &phy_meson8_usb2_match_data
+       },
+       {
+               .compatible = "amlogic,meson8b-usb2-phy",
+               .data = &phy_meson8b_usb2_match_data
+       },
+       {
+               .compatible = "amlogic,meson8m2-usb2-phy",
+               .data = &phy_meson8b_usb2_match_data
+       },
+       {
+               .compatible = "amlogic,meson-gxbb-usb2-phy",
+               .data = &phy_meson8b_usb2_match_data
+       },
+       { /* sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, phy_meson8b_usb2_of_match);
 
@@ -277,5 +324,5 @@ static struct platform_driver phy_meson8b_usb2_driver = {
 module_platform_driver(phy_meson8b_usb2_driver);
 
 MODULE_AUTHOR("Martin Blumenstingl <martin.blumenstingl@googlemail.com>");
-MODULE_DESCRIPTION("Meson8, Meson8b and GXBB USB2 PHY driver");
+MODULE_DESCRIPTION("Meson8, Meson8b, Meson8m2 and GXBB USB2 PHY driver");
 MODULE_LICENSE("GPL");
index 7ceea5a..5276259 100644 (file)
@@ -279,7 +279,7 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
        .init           = ns2_drd_phy_init,
        .power_on       = ns2_drd_phy_poweron,
        .power_off      = ns2_drd_phy_poweroff,
index fe6c589..77c025a 100644 (file)
@@ -16,8 +16,6 @@ enum bcm_usb_phy_version {
 };
 
 enum bcm_usb_phy_reg {
-       PLL_NDIV_FRAC,
-       PLL_NDIV_INT,
        PLL_CTRL,
        PHY_CTRL,
        PHY_PLL_CTRL,
@@ -31,18 +29,11 @@ static const u8 bcm_usb_combo_phy_ss[] = {
 };
 
 static const u8 bcm_usb_combo_phy_hs[] = {
-       [PLL_NDIV_FRAC] = 0x04,
-       [PLL_NDIV_INT]  = 0x08,
        [PLL_CTRL]      = 0x0c,
        [PHY_CTRL]      = 0x10,
 };
 
-#define HSPLL_NDIV_INT_VAL     0x13
-#define HSPLL_NDIV_FRAC_VAL    0x1005
-
 static const u8 bcm_usb_hs_phy[] = {
-       [PLL_NDIV_FRAC] = 0x0,
-       [PLL_NDIV_INT]  = 0x4,
        [PLL_CTRL]      = 0x8,
        [PHY_CTRL]      = 0xc,
 };
@@ -52,7 +43,6 @@ enum pll_ctrl_bits {
        SSPLL_SUSPEND_EN,
        PLL_SEQ_START,
        PLL_LOCK,
-       PLL_PDIV,
 };
 
 static const u8 u3pll_ctrl[] = {
@@ -66,29 +56,17 @@ static const u8 u3pll_ctrl[] = {
 #define HSPLL_PDIV_VAL         0x1
 
 static const u8 u2pll_ctrl[] = {
-       [PLL_PDIV]      = 1,
        [PLL_RESETB]    = 5,
        [PLL_LOCK]      = 6,
 };
 
 enum bcm_usb_phy_ctrl_bits {
        CORERDY,
-       AFE_LDO_PWRDWNB,
-       AFE_PLL_PWRDWNB,
-       AFE_BG_PWRDWNB,
-       PHY_ISO,
        PHY_RESETB,
        PHY_PCTL,
 };
 
 #define PHY_PCTL_MASK  0xffff
-/*
- * 0x0806 of PCTL_VAL has below bits set
- * BIT-8 : refclk divider 1
- * BIT-3:2: device mode; mode is not effect
- * BIT-1: soft reset active low
- */
-#define HSPHY_PCTL_VAL 0x0806
 #define SSPHY_PCTL_VAL 0x0006
 
 static const u8 u3phy_ctrl[] = {
@@ -98,10 +76,6 @@ static const u8 u3phy_ctrl[] = {
 
 static const u8 u2phy_ctrl[] = {
        [CORERDY]               = 0,
-       [AFE_LDO_PWRDWNB]       = 1,
-       [AFE_PLL_PWRDWNB]       = 2,
-       [AFE_BG_PWRDWNB]        = 3,
-       [PHY_ISO]               = 4,
        [PHY_RESETB]            = 5,
        [PHY_PCTL]              = 6,
 };
@@ -186,38 +160,13 @@ static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
        int ret = 0;
        void __iomem *regs = phy_cfg->regs;
        const u8 *offset;
-       u32 rd_data;
 
        offset = phy_cfg->offset;
 
-       writel(HSPLL_NDIV_INT_VAL, regs + offset[PLL_NDIV_INT]);
-       writel(HSPLL_NDIV_FRAC_VAL, regs + offset[PLL_NDIV_FRAC]);
-
-       rd_data = readl(regs + offset[PLL_CTRL]);
-       rd_data &= ~(HSPLL_PDIV_MASK << u2pll_ctrl[PLL_PDIV]);
-       rd_data |= (HSPLL_PDIV_VAL << u2pll_ctrl[PLL_PDIV]);
-       writel(rd_data, regs + offset[PLL_CTRL]);
-
-       /* Set Core Ready high */
-       bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
-                             BIT(u2phy_ctrl[CORERDY]));
-
-       /* Maximum timeout for Core Ready done */
-       msleep(30);
-
+       bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],
+                             BIT(u2pll_ctrl[PLL_RESETB]));
        bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
                              BIT(u2pll_ctrl[PLL_RESETB]));
-       bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
-                             BIT(u2phy_ctrl[PHY_RESETB]));
-
-
-       rd_data = readl(regs + offset[PHY_CTRL]);
-       rd_data &= ~(PHY_PCTL_MASK << u2phy_ctrl[PHY_PCTL]);
-       rd_data |= (HSPHY_PCTL_VAL << u2phy_ctrl[PHY_PCTL]);
-       writel(rd_data, regs + offset[PHY_CTRL]);
-
-       /* Maximum timeout for PLL reset done */
-       msleep(30);
 
        ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
                                     BIT(u2pll_ctrl[PLL_LOCK]));
@@ -256,7 +205,7 @@ static int bcm_usb_phy_init(struct phy *phy)
        return ret;
 }
 
-static struct phy_ops sr_phy_ops = {
+static const struct phy_ops sr_phy_ops = {
        .init           = bcm_usb_phy_init,
        .reset          = bcm_usb_phy_reset,
        .owner          = THIS_MODULE,
index 491bbd4..99fbc7e 100644 (file)
@@ -39,14 +39,14 @@ struct match_chip_info {
        u8 optional_reg;
 };
 
-static struct value_to_name_map brcm_dr_mode_to_name[] = {
+static const struct value_to_name_map brcm_dr_mode_to_name[] = {
        { USB_CTLR_MODE_HOST, "host" },
        { USB_CTLR_MODE_DEVICE, "peripheral" },
        { USB_CTLR_MODE_DRD, "drd" },
        { USB_CTLR_MODE_TYPEC_PD, "typec-pd" }
 };
 
-static struct value_to_name_map brcm_dual_mode_to_name[] = {
+static const struct value_to_name_map brcm_dual_mode_to_name[] = {
        { 0, "host" },
        { 1, "device" },
        { 2, "auto" },
@@ -138,7 +138,7 @@ static int brcm_usb_phy_exit(struct phy *gphy)
        return 0;
 }
 
-static struct phy_ops brcm_usb_phy_ops = {
+static const struct phy_ops brcm_usb_phy_ops = {
        .init           = brcm_usb_phy_init,
        .exit           = brcm_usb_phy_exit,
        .owner          = THIS_MODULE,
@@ -170,7 +170,7 @@ static struct phy *brcm_usb_phy_xlate(struct device *dev,
        return ERR_PTR(-ENODEV);
 }
 
-static int name_to_value(struct value_to_name_map *table, int count,
+static int name_to_value(const struct value_to_name_map *table, int count,
                         const char *name, int *value)
 {
        int x;
@@ -185,7 +185,7 @@ static int name_to_value(struct value_to_name_map *table, int count,
        return -EINVAL;
 }
 
-static const char *value_to_name(struct value_to_name_map *table, int count,
+static const char *value_to_name(const struct value_to_name_map *table, int count,
                                 int value)
 {
        if (value >= count)
@@ -252,7 +252,7 @@ static const struct attribute_group brcm_usb_phy_group = {
        .attrs = brcm_usb_phy_attrs,
 };
 
-static struct match_chip_info chip_info_7216 = {
+static const struct match_chip_info chip_info_7216 = {
        .init_func = &brcm_usb_dvr_init_7216,
        .required_regs = {
                BRCM_REGS_CTRL,
@@ -262,7 +262,7 @@ static struct match_chip_info chip_info_7216 = {
        },
 };
 
-static struct match_chip_info chip_info_7211b0 = {
+static const struct match_chip_info chip_info_7211b0 = {
        .init_func = &brcm_usb_dvr_init_7211b0,
        .required_regs = {
                BRCM_REGS_CTRL,
@@ -275,7 +275,7 @@ static struct match_chip_info chip_info_7211b0 = {
        .optional_reg = BRCM_REGS_BDC_EC,
 };
 
-static struct match_chip_info chip_info_7445 = {
+static const struct match_chip_info chip_info_7445 = {
        .init_func = &brcm_usb_dvr_init_7445,
        .required_regs = {
                BRCM_REGS_CTRL,
index 4595458..432832b 100644 (file)
@@ -27,3 +27,12 @@ config PHY_CADENCE_SIERRA
        select GENERIC_PHY
        help
          Enable this to support the Cadence Sierra PHY driver
+
+config PHY_CADENCE_SALVO
+       tristate "Cadence Salvo PHY Driver"
+       depends on OF && HAS_IOMEM
+       select GENERIC_PHY
+       help
+         Enable this to support the Cadence SALVO PHY driver,
+         this PHY is a legacy PHY, and only are used for USB3
+         and USB2.
index 6a7ffc6..26e16bd 100644 (file)
@@ -2,3 +2,4 @@
 obj-$(CONFIG_PHY_CADENCE_TORRENT)      += phy-cadence-torrent.o
 obj-$(CONFIG_PHY_CADENCE_DPHY) += cdns-dphy.o
 obj-$(CONFIG_PHY_CADENCE_SIERRA)       += phy-cadence-sierra.o
+obj-$(CONFIG_PHY_CADENCE_SALVO)        += phy-cadence-salvo.o
diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c
new file mode 100644 (file)
index 0000000..1ecbb96
--- /dev/null
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Salvo PHY is a 28nm PHY, it is a legacy PHY, and only
+ * for USB3 and USB2.
+ *
+ * Copyright (c) 2019-2020 NXP
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+
+/* PHY register definition */
+#define PHY_PMA_CMN_CTRL1                      0xC800
+#define TB_ADDR_CMN_DIAG_HSCLK_SEL             0x01e0
+#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR       0x0084
+#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR       0x0085
+#define TB_ADDR_CMN_PLL0_INTDIV                        0x0094
+#define TB_ADDR_CMN_PLL0_FRACDIV               0x0095
+#define TB_ADDR_CMN_PLL0_HIGH_THR              0x0096
+#define TB_ADDR_CMN_PLL0_SS_CTRL1              0x0098
+#define TB_ADDR_CMN_PLL0_SS_CTRL2              0x0099
+#define TB_ADDR_CMN_PLL0_DSM_DIAG              0x0097
+#define TB_ADDR_CMN_DIAG_PLL0_OVRD             0x01c2
+#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD         0x01c0
+#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD         0x01c1
+#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE          0x01C5
+#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE           0x01C6
+#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG           0x01C7
+#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE                0x01c4
+#define TB_ADDR_CMN_PSM_CLK_CTRL               0x0061
+#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR  0x40ea
+#define TB_ADDR_XCVR_PSM_RCTRL                 0x4001
+#define TB_ADDR_TX_PSC_A0                      0x4100
+#define TB_ADDR_TX_PSC_A1                      0x4101
+#define TB_ADDR_TX_PSC_A2                      0x4102
+#define TB_ADDR_TX_PSC_A3                      0x4103
+#define TB_ADDR_TX_DIAG_ECTRL_OVRD             0x41f5
+#define TB_ADDR_TX_PSC_CAL                     0x4106
+#define TB_ADDR_TX_PSC_RDY                     0x4107
+#define TB_ADDR_RX_PSC_A0                      0x8000
+#define TB_ADDR_RX_PSC_A1                      0x8001
+#define TB_ADDR_RX_PSC_A2                      0x8002
+#define TB_ADDR_RX_PSC_A3                      0x8003
+#define TB_ADDR_RX_PSC_CAL                     0x8006
+#define TB_ADDR_RX_PSC_RDY                     0x8007
+#define TB_ADDR_TX_TXCC_MGNLS_MULT_000         0x4058
+#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY     0x41e7
+#define TB_ADDR_RX_SLC_CU_ITER_TMR             0x80e3
+#define TB_ADDR_RX_SIGDET_HL_FILT_TMR          0x8090
+#define TB_ADDR_RX_SAMP_DAC_CTRL               0x8058
+#define TB_ADDR_RX_DIAG_SIGDET_TUNE            0x81dc
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2          0x81df
+#define TB_ADDR_RX_DIAG_BS_TM                  0x81f5
+#define TB_ADDR_RX_DIAG_DFE_CTRL1              0x81d3
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4          0x81c7
+#define TB_ADDR_RX_DIAG_ILL_E_TRIM0            0x81c2
+#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0           0x81c1
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6          0x81c9
+#define TB_ADDR_RX_DIAG_RXFE_TM3               0x81f8
+#define TB_ADDR_RX_DIAG_RXFE_TM4               0x81f9
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE           0x81dd
+#define TB_ADDR_RX_DIAG_DFE_CTRL3              0x81d5
+#define TB_ADDR_RX_DIAG_SC2C_DELAY             0x81e1
+#define TB_ADDR_RX_REE_VGA_GAIN_NODFE          0x81bf
+#define TB_ADDR_XCVR_PSM_CAL_TMR               0x4002
+#define TB_ADDR_XCVR_PSM_A0BYP_TMR             0x4004
+#define TB_ADDR_XCVR_PSM_A0IN_TMR              0x4003
+#define TB_ADDR_XCVR_PSM_A1IN_TMR              0x4005
+#define TB_ADDR_XCVR_PSM_A2IN_TMR              0x4006
+#define TB_ADDR_XCVR_PSM_A3IN_TMR              0x4007
+#define TB_ADDR_XCVR_PSM_A4IN_TMR              0x4008
+#define TB_ADDR_XCVR_PSM_A5IN_TMR              0x4009
+#define TB_ADDR_XCVR_PSM_A0OUT_TMR             0x400a
+#define TB_ADDR_XCVR_PSM_A1OUT_TMR             0x400b
+#define TB_ADDR_XCVR_PSM_A2OUT_TMR             0x400c
+#define TB_ADDR_XCVR_PSM_A3OUT_TMR             0x400d
+#define TB_ADDR_XCVR_PSM_A4OUT_TMR             0x400e
+#define TB_ADDR_XCVR_PSM_A5OUT_TMR             0x400f
+#define TB_ADDR_TX_RCVDET_EN_TMR               0x4122
+#define TB_ADDR_TX_RCVDET_ST_TMR               0x4123
+#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR  0x40f2
+#define TB_ADDR_TX_RCVDETSC_CTRL               0x4124
+
+/* TB_ADDR_TX_RCVDETSC_CTRL */
+#define RXDET_IN_P3_32KHZ                      BIT(1)
+
+struct cdns_reg_pairs {
+       u16 val;
+       u32 off;
+};
+
+struct cdns_salvo_data {
+       u8 reg_offset_shift;
+       struct cdns_reg_pairs *init_sequence_val;
+       u8 init_sequence_length;
+};
+
+struct cdns_salvo_phy {
+       struct phy *phy;
+       struct clk *clk;
+       void __iomem *base;
+       struct cdns_salvo_data *data;
+};
+
+static const struct of_device_id cdns_salvo_phy_of_match[];
+static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 reg)
+{
+       return (u16)readl(salvo_phy->base +
+               reg * (1 << salvo_phy->data->reg_offset_shift));
+}
+
+static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy,
+                            u32 reg, u16 val)
+{
+       writel(val, salvo_phy->base +
+               reg * (1 << salvo_phy->data->reg_offset_shift));
+}
+
+/*
+ * Below bringup sequence pair are from Cadence PHY's User Guide
+ * and NXP platform tuning results.
+ */
+static struct cdns_reg_pairs cdns_nxp_sequence_pair[] = {
+       {0x0830, PHY_PMA_CMN_CTRL1},
+       {0x0010, TB_ADDR_CMN_DIAG_HSCLK_SEL},
+       {0x00f0, TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR},
+       {0x0018, TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR},
+       {0x00d0, TB_ADDR_CMN_PLL0_INTDIV},
+       {0x4aaa, TB_ADDR_CMN_PLL0_FRACDIV},
+       {0x0034, TB_ADDR_CMN_PLL0_HIGH_THR},
+       {0x01ee, TB_ADDR_CMN_PLL0_SS_CTRL1},
+       {0x7f03, TB_ADDR_CMN_PLL0_SS_CTRL2},
+       {0x0020, TB_ADDR_CMN_PLL0_DSM_DIAG},
+       {0x0000, TB_ADDR_CMN_DIAG_PLL0_OVRD},
+       {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD},
+       {0x0000, TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD},
+       {0x0007, TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE},
+       {0x0027, TB_ADDR_CMN_DIAG_PLL0_CP_TUNE},
+       {0x0008, TB_ADDR_CMN_DIAG_PLL0_LF_PROG},
+       {0x0022, TB_ADDR_CMN_DIAG_PLL0_TEST_MODE},
+       {0x000a, TB_ADDR_CMN_PSM_CLK_CTRL},
+       {0x0139, TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR},
+       {0xbefc, TB_ADDR_XCVR_PSM_RCTRL},
+
+       {0x7799, TB_ADDR_TX_PSC_A0},
+       {0x7798, TB_ADDR_TX_PSC_A1},
+       {0x509b, TB_ADDR_TX_PSC_A2},
+       {0x0003, TB_ADDR_TX_DIAG_ECTRL_OVRD},
+       {0x509b, TB_ADDR_TX_PSC_A3},
+       {0x2090, TB_ADDR_TX_PSC_CAL},
+       {0x2090, TB_ADDR_TX_PSC_RDY},
+
+       {0xA6FD, TB_ADDR_RX_PSC_A0},
+       {0xA6FD, TB_ADDR_RX_PSC_A1},
+       {0xA410, TB_ADDR_RX_PSC_A2},
+       {0x2410, TB_ADDR_RX_PSC_A3},
+
+       {0x23FF, TB_ADDR_RX_PSC_CAL},
+       {0x2010, TB_ADDR_RX_PSC_RDY},
+
+       {0x0020, TB_ADDR_TX_TXCC_MGNLS_MULT_000},
+       {0x00ff, TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY},
+       {0x0002, TB_ADDR_RX_SLC_CU_ITER_TMR},
+       {0x0013, TB_ADDR_RX_SIGDET_HL_FILT_TMR},
+       {0x0000, TB_ADDR_RX_SAMP_DAC_CTRL},
+       {0x1004, TB_ADDR_RX_DIAG_SIGDET_TUNE},
+       {0x4041, TB_ADDR_RX_DIAG_LFPSDET_TUNE2},
+       {0x0480, TB_ADDR_RX_DIAG_BS_TM},
+       {0x8006, TB_ADDR_RX_DIAG_DFE_CTRL1},
+       {0x003f, TB_ADDR_RX_DIAG_ILL_IQE_TRIM4},
+       {0x543f, TB_ADDR_RX_DIAG_ILL_E_TRIM0},
+       {0x543f, TB_ADDR_RX_DIAG_ILL_IQ_TRIM0},
+       {0x0000, TB_ADDR_RX_DIAG_ILL_IQE_TRIM6},
+       {0x8000, TB_ADDR_RX_DIAG_RXFE_TM3},
+       {0x0003, TB_ADDR_RX_DIAG_RXFE_TM4},
+       {0x2408, TB_ADDR_RX_DIAG_LFPSDET_TUNE},
+       {0x05ca, TB_ADDR_RX_DIAG_DFE_CTRL3},
+       {0x0258, TB_ADDR_RX_DIAG_SC2C_DELAY},
+       {0x1fff, TB_ADDR_RX_REE_VGA_GAIN_NODFE},
+
+       {0x02c6, TB_ADDR_XCVR_PSM_CAL_TMR},
+       {0x0002, TB_ADDR_XCVR_PSM_A0BYP_TMR},
+       {0x02c6, TB_ADDR_XCVR_PSM_A0IN_TMR},
+       {0x0010, TB_ADDR_XCVR_PSM_A1IN_TMR},
+       {0x0010, TB_ADDR_XCVR_PSM_A2IN_TMR},
+       {0x0010, TB_ADDR_XCVR_PSM_A3IN_TMR},
+       {0x0010, TB_ADDR_XCVR_PSM_A4IN_TMR},
+       {0x0010, TB_ADDR_XCVR_PSM_A5IN_TMR},
+
+       {0x0002, TB_ADDR_XCVR_PSM_A0OUT_TMR},
+       {0x0002, TB_ADDR_XCVR_PSM_A1OUT_TMR},
+       {0x0002, TB_ADDR_XCVR_PSM_A2OUT_TMR},
+       {0x0002, TB_ADDR_XCVR_PSM_A3OUT_TMR},
+       {0x0002, TB_ADDR_XCVR_PSM_A4OUT_TMR},
+       {0x0002, TB_ADDR_XCVR_PSM_A5OUT_TMR},
+       /* Change rx detect parameter */
+       {0x0960, TB_ADDR_TX_RCVDET_EN_TMR},
+       {0x01e0, TB_ADDR_TX_RCVDET_ST_TMR},
+       {0x0090, TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR},
+};
+
+static int cdns_salvo_phy_init(struct phy *phy)
+{
+       struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
+       struct cdns_salvo_data *data = salvo_phy->data;
+       int ret, i;
+       u16 value;
+
+       ret = clk_prepare_enable(salvo_phy->clk);
+       if (ret)
+               return ret;
+
+       for (i = 0; i < data->init_sequence_length; i++) {
+               struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i;
+
+               cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val);
+       }
+
+       /* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */
+       value = cdns_salvo_read(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL);
+       value |= RXDET_IN_P3_32KHZ;
+       cdns_salvo_write(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL,
+                        RXDET_IN_P3_32KHZ);
+
+       udelay(10);
+
+       clk_disable_unprepare(salvo_phy->clk);
+
+       return ret;
+}
+
+static int cdns_salvo_phy_power_on(struct phy *phy)
+{
+       struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
+
+       return clk_prepare_enable(salvo_phy->clk);
+}
+
+static int cdns_salvo_phy_power_off(struct phy *phy)
+{
+       struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy);
+
+       clk_disable_unprepare(salvo_phy->clk);
+
+       return 0;
+}
+
+static struct phy_ops cdns_salvo_phy_ops = {
+       .init           = cdns_salvo_phy_init,
+       .power_on       = cdns_salvo_phy_power_on,
+       .power_off      = cdns_salvo_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int cdns_salvo_phy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct device *dev = &pdev->dev;
+       struct cdns_salvo_phy *salvo_phy;
+       struct resource *res;
+       const struct of_device_id *match;
+       struct cdns_salvo_data *data;
+
+       match = of_match_device(cdns_salvo_phy_of_match, dev);
+       if (!match)
+               return -EINVAL;
+
+       data = (struct cdns_salvo_data *)match->data;
+       salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL);
+       if (!salvo_phy)
+               return -ENOMEM;
+
+       salvo_phy->data = data;
+       salvo_phy->clk = devm_clk_get_optional(dev, "salvo_phy_clk");
+       if (IS_ERR(salvo_phy->clk))
+               return PTR_ERR(salvo_phy->clk);
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       salvo_phy->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(salvo_phy->base))
+               return PTR_ERR(salvo_phy->base);
+
+       salvo_phy->phy = devm_phy_create(dev, NULL, &cdns_salvo_phy_ops);
+       if (IS_ERR(salvo_phy->phy))
+               return PTR_ERR(salvo_phy->phy);
+
+       phy_set_drvdata(salvo_phy->phy, salvo_phy);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct cdns_salvo_data cdns_nxp_salvo_data = {
+       2,
+       cdns_nxp_sequence_pair,
+       ARRAY_SIZE(cdns_nxp_sequence_pair),
+};
+
+static const struct of_device_id cdns_salvo_phy_of_match[] = {
+       {
+               .compatible = "nxp,salvo-phy",
+               .data = &cdns_nxp_salvo_data,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, cdns_salvo_phy_of_match);
+
+static struct platform_driver cdns_salvo_phy_driver = {
+       .probe  = cdns_salvo_phy_probe,
+       .driver = {
+               .name   = "cdns-salvo-phy",
+               .of_match_table = cdns_salvo_phy_of_match,
+       }
+};
+module_platform_driver(cdns_salvo_phy_driver);
+
+MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence SALVO PHY Driver");
index a5c08e5..faed652 100644 (file)
@@ -685,10 +685,10 @@ static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
 static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
        {0xFE0A, SIERRA_DET_STANDEC_A_PREG},
        {0x000F, SIERRA_DET_STANDEC_B_PREG},
-       {0x00A5, SIERRA_DET_STANDEC_C_PREG},
+       {0x55A5, SIERRA_DET_STANDEC_C_PREG},
        {0x69ad, SIERRA_DET_STANDEC_D_PREG},
        {0x0241, SIERRA_DET_STANDEC_E_PREG},
-       {0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG},
+       {0x0110, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG},
        {0x0014, SIERRA_PSM_A0IN_TMR_PREG},
        {0xCF00, SIERRA_PSM_DIAG_PREG},
        {0x001F, SIERRA_PSC_TX_A0_PREG},
@@ -696,7 +696,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
        {0x0003, SIERRA_PSC_TX_A2_PREG},
        {0x0003, SIERRA_PSC_TX_A3_PREG},
        {0x0FFF, SIERRA_PSC_RX_A0_PREG},
-       {0x0619, SIERRA_PSC_RX_A1_PREG},
+       {0x0003, SIERRA_PSC_RX_A1_PREG},
        {0x0003, SIERRA_PSC_RX_A2_PREG},
        {0x0001, SIERRA_PSC_RX_A3_PREG},
        {0x0001, SIERRA_PLLCTRL_SUBRATE_PREG},
@@ -705,19 +705,19 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
        {0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG},
        {0x2512, SIERRA_DFE_BIASTRIM_PREG},
        {0x0000, SIERRA_DRVCTRL_ATTEN_PREG},
-       {0x873E, SIERRA_CLKPATHCTRL_TMR_PREG},
-       {0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
-       {0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+       {0x823E, SIERRA_CLKPATHCTRL_TMR_PREG},
+       {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+       {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
        {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG},
-       {0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+       {0x023C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
        {0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG},
        {0x0000, SIERRA_CREQ_EQ_CTRL_PREG},
-       {0x8000, SIERRA_CREQ_SPARE_PREG},
+       {0x0000, SIERRA_CREQ_SPARE_PREG},
        {0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
-       {0x8453, SIERRA_CTLELUT_CTRL_PREG},
-       {0x4110, SIERRA_DFE_ECMP_RATESEL_PREG},
-       {0x4110, SIERRA_DFE_SMP_RATESEL_PREG},
-       {0x0002, SIERRA_DEQ_PHALIGN_CTRL},
+       {0x8452, SIERRA_CTLELUT_CTRL_PREG},
+       {0x4121, SIERRA_DFE_ECMP_RATESEL_PREG},
+       {0x4121, SIERRA_DFE_SMP_RATESEL_PREG},
+       {0x0003, SIERRA_DEQ_PHALIGN_CTRL},
        {0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG},
        {0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG},
        {0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
@@ -725,7 +725,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
        {0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG},
        {0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG},
        {0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG},
-       {0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG},
+       {0x9999, SIERRA_DEQ_VGATUNE_CTRL_PREG},
        {0x0014, SIERRA_DEQ_GLUT0},
        {0x0014, SIERRA_DEQ_GLUT1},
        {0x0014, SIERRA_DEQ_GLUT2},
@@ -772,6 +772,7 @@ static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
        {0x000F, SIERRA_LFPSFILT_NS_PREG},
        {0x0009, SIERRA_LFPSFILT_RD_PREG},
        {0x0001, SIERRA_LFPSFILT_MP_PREG},
+       {0x6013, SIERRA_SIGDET_SUPPORT_PREG},
        {0x8013, SIERRA_SDFILT_H2L_A_PREG},
        {0x8009, SIERRA_SDFILT_L2H_PREG},
        {0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG},
index 4ea6a88..7b47682 100644 (file)
@@ -2,8 +2,23 @@
 #
 # Phy drivers for Intel Lightning Mountain(LGM) platform
 #
+config PHY_INTEL_COMBO
+       bool "Intel ComboPHY driver"
+       depends on X86 || COMPILE_TEST
+       depends on OF && HAS_IOMEM
+       select MFD_SYSCON
+       select GENERIC_PHY
+       select REGMAP
+       help
+         Enable this to support Intel ComboPhy.
+
+         This driver configures ComboPhy subsystem on Intel gateway
+         chipsets which provides PHYs for various controllers, EMAC,
+         SATA and PCIe.
+
 config PHY_INTEL_EMMC
        tristate "Intel EMMC PHY driver"
+       depends on X86 || COMPILE_TEST
        select GENERIC_PHY
        help
          Enable this to support the Intel EMMC PHY
index 6b876a7..233d530 100644 (file)
@@ -1,2 +1,3 @@
 # SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_INTEL_COMBO)          += phy-intel-combo.o
 obj-$(CONFIG_PHY_INTEL_EMMC)            += phy-intel-emmc.o
diff --git a/drivers/phy/intel/phy-intel-combo.c b/drivers/phy/intel/phy-intel-combo.c
new file mode 100644 (file)
index 0000000..c2a35be
--- /dev/null
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel Combo-PHY driver
+ *
+ * Copyright (C) 2019-2020 Intel Corporation.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <dt-bindings/phy/phy.h>
+
+#define PCIE_PHY_GEN_CTRL      0x00
+#define PCIE_PHY_CLK_PAD       BIT(17)
+
+#define PAD_DIS_CFG            0x174
+
+#define PCS_XF_ATE_OVRD_IN_2   0x3008
+#define ADAPT_REQ_MSK          GENMASK(5, 4)
+
+#define PCS_XF_RX_ADAPT_ACK    0x3010
+#define RX_ADAPT_ACK_BIT       BIT(0)
+
+#define CR_ADDR(addr, lane)    (((addr) + (lane) * 0x100) << 2)
+#define REG_COMBO_MODE(x)      ((x) * 0x200)
+#define REG_CLK_DISABLE(x)     ((x) * 0x200 + 0x124)
+
+#define COMBO_PHY_ID(x)                ((x)->parent->id)
+#define PHY_ID(x)              ((x)->id)
+
+#define CLK_100MHZ             100000000
+#define CLK_156_25MHZ          156250000
+
+static const unsigned long intel_iphy_clk_rates[] = {
+       CLK_100MHZ, CLK_156_25MHZ, CLK_100MHZ,
+};
+
+enum {
+       PHY_0,
+       PHY_1,
+       PHY_MAX_NUM
+};
+
+/*
+ * Clock Register bit fields to enable clocks
+ * for ComboPhy according to the mode.
+ */
+enum intel_phy_mode {
+       PHY_PCIE_MODE = 0,
+       PHY_XPCS_MODE,
+       PHY_SATA_MODE,
+};
+
+/* ComboPhy mode Register values */
+enum intel_combo_mode {
+       PCIE0_PCIE1_MODE = 0,
+       PCIE_DL_MODE,
+       RXAUI_MODE,
+       XPCS0_XPCS1_MODE,
+       SATA0_SATA1_MODE,
+};
+
+enum aggregated_mode {
+       PHY_SL_MODE,
+       PHY_DL_MODE,
+};
+
+struct intel_combo_phy;
+
+struct intel_cbphy_iphy {
+       struct phy              *phy;
+       struct intel_combo_phy  *parent;
+       struct reset_control    *app_rst;
+       u32                     id;
+};
+
+struct intel_combo_phy {
+       struct device           *dev;
+       struct clk              *core_clk;
+       unsigned long           clk_rate;
+       void __iomem            *app_base;
+       void __iomem            *cr_base;
+       struct regmap           *syscfg;
+       struct regmap           *hsiocfg;
+       u32                     id;
+       u32                     bid;
+       struct reset_control    *phy_rst;
+       struct reset_control    *core_rst;
+       struct intel_cbphy_iphy iphy[PHY_MAX_NUM];
+       enum intel_phy_mode     phy_mode;
+       enum aggregated_mode    aggr_mode;
+       u32                     init_cnt;
+       struct mutex            lock;
+};
+
+static int intel_cbphy_iphy_enable(struct intel_cbphy_iphy *iphy, bool set)
+{
+       struct intel_combo_phy *cbphy = iphy->parent;
+       u32 mask = BIT(cbphy->phy_mode * 2 + iphy->id);
+       u32 val;
+
+       /* Register: 0 is enable, 1 is disable */
+       val = set ? 0 : mask;
+
+       return regmap_update_bits(cbphy->hsiocfg, REG_CLK_DISABLE(cbphy->bid),
+                                 mask, val);
+}
+
+static int intel_cbphy_pcie_refclk_cfg(struct intel_cbphy_iphy *iphy, bool set)
+{
+       struct intel_combo_phy *cbphy = iphy->parent;
+       u32 mask = BIT(cbphy->id * 2 + iphy->id);
+       u32 val;
+
+       /* Register: 0 is enable, 1 is disable */
+       val = set ? 0 : mask;
+
+       return regmap_update_bits(cbphy->syscfg, PAD_DIS_CFG, mask, val);
+}
+
+static inline void combo_phy_w32_off_mask(void __iomem *base, unsigned int reg,
+                                         u32 mask, u32 val)
+{
+       u32 reg_val;
+
+       reg_val = readl(base + reg);
+       reg_val &= ~mask;
+       reg_val |= FIELD_PREP(mask, val);
+       writel(reg_val, base + reg);
+}
+
+static int intel_cbphy_iphy_cfg(struct intel_cbphy_iphy *iphy,
+                               int (*phy_cfg)(struct intel_cbphy_iphy *))
+{
+       struct intel_combo_phy *cbphy = iphy->parent;
+       int ret;
+
+       ret = phy_cfg(iphy);
+       if (ret)
+               return ret;
+
+       if (cbphy->aggr_mode != PHY_DL_MODE)
+               return 0;
+
+       return phy_cfg(&cbphy->iphy[PHY_1]);
+}
+
+static int intel_cbphy_pcie_en_pad_refclk(struct intel_cbphy_iphy *iphy)
+{
+       struct intel_combo_phy *cbphy = iphy->parent;
+       int ret;
+
+       ret = intel_cbphy_pcie_refclk_cfg(iphy, true);
+       if (ret) {
+               dev_err(cbphy->dev, "Failed to enable PCIe pad refclk\n");
+               return ret;
+       }
+
+       if (cbphy->init_cnt)
+               return 0;
+
+       combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
+                              PCIE_PHY_CLK_PAD, 0);
+
+       /* Delay for stable clock PLL */
+       usleep_range(50, 100);
+
+       return 0;
+}
+
+static int intel_cbphy_pcie_dis_pad_refclk(struct intel_cbphy_iphy *iphy)
+{
+       struct intel_combo_phy *cbphy = iphy->parent;
+       int ret;
+
+       ret = intel_cbphy_pcie_refclk_cfg(iphy, false);
+       if (ret) {
+               dev_err(cbphy->dev, "Failed to disable PCIe pad refclk\n");
+               return ret;
+       }
+
+       if (cbphy->init_cnt)
+               return 0;
+
+       combo_phy_w32_off_mask(cbphy->app_base, PCIE_PHY_GEN_CTRL,
+                              PCIE_PHY_CLK_PAD, 1);
+
+       return 0;
+}
+
+static int intel_cbphy_set_mode(struct intel_combo_phy *cbphy)
+{
+       enum intel_combo_mode cb_mode = PHY_PCIE_MODE;
+       enum aggregated_mode aggr = cbphy->aggr_mode;
+       struct device *dev = cbphy->dev;
+       enum intel_phy_mode mode;
+       int ret;
+
+       mode = cbphy->phy_mode;
+
+       switch (mode) {
+       case PHY_PCIE_MODE:
+               cb_mode = (aggr == PHY_DL_MODE) ? PCIE_DL_MODE : PCIE0_PCIE1_MODE;
+               break;
+
+       case PHY_XPCS_MODE:
+               cb_mode = (aggr == PHY_DL_MODE) ? RXAUI_MODE : XPCS0_XPCS1_MODE;
+               break;
+
+       case PHY_SATA_MODE:
+               if (aggr == PHY_DL_MODE) {
+                       dev_err(dev, "Mode:%u not support dual lane!\n", mode);
+                       return -EINVAL;
+               }
+
+               cb_mode = SATA0_SATA1_MODE;
+               break;
+       }
+
+       ret = regmap_write(cbphy->hsiocfg, REG_COMBO_MODE(cbphy->bid), cb_mode);
+       if (ret)
+               dev_err(dev, "Failed to set ComboPhy mode: %d\n", ret);
+
+       return ret;
+}
+
+static void intel_cbphy_rst_assert(struct intel_combo_phy *cbphy)
+{
+       reset_control_assert(cbphy->core_rst);
+       reset_control_assert(cbphy->phy_rst);
+}
+
+static void intel_cbphy_rst_deassert(struct intel_combo_phy *cbphy)
+{
+       reset_control_deassert(cbphy->core_rst);
+       reset_control_deassert(cbphy->phy_rst);
+       /* Delay to ensure reset process is done */
+       usleep_range(10, 20);
+}
+
+static int intel_cbphy_iphy_power_on(struct intel_cbphy_iphy *iphy)
+{
+       struct intel_combo_phy *cbphy = iphy->parent;
+       int ret;
+
+       if (!cbphy->init_cnt) {
+               ret = clk_prepare_enable(cbphy->core_clk);
+               if (ret) {
+                       dev_err(cbphy->dev, "Clock enable failed!\n");
+                       return ret;
+               }
+
+               ret = clk_set_rate(cbphy->core_clk, cbphy->clk_rate);
+               if (ret) {
+                       dev_err(cbphy->dev, "Clock freq set to %lu failed!\n",
+                               cbphy->clk_rate);
+                       goto clk_err;
+               }
+
+               intel_cbphy_rst_assert(cbphy);
+               intel_cbphy_rst_deassert(cbphy);
+               ret = intel_cbphy_set_mode(cbphy);
+               if (ret)
+                       goto clk_err;
+       }
+
+       ret = intel_cbphy_iphy_enable(iphy, true);
+       if (ret) {
+               dev_err(cbphy->dev, "Failed enabling PHY core\n");
+               goto clk_err;
+       }
+
+       ret = reset_control_deassert(iphy->app_rst);
+       if (ret) {
+               dev_err(cbphy->dev, "PHY(%u:%u) reset deassert failed!\n",
+                       COMBO_PHY_ID(iphy), PHY_ID(iphy));
+               goto clk_err;
+       }
+
+       /* Delay to ensure reset process is done */
+       udelay(1);
+
+       return 0;
+
+clk_err:
+       clk_disable_unprepare(cbphy->core_clk);
+
+       return ret;
+}
+
+static int intel_cbphy_iphy_power_off(struct intel_cbphy_iphy *iphy)
+{
+       struct intel_combo_phy *cbphy = iphy->parent;
+       int ret;
+
+       ret = reset_control_assert(iphy->app_rst);
+       if (ret) {
+               dev_err(cbphy->dev, "PHY(%u:%u) reset assert failed!\n",
+                       COMBO_PHY_ID(iphy), PHY_ID(iphy));
+               return ret;
+       }
+
+       ret = intel_cbphy_iphy_enable(iphy, false);
+       if (ret) {
+               dev_err(cbphy->dev, "Failed disabling PHY core\n");
+               return ret;
+       }
+
+       if (cbphy->init_cnt)
+               return 0;
+
+       clk_disable_unprepare(cbphy->core_clk);
+       intel_cbphy_rst_assert(cbphy);
+
+       return 0;
+}
+
+static int intel_cbphy_init(struct phy *phy)
+{
+       struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
+       struct intel_combo_phy *cbphy = iphy->parent;
+       int ret;
+
+       mutex_lock(&cbphy->lock);
+       ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_on);
+       if (ret)
+               goto err;
+
+       if (cbphy->phy_mode == PHY_PCIE_MODE) {
+               ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_en_pad_refclk);
+               if (ret)
+                       goto err;
+       }
+
+       cbphy->init_cnt++;
+
+err:
+       mutex_unlock(&cbphy->lock);
+
+       return ret;
+}
+
+static int intel_cbphy_exit(struct phy *phy)
+{
+       struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
+       struct intel_combo_phy *cbphy = iphy->parent;
+       int ret;
+
+       mutex_lock(&cbphy->lock);
+       cbphy->init_cnt--;
+       if (cbphy->phy_mode == PHY_PCIE_MODE) {
+               ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_pcie_dis_pad_refclk);
+               if (ret)
+                       goto err;
+       }
+
+       ret = intel_cbphy_iphy_cfg(iphy, intel_cbphy_iphy_power_off);
+
+err:
+       mutex_unlock(&cbphy->lock);
+
+       return ret;
+}
+
+static int intel_cbphy_calibrate(struct phy *phy)
+{
+       struct intel_cbphy_iphy *iphy = phy_get_drvdata(phy);
+       struct intel_combo_phy *cbphy = iphy->parent;
+       void __iomem *cr_base = cbphy->cr_base;
+       int val, ret, id;
+
+       if (cbphy->phy_mode != PHY_XPCS_MODE)
+               return 0;
+
+       id = PHY_ID(iphy);
+
+       /* trigger auto RX adaptation */
+       combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
+                              ADAPT_REQ_MSK, 3);
+       /* Wait RX adaptation to finish */
+       ret = readl_poll_timeout(cr_base + CR_ADDR(PCS_XF_RX_ADAPT_ACK, id),
+                                val, val & RX_ADAPT_ACK_BIT, 10, 5000);
+       if (ret)
+               dev_err(cbphy->dev, "RX Adaptation failed!\n");
+       else
+               dev_dbg(cbphy->dev, "RX Adaptation success!\n");
+
+       /* Stop RX adaptation */
+       combo_phy_w32_off_mask(cr_base, CR_ADDR(PCS_XF_ATE_OVRD_IN_2, id),
+                              ADAPT_REQ_MSK, 0);
+
+       return ret;
+}
+
+static int intel_cbphy_fwnode_parse(struct intel_combo_phy *cbphy)
+{
+       struct device *dev = cbphy->dev;
+       struct platform_device *pdev = to_platform_device(dev);
+       struct fwnode_handle *fwnode = dev_fwnode(dev);
+       struct fwnode_reference_args ref;
+       int ret;
+       u32 val;
+
+       cbphy->core_clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(cbphy->core_clk)) {
+               ret = PTR_ERR(cbphy->core_clk);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Get clk failed:%d!\n", ret);
+               return ret;
+       }
+
+       cbphy->core_rst = devm_reset_control_get_optional(dev, "core");
+       if (IS_ERR(cbphy->core_rst)) {
+               ret = PTR_ERR(cbphy->core_rst);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Get core reset control err: %d!\n", ret);
+               return ret;
+       }
+
+       cbphy->phy_rst = devm_reset_control_get_optional(dev, "phy");
+       if (IS_ERR(cbphy->phy_rst)) {
+               ret = PTR_ERR(cbphy->phy_rst);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Get PHY reset control err: %d!\n", ret);
+               return ret;
+       }
+
+       cbphy->iphy[0].app_rst = devm_reset_control_get_optional(dev, "iphy0");
+       if (IS_ERR(cbphy->iphy[0].app_rst)) {
+               ret = PTR_ERR(cbphy->iphy[0].app_rst);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Get phy0 reset control err: %d!\n", ret);
+               return ret;
+       }
+
+       cbphy->iphy[1].app_rst = devm_reset_control_get_optional(dev, "iphy1");
+       if (IS_ERR(cbphy->iphy[1].app_rst)) {
+               ret = PTR_ERR(cbphy->iphy[1].app_rst);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Get phy1 reset control err: %d!\n", ret);
+               return ret;
+       }
+
+       cbphy->app_base = devm_platform_ioremap_resource_byname(pdev, "app");
+       if (IS_ERR(cbphy->app_base))
+               return PTR_ERR(cbphy->app_base);
+
+       cbphy->cr_base = devm_platform_ioremap_resource_byname(pdev, "core");
+       if (IS_ERR(cbphy->cr_base))
+               return PTR_ERR(cbphy->cr_base);
+
+       /*
+        * syscfg and hsiocfg variables stores the handle of the registers set
+        * in which ComboPhy subsytem specific registers are subset. Using
+        * Register map framework to access the registers set.
+        */
+       ret = fwnode_property_get_reference_args(fwnode, "intel,syscfg", NULL,
+                                                1, 0, &ref);
+       if (ret < 0)
+               return ret;
+
+       cbphy->id = ref.args[0];
+       cbphy->syscfg = device_node_to_regmap(to_of_node(ref.fwnode));
+       fwnode_handle_put(ref.fwnode);
+
+       ret = fwnode_property_get_reference_args(fwnode, "intel,hsio", NULL, 1,
+                                                0, &ref);
+       if (ret < 0)
+               return ret;
+
+       cbphy->bid = ref.args[0];
+       cbphy->hsiocfg = device_node_to_regmap(to_of_node(ref.fwnode));
+       fwnode_handle_put(ref.fwnode);
+
+       ret = fwnode_property_read_u32_array(fwnode, "intel,phy-mode", &val, 1);
+       if (ret)
+               return ret;
+
+       switch (val) {
+       case PHY_TYPE_PCIE:
+               cbphy->phy_mode = PHY_PCIE_MODE;
+               break;
+
+       case PHY_TYPE_SATA:
+               cbphy->phy_mode = PHY_SATA_MODE;
+               break;
+
+       case PHY_TYPE_XPCS:
+               cbphy->phy_mode = PHY_XPCS_MODE;
+               break;
+
+       default:
+               dev_err(dev, "Invalid PHY mode: %u\n", val);
+               return -EINVAL;
+       }
+
+       cbphy->clk_rate = intel_iphy_clk_rates[cbphy->phy_mode];
+
+       if (fwnode_property_present(fwnode, "intel,aggregation"))
+               cbphy->aggr_mode = PHY_DL_MODE;
+       else
+               cbphy->aggr_mode = PHY_SL_MODE;
+
+       return 0;
+}
+
+static const struct phy_ops intel_cbphy_ops = {
+       .init           = intel_cbphy_init,
+       .exit           = intel_cbphy_exit,
+       .calibrate      = intel_cbphy_calibrate,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *intel_cbphy_xlate(struct device *dev,
+                                    struct of_phandle_args *args)
+{
+       struct intel_combo_phy *cbphy = dev_get_drvdata(dev);
+       u32 iphy_id;
+
+       if (args->args_count < 1) {
+               dev_err(dev, "Invalid number of arguments\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       iphy_id = args->args[0];
+       if (iphy_id >= PHY_MAX_NUM) {
+               dev_err(dev, "Invalid phy instance %d\n", iphy_id);
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (cbphy->aggr_mode == PHY_DL_MODE && iphy_id == PHY_1) {
+               dev_err(dev, "Invalid. ComboPhy is in Dual lane mode %d\n", iphy_id);
+               return ERR_PTR(-EINVAL);
+       }
+
+       return cbphy->iphy[iphy_id].phy;
+}
+
+static int intel_cbphy_create(struct intel_combo_phy *cbphy)
+{
+       struct phy_provider *phy_provider;
+       struct device *dev = cbphy->dev;
+       struct intel_cbphy_iphy *iphy;
+       int i;
+
+       for (i = 0; i < PHY_MAX_NUM; i++) {
+               iphy = &cbphy->iphy[i];
+               iphy->parent = cbphy;
+               iphy->id = i;
+
+               /* In dual lane mode skip phy creation for the second phy */
+               if (cbphy->aggr_mode == PHY_DL_MODE && iphy->id == PHY_1)
+                       continue;
+
+               iphy->phy = devm_phy_create(dev, NULL, &intel_cbphy_ops);
+               if (IS_ERR(iphy->phy)) {
+                       dev_err(dev, "PHY[%u:%u]: create PHY instance failed!\n",
+                               COMBO_PHY_ID(iphy), PHY_ID(iphy));
+
+                       return PTR_ERR(iphy->phy);
+               }
+
+               phy_set_drvdata(iphy->phy, iphy);
+       }
+
+       dev_set_drvdata(dev, cbphy);
+       phy_provider = devm_of_phy_provider_register(dev, intel_cbphy_xlate);
+       if (IS_ERR(phy_provider))
+               dev_err(dev, "Register PHY provider failed!\n");
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static int intel_cbphy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct intel_combo_phy *cbphy;
+       int ret;
+
+       cbphy = devm_kzalloc(dev, sizeof(*cbphy), GFP_KERNEL);
+       if (!cbphy)
+               return -ENOMEM;
+
+       cbphy->dev = dev;
+       cbphy->init_cnt = 0;
+       mutex_init(&cbphy->lock);
+       ret = intel_cbphy_fwnode_parse(cbphy);
+       if (ret)
+               return ret;
+
+       platform_set_drvdata(pdev, cbphy);
+
+       return intel_cbphy_create(cbphy);
+}
+
+static int intel_cbphy_remove(struct platform_device *pdev)
+{
+       struct intel_combo_phy *cbphy = platform_get_drvdata(pdev);
+
+       intel_cbphy_rst_assert(cbphy);
+       clk_disable_unprepare(cbphy->core_clk);
+       return 0;
+}
+
+static const struct of_device_id of_intel_cbphy_match[] = {
+       { .compatible = "intel,combo-phy" },
+       { .compatible = "intel,combophy-lgm" },
+       {}
+};
+
+static struct platform_driver intel_cbphy_driver = {
+       .probe = intel_cbphy_probe,
+       .remove = intel_cbphy_remove,
+       .driver = {
+               .name = "intel-combo-phy",
+               .of_match_table = of_intel_cbphy_match,
+       }
+};
+
+module_platform_driver(intel_cbphy_driver);
+
+MODULE_DESCRIPTION("Intel Combo-phy driver");
+MODULE_LICENSE("GPL v2");
index 12e71a3..089db0d 100644 (file)
@@ -122,7 +122,6 @@ enum cpcap_gpio_mode {
 struct cpcap_phy_ddata {
        struct regmap *reg;
        struct device *dev;
-       struct clk *refclk;
        struct usb_phy phy;
        struct delayed_work detect_work;
        struct pinctrl *pins;
@@ -707,7 +706,6 @@ static int cpcap_usb_phy_remove(struct platform_device *pdev)
 
        usb_remove_phy(&ddata->phy);
        cancel_delayed_work_sync(&ddata->detect_work);
-       clk_unprepare(ddata->refclk);
        regulator_disable(ddata->vusb);
 
        return 0;
index 98674ed..ca9ce7e 100644 (file)
@@ -18,6 +18,13 @@ config PHY_QCOM_APQ8064_SATA
        depends on OF
        select GENERIC_PHY
 
+config PHY_QCOM_IPQ4019_USB
+       tristate "Qualcomm IPQ4019 USB PHY driver"
+       depends on OF && (ARCH_QCOM || COMPILE_TEST)
+       select GENERIC_PHY
+       help
+         Support for the USB PHY-s on Qualcomm IPQ40xx SoC-s.
+
 config PHY_QCOM_IPQ806X_SATA
        tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
        depends on ARCH_QCOM
@@ -85,6 +92,16 @@ config PHY_QCOM_USB_HS
          Support for the USB high-speed ULPI compliant phy on Qualcomm
          chipsets.
 
+config PHY_QCOM_USB_SNPS_FEMTO_V2
+       tristate "Qualcomm SNPS FEMTO USB HS PHY V2 module"
+       depends on OF && (ARCH_QCOM || COMPILE_TEST)
+       select GENERIC_PHY
+       help
+         Enable support for the USB high-speed SNPS Femto phy on Qualcomm
+         chipsets.  This PHY has differences in the register map compared
+         to the V1 variants.  The PHY is paired with a Synopsys DWC3 USB
+         controller on Qualcomm SOCs.
+
 config PHY_QCOM_USB_HSIC
        tristate "Qualcomm USB HSIC ULPI PHY module"
        depends on USB_ULPI_BUS
index 1f14aea..86fb32e 100644 (file)
@@ -1,6 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0
 obj-$(CONFIG_PHY_ATH79_USB)            += phy-ath79-usb.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)    += phy-qcom-apq8064-sata.o
+obj-$(CONFIG_PHY_QCOM_IPQ4019_USB)     += phy-qcom-ipq4019-usb.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)    += phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_QCOM_PCIE2)           += phy-qcom-pcie2.o
 obj-$(CONFIG_PHY_QCOM_QMP)             += phy-qcom-qmp.o
@@ -12,3 +13,4 @@ obj-$(CONFIG_PHY_QCOM_USB_HS)                 += phy-qcom-usb-hs.o
 obj-$(CONFIG_PHY_QCOM_USB_HSIC)        += phy-qcom-usb-hsic.o
 obj-$(CONFIG_PHY_QCOM_USB_HS_28NM)     += phy-qcom-usb-hs-28nm.o
 obj-$(CONFIG_PHY_QCOM_USB_SS)          += phy-qcom-usb-ss.o
+obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o
diff --git a/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq4019-usb.c
new file mode 100644 (file)
index 0000000..b8ef331
--- /dev/null
@@ -0,0 +1,148 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2018 John Crispin <john@phrozen.org>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of_platform.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+struct ipq4019_usb_phy {
+       struct device           *dev;
+       struct phy              *phy;
+       void __iomem            *base;
+       struct reset_control    *por_rst;
+       struct reset_control    *srif_rst;
+};
+
+static int ipq4019_ss_phy_power_off(struct phy *_phy)
+{
+       struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+       reset_control_assert(phy->por_rst);
+       msleep(10);
+
+       return 0;
+}
+
+static int ipq4019_ss_phy_power_on(struct phy *_phy)
+{
+       struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+       ipq4019_ss_phy_power_off(_phy);
+
+       reset_control_deassert(phy->por_rst);
+
+       return 0;
+}
+
+static struct phy_ops ipq4019_usb_ss_phy_ops = {
+       .power_on       = ipq4019_ss_phy_power_on,
+       .power_off      = ipq4019_ss_phy_power_off,
+};
+
+static int ipq4019_hs_phy_power_off(struct phy *_phy)
+{
+       struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+       reset_control_assert(phy->por_rst);
+       msleep(10);
+
+       reset_control_assert(phy->srif_rst);
+       msleep(10);
+
+       return 0;
+}
+
+static int ipq4019_hs_phy_power_on(struct phy *_phy)
+{
+       struct ipq4019_usb_phy *phy = phy_get_drvdata(_phy);
+
+       ipq4019_hs_phy_power_off(_phy);
+
+       reset_control_deassert(phy->srif_rst);
+       msleep(10);
+
+       reset_control_deassert(phy->por_rst);
+
+       return 0;
+}
+
+static struct phy_ops ipq4019_usb_hs_phy_ops = {
+       .power_on       = ipq4019_hs_phy_power_on,
+       .power_off      = ipq4019_hs_phy_power_off,
+};
+
+static const struct of_device_id ipq4019_usb_phy_of_match[] = {
+       { .compatible = "qcom,usb-hs-ipq4019-phy", .data = &ipq4019_usb_hs_phy_ops},
+       { .compatible = "qcom,usb-ss-ipq4019-phy", .data = &ipq4019_usb_ss_phy_ops},
+       { },
+};
+MODULE_DEVICE_TABLE(of, ipq4019_usb_phy_of_match);
+
+static int ipq4019_usb_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct ipq4019_usb_phy *phy;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       phy->dev = &pdev->dev;
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(phy->base)) {
+               dev_err(dev, "failed to remap register memory\n");
+               return PTR_ERR(phy->base);
+       }
+
+       phy->por_rst = devm_reset_control_get(phy->dev, "por_rst");
+       if (IS_ERR(phy->por_rst)) {
+               if (PTR_ERR(phy->por_rst) != -EPROBE_DEFER)
+                       dev_err(dev, "POR reset is missing\n");
+               return PTR_ERR(phy->por_rst);
+       }
+
+       phy->srif_rst = devm_reset_control_get_optional(phy->dev, "srif_rst");
+       if (IS_ERR(phy->srif_rst))
+               return PTR_ERR(phy->srif_rst);
+
+       phy->phy = devm_phy_create(dev, NULL, of_device_get_match_data(dev));
+       if (IS_ERR(phy->phy)) {
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(phy->phy);
+       }
+       phy_set_drvdata(phy->phy, phy);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver ipq4019_usb_phy_driver = {
+       .probe  = ipq4019_usb_phy_probe,
+       .driver = {
+               .of_match_table = ipq4019_usb_phy_of_match,
+               .name  = "ipq4019-usb-phy",
+       }
+};
+module_platform_driver(ipq4019_usb_phy_driver);
+
+MODULE_DESCRIPTION("QCOM/IPQ4019 USB phy driver");
+MODULE_AUTHOR("John Crispin <john@phrozen.org>");
+MODULE_LICENSE("GPL v2");
index c190406..e91040a 100644 (file)
@@ -119,14 +119,17 @@ enum qphy_reg_layout {
        QPHY_PCS_AUTONOMOUS_MODE_CTRL,
        QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR,
        QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
+       QPHY_PCS_POWER_DOWN_CONTROL,
+       /* Keep last to ensure regs_layout arrays are properly initialized */
+       QPHY_LAYOUT_SIZE
 };
 
-static const unsigned int msm8996_ufsphy_regs_layout[] = {
+static const unsigned int msm8996_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
        [QPHY_START_CTRL]               = 0x00,
        [QPHY_PCS_READY_STATUS]         = 0x168,
 };
 
-static const unsigned int pciephy_regs_layout[] = {
+static const unsigned int pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
        [QPHY_COM_SW_RESET]             = 0x400,
        [QPHY_COM_POWER_DOWN_CONTROL]   = 0x404,
        [QPHY_COM_START_CONTROL]        = 0x408,
@@ -142,7 +145,7 @@ static const unsigned int pciephy_regs_layout[] = {
        [QPHY_PCS_STATUS]               = 0x174,
 };
 
-static const unsigned int usb3phy_regs_layout[] = {
+static const unsigned int usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
        [QPHY_FLL_CNTRL1]               = 0xc0,
        [QPHY_FLL_CNTRL2]               = 0xc4,
        [QPHY_FLL_CNT_VAL_L]            = 0xc8,
@@ -156,7 +159,7 @@ static const unsigned int usb3phy_regs_layout[] = {
        [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x178,
 };
 
-static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
+static const unsigned int qmp_v3_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
        [QPHY_SW_RESET]                 = 0x00,
        [QPHY_START_CTRL]               = 0x08,
        [QPHY_PCS_STATUS]               = 0x174,
@@ -165,27 +168,34 @@ static const unsigned int qmp_v3_usb3phy_regs_layout[] = {
        [QPHY_PCS_LFPS_RXTERM_IRQ_STATUS] = 0x170,
 };
 
-static const unsigned int sdm845_qmp_pciephy_regs_layout[] = {
+static const unsigned int sdm845_qmp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
        [QPHY_SW_RESET]                 = 0x00,
        [QPHY_START_CTRL]               = 0x08,
        [QPHY_PCS_STATUS]               = 0x174,
 };
 
-static const unsigned int sdm845_qhp_pciephy_regs_layout[] = {
+static const unsigned int sdm845_qhp_pciephy_regs_layout[QPHY_LAYOUT_SIZE] = {
        [QPHY_SW_RESET]                 = 0x00,
        [QPHY_START_CTRL]               = 0x08,
        [QPHY_PCS_STATUS]               = 0x2ac,
 };
 
-static const unsigned int sdm845_ufsphy_regs_layout[] = {
+static const unsigned int qmp_v4_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = {
+       [QPHY_SW_RESET]                 = 0x00,
+       [QPHY_START_CTRL]               = 0x44,
+       [QPHY_PCS_STATUS]               = 0x14,
+       [QPHY_PCS_POWER_DOWN_CONTROL]   = 0x40,
+};
+
+static const unsigned int sdm845_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
        [QPHY_START_CTRL]               = 0x00,
        [QPHY_PCS_READY_STATUS]         = 0x160,
 };
 
-static const unsigned int sm8150_ufsphy_regs_layout[] = {
-       [QPHY_START_CTRL]               = QPHY_V4_PHY_START,
-       [QPHY_PCS_READY_STATUS]         = QPHY_V4_PCS_READY_STATUS,
-       [QPHY_SW_RESET]                 = QPHY_V4_SW_RESET,
+static const unsigned int sm8150_ufsphy_regs_layout[QPHY_LAYOUT_SIZE] = {
+       [QPHY_START_CTRL]               = QPHY_V4_PCS_UFS_PHY_START,
+       [QPHY_PCS_READY_STATUS]         = QPHY_V4_PCS_UFS_READY_STATUS,
+       [QPHY_SW_RESET]                 = QPHY_V4_PCS_UFS_SW_RESET,
 };
 
 static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
@@ -1272,13 +1282,121 @@ static const struct qmp_phy_init_tbl sm8150_ufsphy_rx_tbl[] = {
 };
 
 static const struct qmp_phy_init_tbl sm8150_ufsphy_pcs_tbl[] = {
-       QMP_PHY_INIT_CFG(QPHY_V4_RX_SIGDET_CTRL2, 0x6d),
-       QMP_PHY_INIT_CFG(QPHY_V4_TX_LARGE_AMP_DRV_LVL, 0x0a),
-       QMP_PHY_INIT_CFG(QPHY_V4_TX_SMALL_AMP_DRV_LVL, 0x02),
-       QMP_PHY_INIT_CFG(QPHY_V4_TX_MID_TERM_CTRL1, 0x43),
-       QMP_PHY_INIT_CFG(QPHY_V4_DEBUG_BUS_CLKSEL, 0x1f),
-       QMP_PHY_INIT_CFG(QPHY_V4_RX_MIN_HIBERN8_TIME, 0xff),
-       QMP_PHY_INIT_CFG(QPHY_V4_MULTI_LANE_CTRL1, 0x02),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2, 0x6d),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL, 0x0a),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL, 0x02),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1, 0x43),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL, 0x1f),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME, 0xff),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1, 0x02),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_serdes_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_EN_CENTER, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER1, 0x31),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_PER2, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0, 0xde),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0, 0x07),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1, 0xde),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1, 0x07),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_BUF_ENABLE, 0x0a),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_CMN_IPTRIM, 0x20),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE0, 0x06),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_CP_CTRL_MODE1, 0x06),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE0, 0x16),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_RCTRL_MODE1, 0x16),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE0, 0x36),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_PLL_CCTRL_MODE1, 0x36),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0x1a),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP_EN, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE0, 0x14),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE0, 0x34),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP1_MODE1, 0x34),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_LOCK_CMP2_MODE1, 0x82),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE0, 0x82),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DEC_START_MODE1, 0x82),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE0, 0xab),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE0, 0xea),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE0, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE_MAP, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START1_MODE1, 0xab),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START2_MODE1, 0xea),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_DIV_FRAC_START3_MODE1, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE0, 0x24),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE1_MODE1, 0x24),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_VCO_TUNE2_MODE1, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_CORECLK_DIV_MODE1, 0x08),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xca),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1e),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1, 0xca),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1, 0x1e),
+       QMP_PHY_INIT_CFG(QSERDES_V4_COM_BIN_VCOCAL_HSCLK_SEL, 0x11),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_tx_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_TX, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V4_TX_RES_CODE_LANE_RX, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V4_TX_LANE_MODE_1, 0xd5),
+       QMP_PHY_INIT_CFG(QSERDES_V4_TX_RCV_DETECT_LVL_2, 0x12),
+       QMP_PHY_INIT_CFG(QSERDES_V4_TX_PI_QEC_CTRL, 0x20),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_rx_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_GAIN, 0x05),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN, 0x2f),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7f),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW, 0xff),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x0f),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_PI_CONTROLS, 0x99),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH1, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_THRESH2, 0x08),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN1, 0x05),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_UCDR_SB2_GAIN2, 0x05),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL1, 0x54),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_VGA_CAL_CNTRL2, 0x0e),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0f),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4a),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0a),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW, 0xc0),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_CNTRL, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_SIGDET_DEGLITCH_CNTRL, 0x0e),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_LOW, 0xbf),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH, 0xbf),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH2, 0x3f),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH3, 0x7f),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_00_HIGH4, 0x94),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_LOW, 0xdc),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH, 0xdc),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH2, 0x5c),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH3, 0x0b),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_RX_MODE_01_HIGH4, 0xb3),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_EN_TIMER, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE, 0xa0),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_DCC_CTRL1, 0x0c),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_GM_CAL, 0x1f),
+       QMP_PHY_INIT_CFG(QSERDES_V4_RX_VTH_CODE, 0x10),
+};
+
+static const struct qmp_phy_init_tbl sm8150_usb3_pcs_tbl[] = {
+       /* Lock Det settings */
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG1, 0xd0),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG2, 0x07),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_LOCK_DETECT_CONFIG6, 0x13),
+
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_REFGEN_REQ_CONFIG1, 0x21),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_RX_SIGDET_LVL, 0xaa),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_CDR_RESET_TIME, 0x0a),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG1, 0x88),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_ALIGN_DETECT_CONFIG2, 0x13),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_PCS_TX_RX_CONFIG, 0x0c),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG1, 0x4b),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_EQ_CONFIG5, 0x10),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8),
+       QMP_PHY_INIT_CFG(QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07),
 };
 
 /* struct qmp_phy_cfg - per-PHY initialization config */
@@ -1445,6 +1563,10 @@ static const char * const sdm845_pciephy_clk_l[] = {
        "aux", "cfg_ahb", "ref", "refgen",
 };
 
+static const char * const qmp_v4_phy_clk_l[] = {
+       "aux", "ref_clk_src", "ref", "com_aux",
+};
+
 static const char * const sdm845_ufs_phy_clk_l[] = {
        "ref", "ref_aux",
 };
@@ -1458,6 +1580,10 @@ static const char * const msm8996_usb3phy_reset_l[] = {
        "phy", "common",
 };
 
+static const char * const sc7180_usb3phy_reset_l[] = {
+       "phy",
+};
+
 static const char * const sdm845_pciephy_reset_l[] = {
        "phy",
 };
@@ -1671,6 +1797,37 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
        .is_dual_lane_phy       = true,
 };
 
+static const struct qmp_phy_cfg sc7180_usb3phy_cfg = {
+       .type                   = PHY_TYPE_USB3,
+       .nlanes                 = 1,
+
+       .serdes_tbl             = qmp_v3_usb3_serdes_tbl,
+       .serdes_tbl_num         = ARRAY_SIZE(qmp_v3_usb3_serdes_tbl),
+       .tx_tbl                 = qmp_v3_usb3_tx_tbl,
+       .tx_tbl_num             = ARRAY_SIZE(qmp_v3_usb3_tx_tbl),
+       .rx_tbl                 = qmp_v3_usb3_rx_tbl,
+       .rx_tbl_num             = ARRAY_SIZE(qmp_v3_usb3_rx_tbl),
+       .pcs_tbl                = qmp_v3_usb3_pcs_tbl,
+       .pcs_tbl_num            = ARRAY_SIZE(qmp_v3_usb3_pcs_tbl),
+       .clk_list               = qmp_v3_phy_clk_l,
+       .num_clks               = ARRAY_SIZE(qmp_v3_phy_clk_l),
+       .reset_list             = sc7180_usb3phy_reset_l,
+       .num_resets             = ARRAY_SIZE(sc7180_usb3phy_reset_l),
+       .vreg_list              = qmp_phy_vreg_l,
+       .num_vregs              = ARRAY_SIZE(qmp_phy_vreg_l),
+       .regs                   = qmp_v3_usb3phy_regs_layout,
+
+       .start_ctrl             = SERDES_START | PCS_START,
+       .pwrdn_ctrl             = SW_PWRDN,
+
+       .has_pwrdn_delay        = true,
+       .pwrdn_delay_min        = POWER_DOWN_DELAY_US_MIN,
+       .pwrdn_delay_max        = POWER_DOWN_DELAY_US_MAX,
+
+       .has_phy_dp_com_ctrl    = true,
+       .is_dual_lane_phy       = true,
+};
+
 static const struct qmp_phy_cfg qmp_v3_usb3_uniphy_cfg = {
        .type                   = PHY_TYPE_USB3,
        .nlanes                 = 1,
@@ -1798,6 +1955,37 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
        .is_dual_lane_phy       = true,
 };
 
+static const struct qmp_phy_cfg sm8150_usb3phy_cfg = {
+       .type                   = PHY_TYPE_USB3,
+       .nlanes                 = 1,
+
+       .serdes_tbl             = sm8150_usb3_serdes_tbl,
+       .serdes_tbl_num         = ARRAY_SIZE(sm8150_usb3_serdes_tbl),
+       .tx_tbl                 = sm8150_usb3_tx_tbl,
+       .tx_tbl_num             = ARRAY_SIZE(sm8150_usb3_tx_tbl),
+       .rx_tbl                 = sm8150_usb3_rx_tbl,
+       .rx_tbl_num             = ARRAY_SIZE(sm8150_usb3_rx_tbl),
+       .pcs_tbl                = sm8150_usb3_pcs_tbl,
+       .pcs_tbl_num            = ARRAY_SIZE(sm8150_usb3_pcs_tbl),
+       .clk_list               = qmp_v4_phy_clk_l,
+       .num_clks               = ARRAY_SIZE(qmp_v4_phy_clk_l),
+       .reset_list             = msm8996_usb3phy_reset_l,
+       .num_resets             = ARRAY_SIZE(msm8996_usb3phy_reset_l),
+       .vreg_list              = qmp_phy_vreg_l,
+       .num_vregs              = ARRAY_SIZE(qmp_phy_vreg_l),
+       .regs                   = qmp_v4_usb3phy_regs_layout,
+
+       .start_ctrl             = SERDES_START | PCS_START,
+       .pwrdn_ctrl             = SW_PWRDN,
+
+       .has_pwrdn_delay        = true,
+       .pwrdn_delay_min        = POWER_DOWN_DELAY_US_MIN,
+       .pwrdn_delay_max        = POWER_DOWN_DELAY_US_MAX,
+
+       .has_phy_dp_com_ctrl    = true,
+       .is_dual_lane_phy       = true,
+};
+
 static void qcom_qmp_phy_configure(void __iomem *base,
                                   const unsigned int *regs,
                                   const struct qmp_phy_init_tbl tbl[],
@@ -1880,11 +2068,18 @@ static int qcom_qmp_phy_com_init(struct qmp_phy *qphy)
                             SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET);
        }
 
-       if (cfg->has_phy_com_ctrl)
+       if (cfg->has_phy_com_ctrl) {
                qphy_setbits(serdes, cfg->regs[QPHY_COM_POWER_DOWN_CONTROL],
                             SW_PWRDN);
-       else
-               qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
+       } else {
+               if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL])
+                       qphy_setbits(pcs,
+                                       cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+                                       cfg->pwrdn_ctrl);
+               else
+                       qphy_setbits(pcs, QPHY_POWER_DOWN_CONTROL,
+                                       cfg->pwrdn_ctrl);
+       }
 
        /* Serdes configuration */
        qcom_qmp_phy_configure(serdes, cfg->regs, cfg->serdes_tbl,
@@ -2110,7 +2305,13 @@ static int qcom_qmp_phy_disable(struct phy *phy)
        qphy_clrbits(qphy->pcs, cfg->regs[QPHY_START_CTRL], cfg->start_ctrl);
 
        /* Put PHY into POWER DOWN state: active low */
-       qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL, cfg->pwrdn_ctrl);
+       if (cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL]) {
+               qphy_clrbits(qphy->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL],
+                            cfg->pwrdn_ctrl);
+       } else {
+               qphy_clrbits(qphy->pcs, QPHY_POWER_DOWN_CONTROL,
+                               cfg->pwrdn_ctrl);
+       }
 
        if (cfg->has_lane_rst)
                reset_control_assert(qphy->lane_rst);
@@ -2516,6 +2717,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
                .compatible = "qcom,ipq8074-qmp-pcie-phy",
                .data = &ipq8074_pciephy_cfg,
        }, {
+               .compatible = "qcom,sc7180-qmp-usb3-phy",
+               .data = &sc7180_usb3phy_cfg,
+       }, {
                .compatible = "qcom,sdm845-qhp-pcie-phy",
                .data = &sdm845_qhp_pciephy_cfg,
        }, {
@@ -2536,6 +2740,12 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
        }, {
                .compatible = "qcom,sm8150-qmp-ufs-phy",
                .data = &sm8150_ufsphy_cfg,
+       }, {
+               .compatible = "qcom,sm8250-qmp-ufs-phy",
+               .data = &sm8150_ufsphy_cfg,
+       }, {
+               .compatible = "qcom,sm8150-qmp-usb3-phy",
+               .data = &sm8150_usb3phy_cfg,
        },
        { },
 };
index dece0e6..6d017a0 100644 (file)
 #define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB           0x1DC
 #define QPHY_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB           0x1E0
 
-/* Only for QMP V3 PHY - DP COM registers */
+/* Only for QMP V3 & V4 PHY - DP COM registers */
 #define QPHY_V3_DP_COM_PHY_MODE_CTRL                   0x00
 #define QPHY_V3_DP_COM_SW_RESET                                0x04
 #define QPHY_V3_DP_COM_POWER_DOWN_CTRL                 0x08
 #define QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5                0x60
 
 /* Only for QMP V4 PHY - QSERDES COM registers */
+#define QSERDES_V4_COM_SSC_EN_CENTER                   0x010
+#define QSERDES_V4_COM_SSC_PER1                                0x01c
+#define QSERDES_V4_COM_SSC_PER2                                0x020
+#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE0            0x024
+#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE0            0x028
+#define QSERDES_V4_COM_SSC_STEP_SIZE1_MODE1            0x030
+#define QSERDES_V4_COM_SSC_STEP_SIZE2_MODE1            0x034
+#define QSERDES_V4_COM_SYSCLK_BUF_ENABLE               0x050
 #define QSERDES_V4_COM_PLL_IVCO                                0x058
 #define QSERDES_V4_COM_CMN_IPTRIM                      0x060
 #define QSERDES_V4_COM_CP_CTRL_MODE0                   0x074
 #define QSERDES_V4_COM_DEC_START_MODE0                 0x0bc
 #define QSERDES_V4_COM_LOCK_CMP2_MODE1                 0x0b8
 #define QSERDES_V4_COM_DEC_START_MODE1                 0x0c4
+#define QSERDES_V4_COM_DIV_FRAC_START1_MODE0           0x0cc
+#define QSERDES_V4_COM_DIV_FRAC_START2_MODE0           0x0d0
+#define QSERDES_V4_COM_DIV_FRAC_START3_MODE0           0x0d4
+#define QSERDES_V4_COM_DIV_FRAC_START1_MODE1           0x0d8
+#define QSERDES_V4_COM_DIV_FRAC_START2_MODE1           0x0dc
+#define QSERDES_V4_COM_DIV_FRAC_START3_MODE1           0x0e0
 #define QSERDES_V4_COM_VCO_TUNE_MAP                    0x10c
+#define QSERDES_V4_COM_VCO_TUNE1_MODE0                 0x110
+#define QSERDES_V4_COM_VCO_TUNE2_MODE0                 0x114
+#define QSERDES_V4_COM_VCO_TUNE1_MODE1                 0x118
+#define QSERDES_V4_COM_VCO_TUNE2_MODE1                 0x11c
 #define QSERDES_V4_COM_VCO_TUNE_INITVAL2               0x124
 #define QSERDES_V4_COM_HSCLK_SEL                       0x158
 #define QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL             0x15c
+#define QSERDES_V4_COM_CORECLK_DIV_MODE1               0x16c
+#define QSERDES_V4_COM_SVS_MODE_CLK_SEL                        0x184
 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE0      0x1ac
 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE0      0x1b0
 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE1_MODE1      0x1b4
 #define QSERDES_V4_COM_BIN_VCOCAL_CMP_CODE2_MODE1      0x1b8
 
 /* Only for QMP V4 PHY - TX registers */
+#define QSERDES_V4_TX_RES_CODE_LANE_TX                 0x34
+#define QSERDES_V4_TX_RES_CODE_LANE_RX                 0x38
 #define QSERDES_V4_TX_LANE_MODE_1                      0x84
+#define QSERDES_V4_TX_RCV_DETECT_LVL_2                 0x9c
 #define QSERDES_V4_TX_PWM_GEAR_1_DIVIDER_BAND0_1       0xd8
 #define QSERDES_V4_TX_PWM_GEAR_2_DIVIDER_BAND0_1       0xdC
 #define QSERDES_V4_TX_PWM_GEAR_3_DIVIDER_BAND0_1       0xe0
 #define QSERDES_V4_TX_PWM_GEAR_4_DIVIDER_BAND0_1       0xe4
 #define QSERDES_V4_TX_TRAN_DRVR_EMP_EN                 0xb8
+#define QSERDES_V4_TX_PI_QEC_CTRL              0x104
 
 /* Only for QMP V4 PHY - RX registers */
 #define QSERDES_V4_RX_UCDR_FO_GAIN                     0x008
 #define QSERDES_V4_RX_UCDR_FASTLOCK_FO_GAIN            0x030
 #define QSERDES_V4_RX_UCDR_SO_SATURATION_AND_ENABLE    0x034
 #define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_LOW          0x03c
+#define QSERDES_V4_RX_UCDR_FASTLOCK_COUNT_HIGH         0x040
 #define QSERDES_V4_RX_UCDR_PI_CONTROLS                 0x044
 #define QSERDES_V4_RX_UCDR_PI_CTRL2                    0x048
+#define QSERDES_V4_RX_UCDR_SB2_THRESH1                 0x04c
+#define QSERDES_V4_RX_UCDR_SB2_THRESH2                 0x050
+#define QSERDES_V4_RX_UCDR_SB2_GAIN1                   0x054
+#define QSERDES_V4_RX_UCDR_SB2_GAIN2                   0x058
+#define QSERDES_V4_RX_AUX_DATA_TCOARSE_TFINE                   0x060
 #define QSERDES_V4_RX_AC_JTAG_ENABLE                   0x068
 #define QSERDES_V4_RX_AC_JTAG_MODE                     0x078
 #define QSERDES_V4_RX_RX_TERM_BW                       0x080
+#define QSERDES_V4_RX_VGA_CAL_CNTRL1                   0x0d4
+#define QSERDES_V4_RX_VGA_CAL_CNTRL2                   0x0d8
+#define QSERDES_V4_RX_GM_CAL                           0x0dc
 #define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL2            0x0ec
 #define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL3            0x0f0
 #define QSERDES_V4_RX_RX_EQU_ADAPTOR_CNTRL4            0x0f4
 #define QSERDES_V4_RX_RX_IDAC_TSETTLE_LOW              0x0f8
 #define QSERDES_V4_RX_RX_IDAC_TSETTLE_HIGH             0x0fc
 #define QSERDES_V4_RX_RX_IDAC_MEASURE_TIME             0x100
+#define QSERDES_V4_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1      0x110
 #define QSERDES_V4_RX_RX_OFFSET_ADAPTOR_CNTRL2         0x114
 #define QSERDES_V4_RX_SIGDET_CNTRL                     0x11c
 #define QSERDES_V4_RX_SIGDET_LVL                       0x120
 #define QSERDES_V4_RX_RX_MODE_10_HIGH2                 0x1a0
 #define QSERDES_V4_RX_RX_MODE_10_HIGH3                 0x1a4
 #define QSERDES_V4_RX_RX_MODE_10_HIGH4                 0x1a8
+#define QSERDES_V4_RX_DFE_EN_TIMER                     0x1b4
+#define QSERDES_V4_RX_DFE_CTLE_POST_CAL_OFFSET         0x1b8
 #define QSERDES_V4_RX_DCC_CTRL1                                0x1bc
+#define QSERDES_V4_RX_VTH_CODE                         0x1c4
 
-/* Only for QMP V4 PHY - PCS registers */
-#define QPHY_V4_PHY_START                              0x000
-#define QPHY_V4_POWER_DOWN_CONTROL                     0x004
-#define QPHY_V4_SW_RESET                               0x008
-#define QPHY_V4_TIMER_20US_CORECLK_STEPS_MSB           0x00c
-#define QPHY_V4_TIMER_20US_CORECLK_STEPS_LSB           0x010
-#define QPHY_V4_PLL_CNTL                               0x02c
-#define QPHY_V4_TX_LARGE_AMP_DRV_LVL                   0x030
-#define QPHY_V4_TX_SMALL_AMP_DRV_LVL                   0x038
-#define QPHY_V4_BIST_FIXED_PAT_CTRL                    0x060
-#define QPHY_V4_TX_HSGEAR_CAPABILITY                   0x074
-#define QPHY_V4_RX_HSGEAR_CAPABILITY                   0x0b4
-#define QPHY_V4_DEBUG_BUS_CLKSEL                       0x124
-#define QPHY_V4_LINECFG_DISABLE                                0x148
-#define QPHY_V4_RX_MIN_HIBERN8_TIME                    0x150
-#define QPHY_V4_RX_SIGDET_CTRL2                                0x158
-#define QPHY_V4_TX_PWM_GEAR_BAND                       0x160
-#define QPHY_V4_TX_HS_GEAR_BAND                                0x168
-#define QPHY_V4_PCS_READY_STATUS                       0x180
-#define QPHY_V4_TX_MID_TERM_CTRL1                      0x1d8
-#define QPHY_V4_MULTI_LANE_CTRL1                       0x1e0
+/* Only for QMP V4 PHY - UFS PCS registers */
+#define QPHY_V4_PCS_UFS_PHY_START                              0x000
+#define QPHY_V4_PCS_UFS_POWER_DOWN_CONTROL                     0x004
+#define QPHY_V4_PCS_UFS_SW_RESET                               0x008
+#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_MSB           0x00c
+#define QPHY_V4_PCS_UFS_TIMER_20US_CORECLK_STEPS_LSB           0x010
+#define QPHY_V4_PCS_UFS_PLL_CNTL                               0x02c
+#define QPHY_V4_PCS_UFS_TX_LARGE_AMP_DRV_LVL                   0x030
+#define QPHY_V4_PCS_UFS_TX_SMALL_AMP_DRV_LVL                   0x038
+#define QPHY_V4_PCS_UFS_BIST_FIXED_PAT_CTRL                    0x060
+#define QPHY_V4_PCS_UFS_TX_HSGEAR_CAPABILITY                   0x074
+#define QPHY_V4_PCS_UFS_RX_HSGEAR_CAPABILITY                   0x0b4
+#define QPHY_V4_PCS_UFS_DEBUG_BUS_CLKSEL                       0x124
+#define QPHY_V4_PCS_UFS_LINECFG_DISABLE                                0x148
+#define QPHY_V4_PCS_UFS_RX_MIN_HIBERN8_TIME                    0x150
+#define QPHY_V4_PCS_UFS_RX_SIGDET_CTRL2                                0x158
+#define QPHY_V4_PCS_UFS_TX_PWM_GEAR_BAND                       0x160
+#define QPHY_V4_PCS_UFS_TX_HS_GEAR_BAND                                0x168
+#define QPHY_V4_PCS_UFS_READY_STATUS                   0x180
+#define QPHY_V4_PCS_UFS_TX_MID_TERM_CTRL1                      0x1d8
+#define QPHY_V4_PCS_UFS_MULTI_LANE_CTRL1                       0x1e0
 
 /* PCIE GEN3 COM registers */
 #define PCIE_GEN3_QHP_COM_SSC_EN_CENTER                        0x14
 #define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5          0x16c
 #define PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG             0x174
 
+/* Only for QMP V4 PHY - USB/PCIe PCS registers */
+#define QPHY_V4_PCS_SW_RESET                           0x000
+#define QPHY_V4_PCS_REVISION_ID0                       0x004
+#define QPHY_V4_PCS_REVISION_ID1                       0x008
+#define QPHY_V4_PCS_REVISION_ID2                       0x00c
+#define QPHY_V4_PCS_REVISION_ID3                       0x010
+#define QPHY_V4_PCS_PCS_STATUS1                                0x014
+#define QPHY_V4_PCS_PCS_STATUS2                                0x018
+#define QPHY_V4_PCS_PCS_STATUS3                                0x01c
+#define QPHY_V4_PCS_PCS_STATUS4                                0x020
+#define QPHY_V4_PCS_PCS_STATUS5                                0x024
+#define QPHY_V4_PCS_PCS_STATUS6                                0x028
+#define QPHY_V4_PCS_PCS_STATUS7                                0x02c
+#define QPHY_V4_PCS_DEBUG_BUS_0_STATUS                 0x030
+#define QPHY_V4_PCS_DEBUG_BUS_1_STATUS                 0x034
+#define QPHY_V4_PCS_DEBUG_BUS_2_STATUS                 0x038
+#define QPHY_V4_PCS_DEBUG_BUS_3_STATUS                 0x03c
+#define QPHY_V4_PCS_POWER_DOWN_CONTROL                 0x040
+#define QPHY_V4_PCS_START_CONTROL                      0x044
+#define QPHY_V4_PCS_INSIG_SW_CTRL1                     0x048
+#define QPHY_V4_PCS_INSIG_SW_CTRL2                     0x04c
+#define QPHY_V4_PCS_INSIG_SW_CTRL3                     0x050
+#define QPHY_V4_PCS_INSIG_SW_CTRL4                     0x054
+#define QPHY_V4_PCS_INSIG_SW_CTRL5                     0x058
+#define QPHY_V4_PCS_INSIG_SW_CTRL6                     0x05c
+#define QPHY_V4_PCS_INSIG_SW_CTRL7                     0x060
+#define QPHY_V4_PCS_INSIG_SW_CTRL8                     0x064
+#define QPHY_V4_PCS_INSIG_MX_CTRL1                     0x068
+#define QPHY_V4_PCS_INSIG_MX_CTRL2                     0x06c
+#define QPHY_V4_PCS_INSIG_MX_CTRL3                     0x070
+#define QPHY_V4_PCS_INSIG_MX_CTRL4                     0x074
+#define QPHY_V4_PCS_INSIG_MX_CTRL5                     0x078
+#define QPHY_V4_PCS_INSIG_MX_CTRL7                     0x07c
+#define QPHY_V4_PCS_INSIG_MX_CTRL8                     0x080
+#define QPHY_V4_PCS_OUTSIG_SW_CTRL1                    0x084
+#define QPHY_V4_PCS_OUTSIG_MX_CTRL1                    0x088
+#define QPHY_V4_PCS_CLAMP_ENABLE                       0x08c
+#define QPHY_V4_PCS_POWER_STATE_CONFIG1                        0x090
+#define QPHY_V4_PCS_POWER_STATE_CONFIG2                        0x094
+#define QPHY_V4_PCS_FLL_CNTRL1                         0x098
+#define QPHY_V4_PCS_FLL_CNTRL2                         0x09c
+#define QPHY_V4_PCS_FLL_CNT_VAL_L                      0x0a0
+#define QPHY_V4_PCS_FLL_CNT_VAL_H_TOL                  0x0a4
+#define QPHY_V4_PCS_FLL_MAN_CODE                       0x0a8
+#define QPHY_V4_PCS_TEST_CONTROL1                      0x0ac
+#define QPHY_V4_PCS_TEST_CONTROL2                      0x0b0
+#define QPHY_V4_PCS_TEST_CONTROL3                      0x0b4
+#define QPHY_V4_PCS_TEST_CONTROL4                      0x0b8
+#define QPHY_V4_PCS_TEST_CONTROL5                      0x0bc
+#define QPHY_V4_PCS_TEST_CONTROL6                      0x0c0
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG1                        0x0c4
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG2                        0x0c8
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG3                        0x0cc
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG4                        0x0d0
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG5                        0x0d4
+#define QPHY_V4_PCS_LOCK_DETECT_CONFIG6                        0x0d8
+#define QPHY_V4_PCS_REFGEN_REQ_CONFIG1                 0x0dc
+#define QPHY_V4_PCS_REFGEN_REQ_CONFIG2                 0x0e0
+#define QPHY_V4_PCS_REFGEN_REQ_CONFIG3                 0x0e4
+#define QPHY_V4_PCS_BIST_CTRL                          0x0e8
+#define QPHY_V4_PCS_PRBS_POLY0                         0x0ec
+#define QPHY_V4_PCS_PRBS_POLY1                         0x0f0
+#define QPHY_V4_PCS_FIXED_PAT0                         0x0f4
+#define QPHY_V4_PCS_FIXED_PAT1                         0x0f8
+#define QPHY_V4_PCS_FIXED_PAT2                         0x0fc
+#define QPHY_V4_PCS_FIXED_PAT3                         0x100
+#define QPHY_V4_PCS_FIXED_PAT4                         0x104
+#define QPHY_V4_PCS_FIXED_PAT5                         0x108
+#define QPHY_V4_PCS_FIXED_PAT6                         0x10c
+#define QPHY_V4_PCS_FIXED_PAT7                         0x110
+#define QPHY_V4_PCS_FIXED_PAT8                         0x114
+#define QPHY_V4_PCS_FIXED_PAT9                         0x118
+#define QPHY_V4_PCS_FIXED_PAT10                                0x11c
+#define QPHY_V4_PCS_FIXED_PAT11                                0x120
+#define QPHY_V4_PCS_FIXED_PAT12                                0x124
+#define QPHY_V4_PCS_FIXED_PAT13                                0x128
+#define QPHY_V4_PCS_FIXED_PAT14                                0x12c
+#define QPHY_V4_PCS_FIXED_PAT15                                0x130
+#define QPHY_V4_PCS_TXMGN_CONFIG                       0x134
+#define QPHY_V4_PCS_G12S1_TXMGN_V0                     0x138
+#define QPHY_V4_PCS_G12S1_TXMGN_V1                     0x13c
+#define QPHY_V4_PCS_G12S1_TXMGN_V2                     0x140
+#define QPHY_V4_PCS_G12S1_TXMGN_V3                     0x144
+#define QPHY_V4_PCS_G12S1_TXMGN_V4                     0x148
+#define QPHY_V4_PCS_G12S1_TXMGN_V0_RS                  0x14c
+#define QPHY_V4_PCS_G12S1_TXMGN_V1_RS                  0x150
+#define QPHY_V4_PCS_G12S1_TXMGN_V2_RS                  0x154
+#define QPHY_V4_PCS_G12S1_TXMGN_V3_RS                  0x158
+#define QPHY_V4_PCS_G12S1_TXMGN_V4_RS                  0x15c
+#define QPHY_V4_PCS_G3S2_TXMGN_MAIN                    0x160
+#define QPHY_V4_PCS_G3S2_TXMGN_MAIN_RS                 0x164
+#define QPHY_V4_PCS_G12S1_TXDEEMPH_M6DB                        0x168
+#define QPHY_V4_PCS_G12S1_TXDEEMPH_M3P5DB              0x16c
+#define QPHY_V4_PCS_G3S2_PRE_GAIN                      0x170
+#define QPHY_V4_PCS_G3S2_POST_GAIN                     0x174
+#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET               0x178
+#define QPHY_V4_PCS_G3S2_PRE_GAIN_RS                   0x17c
+#define QPHY_V4_PCS_G3S2_POST_GAIN_RS                  0x180
+#define QPHY_V4_PCS_G3S2_PRE_POST_OFFSET_RS            0x184
+#define QPHY_V4_PCS_RX_SIGDET_LVL                      0x188
+#define QPHY_V4_PCS_RX_SIGDET_DTCT_CNTRL               0x18c
+#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_L               0x190
+#define QPHY_V4_PCS_RCVR_DTCT_DLY_P1U2_H               0x194
+#define QPHY_V4_PCS_RATE_SLEW_CNTRL1                   0x198
+#define QPHY_V4_PCS_RATE_SLEW_CNTRL2                   0x19c
+#define QPHY_V4_PCS_PWRUP_RESET_DLY_TIME_AUXCLK                0x1a0
+#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_L      0x1a4
+#define QPHY_V4_PCS_P2U3_WAKEUP_DLY_TIME_AUXCLK_H      0x1a8
+#define QPHY_V4_PCS_TSYNC_RSYNC_TIME                   0x1ac
+#define QPHY_V4_PCS_CDR_RESET_TIME                     0x1b0
+#define QPHY_V4_PCS_TSYNC_DLY_TIME                     0x1b4
+#define QPHY_V4_PCS_ELECIDLE_DLY_SEL                   0x1b8
+#define QPHY_V4_PCS_CMN_ACK_OUT_SEL                    0x1bc
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG1               0x1c0
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG2               0x1c4
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG3               0x1c8
+#define QPHY_V4_PCS_ALIGN_DETECT_CONFIG4               0x1cc
+#define QPHY_V4_PCS_PCS_TX_RX_CONFIG                   0x1d0
+#define QPHY_V4_PCS_RX_IDLE_DTCT_CNTRL                 0x1d4
+#define QPHY_V4_PCS_RX_DCC_CAL_CONFIG                  0x1d8
+#define QPHY_V4_PCS_EQ_CONFIG1                         0x1dc
+#define QPHY_V4_PCS_EQ_CONFIG2                         0x1e0
+#define QPHY_V4_PCS_EQ_CONFIG3                         0x1e4
+#define QPHY_V4_PCS_EQ_CONFIG4                         0x1e8
+#define QPHY_V4_PCS_EQ_CONFIG5                         0x1ec
+#define QPHY_V4_PCS_USB3_POWER_STATE_CONFIG1           0x300
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_STATUS                0x304
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL          0x308
+#define QPHY_V4_PCS_USB3_AUTONOMOUS_MODE_CTRL2         0x30c
+#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_SOURCE_STATUS 0x310
+#define QPHY_V4_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR         0x314
+#define QPHY_V4_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL       0x318
+#define QPHY_V4_PCS_USB3_LFPS_TX_ECSTART               0x31c
+#define QPHY_V4_PCS_USB3_LFPS_PER_TIMER_VAL            0x320
+#define QPHY_V4_PCS_USB3_LFPS_TX_END_CNT_U3_START      0x324
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_LOCK_TIME                0x328
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME                0x32c
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_CTLE_TIME                0x330
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_WAIT_TIME_S2     0x334
+#define QPHY_V4_PCS_USB3_RXEQTRAINING_DFE_TIME_S2      0x338
+#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_L            0x33c
+#define QPHY_V4_PCS_USB3_RCVR_DTCT_DLY_U3_H            0x340
+#define QPHY_V4_PCS_USB3_ARCVR_DTCT_EN_PERIOD          0x344
+#define QPHY_V4_PCS_USB3_ARCVR_DTCT_CM_DLY             0x348
+#define QPHY_V4_PCS_USB3_TXONESZEROS_RUN_LENGTH                0x34c
+#define QPHY_V4_PCS_USB3_ALFPS_DEGLITCH_VAL            0x350
+#define QPHY_V4_PCS_USB3_SIGDET_STARTUP_TIMER_VAL      0x354
+#define QPHY_V4_PCS_USB3_TEST_CONTROL                  0x358
+
+/* Only for QMP V4 PHY - PCS_MISC registers */
+#define QPHY_V4_PCS_MISC_TYPEC_CTRL                    0x00
+#define QPHY_V4_PCS_MISC_TYPEC_PWRDN_CTRL              0x04
+#define QPHY_V4_PCS_MISC_PCS_MISC_CONFIG1              0x08
+#define QPHY_V4_PCS_MISC_CLAMP_ENABLE                  0x0c
+#define QPHY_V4_PCS_MISC_TYPEC_STATUS                  0x10
+#define QPHY_V4_PCS_MISC_PLACEHOLDER_STATUS            0x14
+
 #endif
diff --git a/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c b/drivers/phy/qualcomm/phy-qcom-snps-femto-v2.c
new file mode 100644 (file)
index 0000000..4d74045
--- /dev/null
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#define USB2_PHY_USB_PHY_UTMI_CTRL0            (0x3c)
+#define SLEEPM                                 BIT(0)
+#define OPMODE_MASK                            GENMASK(4, 3)
+#define OPMODE_NORMAL                          (0x00)
+#define OPMODE_NONDRIVING                      BIT(3)
+#define TERMSEL                                        BIT(5)
+
+#define USB2_PHY_USB_PHY_UTMI_CTRL1            (0x40)
+#define XCVRSEL                                        BIT(0)
+
+#define USB2_PHY_USB_PHY_UTMI_CTRL5            (0x50)
+#define POR                                    BIT(1)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0   (0x54)
+#define RETENABLEN                             BIT(3)
+#define FSEL_MASK                              GENMASK(7, 5)
+#define FSEL_DEFAULT                           (0x3 << 4)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1   (0x58)
+#define VBUSVLDEXTSEL0                         BIT(4)
+#define PLLBTUNE                               BIT(5)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2   (0x5c)
+#define VREGBYPASS                             BIT(0)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL1          (0x60)
+#define VBUSVLDEXT0                            BIT(0)
+
+#define USB2_PHY_USB_PHY_HS_PHY_CTRL2          (0x64)
+#define USB2_AUTO_RESUME                       BIT(0)
+#define USB2_SUSPEND_N                         BIT(2)
+#define USB2_SUSPEND_N_SEL                     BIT(3)
+
+#define USB2_PHY_USB_PHY_CFG0                  (0x94)
+#define UTMI_PHY_DATAPATH_CTRL_OVERRIDE_EN     BIT(0)
+#define UTMI_PHY_CMN_CTRL_OVERRIDE_EN          BIT(1)
+
+#define USB2_PHY_USB_PHY_REFCLK_CTRL           (0xa0)
+#define REFCLK_SEL_MASK                                GENMASK(1, 0)
+#define REFCLK_SEL_DEFAULT                     (0x2 << 0)
+
+static const char * const qcom_snps_hsphy_vreg_names[] = {
+       "vdda-pll", "vdda33", "vdda18",
+};
+
+#define SNPS_HS_NUM_VREGS              ARRAY_SIZE(qcom_snps_hsphy_vreg_names)
+
+/**
+ * struct qcom_snps_hsphy - snps hs phy attributes
+ *
+ * @phy: generic phy
+ * @base: iomapped memory space for snps hs phy
+ *
+ * @cfg_ahb_clk: AHB2PHY interface clock
+ * @ref_clk: phy reference clock
+ * @iface_clk: phy interface clock
+ * @phy_reset: phy reset control
+ * @vregs: regulator supplies bulk data
+ * @phy_initialized: if PHY has been initialized correctly
+ */
+struct qcom_snps_hsphy {
+       struct phy *phy;
+       void __iomem *base;
+
+       struct clk *cfg_ahb_clk;
+       struct clk *ref_clk;
+       struct reset_control *phy_reset;
+       struct regulator_bulk_data vregs[SNPS_HS_NUM_VREGS];
+
+       bool phy_initialized;
+};
+
+static inline void qcom_snps_hsphy_write_mask(void __iomem *base, u32 offset,
+                                               u32 mask, u32 val)
+{
+       u32 reg;
+
+       reg = readl_relaxed(base + offset);
+       reg &= ~mask;
+       reg |= val & mask;
+       writel_relaxed(reg, base + offset);
+
+       /* Ensure above write is completed */
+       readl_relaxed(base + offset);
+}
+
+static int qcom_snps_hsphy_init(struct phy *phy)
+{
+       struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
+       int ret;
+
+       dev_vdbg(&phy->dev, "%s(): Initializing SNPS HS phy\n", __func__);
+
+       ret = regulator_bulk_enable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
+       if (ret)
+               return ret;
+
+       ret = clk_prepare_enable(hsphy->cfg_ahb_clk);
+       if (ret) {
+               dev_err(&phy->dev, "failed to enable cfg ahb clock, %d\n", ret);
+               goto poweroff_phy;
+       }
+
+       ret = reset_control_assert(hsphy->phy_reset);
+       if (ret) {
+               dev_err(&phy->dev, "failed to assert phy_reset, %d\n", ret);
+               goto disable_ahb_clk;
+       }
+
+       usleep_range(100, 150);
+
+       ret = reset_control_deassert(hsphy->phy_reset);
+       if (ret) {
+               dev_err(&phy->dev, "failed to de-assert phy_reset, %d\n", ret);
+               goto disable_ahb_clk;
+       }
+
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
+                                       UTMI_PHY_CMN_CTRL_OVERRIDE_EN,
+                                       UTMI_PHY_CMN_CTRL_OVERRIDE_EN);
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
+                                                       POR, POR);
+       qcom_snps_hsphy_write_mask(hsphy->base,
+                                       USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON0,
+                                       FSEL_MASK, 0);
+       qcom_snps_hsphy_write_mask(hsphy->base,
+                                       USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
+                                       PLLBTUNE, PLLBTUNE);
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_REFCLK_CTRL,
+                                       REFCLK_SEL_DEFAULT, REFCLK_SEL_MASK);
+       qcom_snps_hsphy_write_mask(hsphy->base,
+                                       USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON1,
+                                       VBUSVLDEXTSEL0, VBUSVLDEXTSEL0);
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL1,
+                                       VBUSVLDEXT0, VBUSVLDEXT0);
+
+       qcom_snps_hsphy_write_mask(hsphy->base,
+                                       USB2_PHY_USB_PHY_HS_PHY_CTRL_COMMON2,
+                                       VREGBYPASS, VREGBYPASS);
+
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
+                                       USB2_SUSPEND_N_SEL | USB2_SUSPEND_N,
+                                       USB2_SUSPEND_N_SEL | USB2_SUSPEND_N);
+
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL0,
+                                       SLEEPM, SLEEPM);
+
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_UTMI_CTRL5,
+                                       POR, 0);
+
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_HS_PHY_CTRL2,
+                                       USB2_SUSPEND_N_SEL, 0);
+
+       qcom_snps_hsphy_write_mask(hsphy->base, USB2_PHY_USB_PHY_CFG0,
+                                       UTMI_PHY_CMN_CTRL_OVERRIDE_EN, 0);
+
+       hsphy->phy_initialized = true;
+
+       return 0;
+
+disable_ahb_clk:
+       clk_disable_unprepare(hsphy->cfg_ahb_clk);
+poweroff_phy:
+       regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
+
+       return ret;
+}
+
+static int qcom_snps_hsphy_exit(struct phy *phy)
+{
+       struct qcom_snps_hsphy *hsphy = phy_get_drvdata(phy);
+
+       reset_control_assert(hsphy->phy_reset);
+       clk_disable_unprepare(hsphy->cfg_ahb_clk);
+       regulator_bulk_disable(ARRAY_SIZE(hsphy->vregs), hsphy->vregs);
+       hsphy->phy_initialized = false;
+
+       return 0;
+}
+
+static const struct phy_ops qcom_snps_hsphy_gen_ops = {
+       .init           = qcom_snps_hsphy_init,
+       .exit           = qcom_snps_hsphy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static const struct of_device_id qcom_snps_hsphy_of_match_table[] = {
+       { .compatible   = "qcom,sm8150-usb-hs-phy", },
+       { .compatible   = "qcom,usb-snps-hs-7nm-phy", },
+       { .compatible   = "qcom,usb-snps-femto-v2-phy", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_of_match_table);
+
+static int qcom_snps_hsphy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct qcom_snps_hsphy *hsphy;
+       struct phy_provider *phy_provider;
+       struct phy *generic_phy;
+       int ret, i;
+       int num;
+
+       hsphy = devm_kzalloc(dev, sizeof(*hsphy), GFP_KERNEL);
+       if (!hsphy)
+               return -ENOMEM;
+
+       hsphy->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(hsphy->base))
+               return PTR_ERR(hsphy->base);
+
+       hsphy->ref_clk = devm_clk_get(dev, "ref");
+       if (IS_ERR(hsphy->ref_clk)) {
+               ret = PTR_ERR(hsphy->ref_clk);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "failed to get ref clk, %d\n", ret);
+               return ret;
+       }
+
+       hsphy->phy_reset = devm_reset_control_get_exclusive(&pdev->dev, NULL);
+       if (IS_ERR(hsphy->phy_reset)) {
+               dev_err(dev, "failed to get phy core reset\n");
+               return PTR_ERR(hsphy->phy_reset);
+       }
+
+       num = ARRAY_SIZE(hsphy->vregs);
+       for (i = 0; i < num; i++)
+               hsphy->vregs[i].supply = qcom_snps_hsphy_vreg_names[i];
+
+       ret = devm_regulator_bulk_get(dev, num, hsphy->vregs);
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "failed to get regulator supplies: %d\n",
+                               ret);
+               return ret;
+       }
+
+       generic_phy = devm_phy_create(dev, NULL, &qcom_snps_hsphy_gen_ops);
+       if (IS_ERR(generic_phy)) {
+               ret = PTR_ERR(generic_phy);
+               dev_err(dev, "failed to create phy, %d\n", ret);
+               return ret;
+       }
+       hsphy->phy = generic_phy;
+
+       dev_set_drvdata(dev, hsphy);
+       phy_set_drvdata(generic_phy, hsphy);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (!IS_ERR(phy_provider))
+               dev_dbg(dev, "Registered Qcom-SNPS HS phy\n");
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver qcom_snps_hsphy_driver = {
+       .probe          = qcom_snps_hsphy_probe,
+       .driver = {
+               .name   = "qcom-snps-hs-femto-v2-phy",
+               .of_match_table = qcom_snps_hsphy_of_match_table,
+       },
+};
+
+module_platform_driver(qcom_snps_hsphy_driver);
+
+MODULE_DESCRIPTION("Qualcomm SNPS FEMTO USB HS PHY V2 driver");
+MODULE_LICENSE("GPL v2");
index 56a5083..32be62e 100644 (file)
@@ -139,6 +139,10 @@ static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
                udelay(10);
                rst &= ~rstbits;
                writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+               /* The following delay is necessary for the reset sequence to be
+                * completed
+                */
+               udelay(80);
        } else {
                pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
                pwr |= phypwr;
index 88a047b..0a166d5 100644 (file)
@@ -77,6 +77,7 @@ static struct regmap_config serdes_am654_regmap_config = {
        .val_bits = 32,
        .reg_stride = 4,
        .fast_io = true,
+       .max_register = 0x1ffc,
 };
 
 static const struct reg_field cmu_master_cdn_o = REG_FIELD(CMU_R07C, 24, 24);
@@ -200,9 +201,91 @@ static int serdes_am654_power_off(struct phy *x)
        return 0;
 }
 
-static int serdes_am654_init(struct phy *x)
+#define SERDES_AM654_CFG(offset, a, b, val) \
+       regmap_update_bits(phy->regmap, (offset),\
+                          GENMASK((a), (b)), (val) << (b))
+
+static int serdes_am654_usb3_init(struct serdes_am654 *phy)
+{
+       SERDES_AM654_CFG(0x0000, 31, 24, 0x17);
+       SERDES_AM654_CFG(0x0004, 15, 8, 0x02);
+       SERDES_AM654_CFG(0x0004, 7, 0, 0x0e);
+       SERDES_AM654_CFG(0x0008, 23, 16, 0x2e);
+       SERDES_AM654_CFG(0x0008, 31, 24, 0x2e);
+       SERDES_AM654_CFG(0x0060, 7, 0, 0x4b);
+       SERDES_AM654_CFG(0x0060, 15, 8, 0x98);
+       SERDES_AM654_CFG(0x0060, 23, 16, 0x60);
+       SERDES_AM654_CFG(0x00d0, 31, 24, 0x45);
+       SERDES_AM654_CFG(0x00e8, 15, 8, 0x0e);
+       SERDES_AM654_CFG(0x0220, 7, 0, 0x34);
+       SERDES_AM654_CFG(0x0220, 15, 8, 0x34);
+       SERDES_AM654_CFG(0x0220, 31, 24, 0x37);
+       SERDES_AM654_CFG(0x0224, 7, 0, 0x37);
+       SERDES_AM654_CFG(0x0224, 15, 8, 0x37);
+       SERDES_AM654_CFG(0x0228, 23, 16, 0x37);
+       SERDES_AM654_CFG(0x0228, 31, 24, 0x37);
+       SERDES_AM654_CFG(0x022c, 7, 0, 0x37);
+       SERDES_AM654_CFG(0x022c, 15, 8, 0x37);
+       SERDES_AM654_CFG(0x0230, 15, 8, 0x2a);
+       SERDES_AM654_CFG(0x0230, 23, 16, 0x2a);
+       SERDES_AM654_CFG(0x0240, 23, 16, 0x10);
+       SERDES_AM654_CFG(0x0240, 31, 24, 0x34);
+       SERDES_AM654_CFG(0x0244, 7, 0, 0x40);
+       SERDES_AM654_CFG(0x0244, 23, 16, 0x34);
+       SERDES_AM654_CFG(0x0248, 15, 8, 0x0d);
+       SERDES_AM654_CFG(0x0258, 15, 8, 0x16);
+       SERDES_AM654_CFG(0x0258, 23, 16, 0x84);
+       SERDES_AM654_CFG(0x0258, 31, 24, 0xf2);
+       SERDES_AM654_CFG(0x025c, 7, 0, 0x21);
+       SERDES_AM654_CFG(0x0260, 7, 0, 0x27);
+       SERDES_AM654_CFG(0x0260, 15, 8, 0x04);
+       SERDES_AM654_CFG(0x0268, 15, 8, 0x04);
+       SERDES_AM654_CFG(0x0288, 15, 8, 0x2c);
+       SERDES_AM654_CFG(0x0330, 31, 24, 0xa0);
+       SERDES_AM654_CFG(0x0338, 23, 16, 0x03);
+       SERDES_AM654_CFG(0x0338, 31, 24, 0x00);
+       SERDES_AM654_CFG(0x033c, 7, 0, 0x00);
+       SERDES_AM654_CFG(0x0344, 31, 24, 0x18);
+       SERDES_AM654_CFG(0x034c, 7, 0, 0x18);
+       SERDES_AM654_CFG(0x039c, 23, 16, 0x3b);
+       SERDES_AM654_CFG(0x0a04, 7, 0, 0x03);
+       SERDES_AM654_CFG(0x0a14, 31, 24, 0x3c);
+       SERDES_AM654_CFG(0x0a18, 15, 8, 0x3c);
+       SERDES_AM654_CFG(0x0a38, 7, 0, 0x3e);
+       SERDES_AM654_CFG(0x0a38, 15, 8, 0x3e);
+       SERDES_AM654_CFG(0x0ae0, 7, 0, 0x07);
+       SERDES_AM654_CFG(0x0b6c, 23, 16, 0xcd);
+       SERDES_AM654_CFG(0x0b6c, 31, 24, 0x04);
+       SERDES_AM654_CFG(0x0b98, 23, 16, 0x03);
+       SERDES_AM654_CFG(0x1400, 7, 0, 0x3f);
+       SERDES_AM654_CFG(0x1404, 23, 16, 0x6f);
+       SERDES_AM654_CFG(0x1404, 31, 24, 0x6f);
+       SERDES_AM654_CFG(0x140c, 7, 0, 0x6f);
+       SERDES_AM654_CFG(0x140c, 15, 8, 0x6f);
+       SERDES_AM654_CFG(0x1410, 15, 8, 0x27);
+       SERDES_AM654_CFG(0x1414, 7, 0, 0x0c);
+       SERDES_AM654_CFG(0x1414, 23, 16, 0x07);
+       SERDES_AM654_CFG(0x1418, 23, 16, 0x40);
+       SERDES_AM654_CFG(0x141c, 7, 0, 0x00);
+       SERDES_AM654_CFG(0x141c, 15, 8, 0x1f);
+       SERDES_AM654_CFG(0x1428, 31, 24, 0x08);
+       SERDES_AM654_CFG(0x1434, 31, 24, 0x00);
+       SERDES_AM654_CFG(0x1444, 7, 0, 0x94);
+       SERDES_AM654_CFG(0x1460, 31, 24, 0x7f);
+       SERDES_AM654_CFG(0x1464, 7, 0, 0x43);
+       SERDES_AM654_CFG(0x1464, 23, 16, 0x6f);
+       SERDES_AM654_CFG(0x1464, 31, 24, 0x43);
+       SERDES_AM654_CFG(0x1484, 23, 16, 0x8f);
+       SERDES_AM654_CFG(0x1498, 7, 0, 0x4f);
+       SERDES_AM654_CFG(0x1498, 23, 16, 0x4f);
+       SERDES_AM654_CFG(0x007c, 31, 24, 0x0d);
+       SERDES_AM654_CFG(0x0b90, 15, 8, 0x0f);
+
+       return 0;
+}
+
+static int serdes_am654_pcie_init(struct serdes_am654 *phy)
 {
-       struct serdes_am654 *phy = phy_get_drvdata(x);
        int ret;
 
        ret = regmap_field_write(phy->config_version, VERSION);
@@ -220,11 +303,28 @@ static int serdes_am654_init(struct phy *x)
        return 0;
 }
 
+static int serdes_am654_init(struct phy *x)
+{
+       struct serdes_am654 *phy = phy_get_drvdata(x);
+
+       switch (phy->type) {
+       case PHY_TYPE_PCIE:
+               return serdes_am654_pcie_init(phy);
+       case PHY_TYPE_USB3:
+               return serdes_am654_usb3_init(phy);
+       default:
+               return -EINVAL;
+       }
+}
+
 static int serdes_am654_reset(struct phy *x)
 {
        struct serdes_am654 *phy = phy_get_drvdata(x);
        int ret;
 
+       serdes_am654_disable_pll(phy);
+       serdes_am654_disable_txrx(phy);
+
        ret = regmap_field_write(phy->por_en, 0x1);
        if (ret)
                return ret;
index 7b51045..30ea5b2 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/reset-controller.h>
+#include <dt-bindings/phy/phy.h>
 
 #define WIZ_SERDES_CTRL                0x404
 #define WIZ_SERDES_TOP_CTRL    0x408
@@ -78,6 +79,8 @@ static const struct reg_field p_enable[WIZ_MAX_LANES] = {
        REG_FIELD(WIZ_LANECTL(3), 30, 31),
 };
 
+enum p_enable { P_ENABLE = 2, P_ENABLE_FORCE = 1, P_ENABLE_DISABLE = 0 };
+
 static const struct reg_field p_align[WIZ_MAX_LANES] = {
        REG_FIELD(WIZ_LANECTL(0), 29, 29),
        REG_FIELD(WIZ_LANECTL(1), 29, 29),
@@ -220,6 +223,7 @@ struct wiz {
        struct reset_controller_dev wiz_phy_reset_dev;
        struct gpio_desc        *gpio_typec_dir;
        int                     typec_dir_delay;
+       u32 lane_phy_type[WIZ_MAX_LANES];
 };
 
 static int wiz_reset(struct wiz *wiz)
@@ -242,12 +246,17 @@ static int wiz_reset(struct wiz *wiz)
 static int wiz_mode_select(struct wiz *wiz)
 {
        u32 num_lanes = wiz->num_lanes;
+       enum wiz_lane_standard_mode mode;
        int ret;
        int i;
 
        for (i = 0; i < num_lanes; i++) {
-               ret = regmap_field_write(wiz->p_standard_mode[i],
-                                        LANE_MODE_GEN4);
+               if (wiz->lane_phy_type[i] == PHY_TYPE_DP)
+                       mode = LANE_MODE_GEN1;
+               else
+                       mode = LANE_MODE_GEN4;
+
+               ret = regmap_field_write(wiz->p_standard_mode[i], mode);
                if (ret)
                        return ret;
        }
@@ -707,7 +716,7 @@ static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev,
                return ret;
        }
 
-       ret = regmap_field_write(wiz->p_enable[id - 1], false);
+       ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_DISABLE);
        return ret;
 }
 
@@ -734,7 +743,11 @@ static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
                return ret;
        }
 
-       ret = regmap_field_write(wiz->p_enable[id - 1], true);
+       if (wiz->lane_phy_type[id - 1] == PHY_TYPE_DP)
+               ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE);
+       else
+               ret = regmap_field_write(wiz->p_enable[id - 1], P_ENABLE_FORCE);
+
        return ret;
 }
 
@@ -761,6 +774,40 @@ static const struct of_device_id wiz_id_table[] = {
 };
 MODULE_DEVICE_TABLE(of, wiz_id_table);
 
+static int wiz_get_lane_phy_types(struct device *dev, struct wiz *wiz)
+{
+       struct device_node *serdes, *subnode;
+
+       serdes = of_get_child_by_name(dev->of_node, "serdes");
+       if (!serdes) {
+               dev_err(dev, "%s: Getting \"serdes\"-node failed\n", __func__);
+               return -EINVAL;
+       }
+
+       for_each_child_of_node(serdes, subnode) {
+               u32 reg, num_lanes = 1, phy_type = PHY_NONE;
+               int ret, i;
+
+               ret = of_property_read_u32(subnode, "reg", &reg);
+               if (ret) {
+                       dev_err(dev,
+                               "%s: Reading \"reg\" from \"%s\" failed: %d\n",
+                               __func__, subnode->name, ret);
+                       return ret;
+               }
+               of_property_read_u32(subnode, "cdns,num-lanes", &num_lanes);
+               of_property_read_u32(subnode, "cdns,phy-type", &phy_type);
+
+               dev_dbg(dev, "%s: Lanes %u-%u have phy-type %u\n", __func__,
+                       reg, reg + num_lanes - 1, phy_type);
+
+               for (i = reg; i < reg + num_lanes; i++)
+                       wiz->lane_phy_type[i] = phy_type;
+       }
+
+       return 0;
+}
+
 static int wiz_probe(struct platform_device *pdev)
 {
        struct reset_controller_dev *phy_reset_dev;
@@ -794,8 +841,10 @@ static int wiz_probe(struct platform_device *pdev)
        }
 
        base = devm_ioremap(dev, res.start, resource_size(&res));
-       if (!base)
+       if (!base) {
+               ret = -ENOMEM;
                goto err_addr_to_resource;
+       }
 
        regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config);
        if (IS_ERR(regmap)) {
@@ -812,6 +861,7 @@ static int wiz_probe(struct platform_device *pdev)
 
        if (num_lanes > WIZ_MAX_LANES) {
                dev_err(dev, "Cannot support %d lanes\n", num_lanes);
+               ret = -ENODEV;
                goto err_addr_to_resource;
        }
 
@@ -844,6 +894,10 @@ static int wiz_probe(struct platform_device *pdev)
                }
        }
 
+       ret = wiz_get_lane_phy_types(dev, wiz);
+       if (ret)
+               return ret;
+
        wiz->dev = dev;
        wiz->regmap = regmap;
        wiz->num_lanes = num_lanes;
@@ -897,6 +951,7 @@ static int wiz_probe(struct platform_device *pdev)
        serdes_pdev = of_platform_device_create(child_node, NULL, dev);
        if (!serdes_pdev) {
                dev_WARN(dev, "Unable to create SERDES platform device\n");
+               ret = -ENOMEM;
                goto err_pdev_create;
        }
        wiz->serdes_pdev = serdes_pdev;
index 3d74629..cb2dd32 100644 (file)
@@ -1,8 +1,8 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 /*
- * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
+ * omap-usb2.c - USB PHY, talking to USB controller on TI SoCs.
  *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com
  * Author: Kishon Vijay Abraham I <kishon@ti.com>
  */
 
 #include <linux/regmap.h>
 #include <linux/of_platform.h>
 
-#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
-#define USB2PHY_ANA_CONFIG1 0x4c
+#define USB2PHY_ANA_CONFIG1            0x4c
+#define USB2PHY_DISCON_BYP_LATCH       BIT(31)
 
+/* SoC Specific USB2_OTG register definitions */
 #define AM654_USB2_OTG_PD              BIT(8)
 #define AM654_USB2_VBUS_DET_EN         BIT(5)
 #define AM654_USB2_VBUSVALID_DET_EN    BIT(4)
 
+#define OMAP_DEV_PHY_PD                BIT(0)
+#define OMAP_USB2_PHY_PD       BIT(28)
+
+#define AM437X_USB2_PHY_PD             BIT(0)
+#define AM437X_USB2_OTG_PD             BIT(1)
+#define AM437X_USB2_OTGVDET_EN         BIT(19)
+#define AM437X_USB2_OTGSESSEND_EN      BIT(20)
+
+/* Driver Flags */
+#define OMAP_USB2_HAS_START_SRP                        BIT(0)
+#define OMAP_USB2_HAS_SET_VBUS                 BIT(1)
+#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT   BIT(2)
+
+struct omap_usb {
+       struct usb_phy          phy;
+       struct phy_companion    *comparator;
+       void __iomem            *pll_ctrl_base;
+       void __iomem            *phy_base;
+       struct device           *dev;
+       struct device           *control_dev;
+       struct clk              *wkupclk;
+       struct clk              *optclk;
+       u8                      flags;
+       struct regmap           *syscon_phy_power; /* ctrl. reg. acces */
+       unsigned int            power_reg; /* power reg. index within syscon */
+       u32                     mask;
+       u32                     power_on;
+       u32                     power_off;
+};
+
+#define        phy_to_omapusb(x)       container_of((x), struct omap_usb, phy)
+
+struct usb_phy_data {
+       const char *label;
+       u8 flags;
+       u32 mask;
+       u32 power_on;
+       u32 power_off;
+};
+
+static inline u32 omap_usb_readl(void __iomem *addr, unsigned int offset)
+{
+       return __raw_readl(addr + offset);
+}
+
+static inline void omap_usb_writel(void __iomem *addr, unsigned int offset,
+                                  u32 data)
+{
+       __raw_writel(data, addr + offset);
+}
+
 /**
  * omap_usb2_set_comparator - links the comparator present in the sytem with
  *     this phy
index 1eb757e..f020107 100644 (file)
@@ -2,7 +2,6 @@
 menuconfig USB4
        tristate "Unified support for USB4 and Thunderbolt"
        depends on PCI
-       depends on X86 || COMPILE_TEST
        select APPLE_PROPERTIES if EFI_STUB && X86
        select CRC32
        select CRYPTO
index fbbe32c..ffcc8c3 100644 (file)
@@ -1633,6 +1633,15 @@ static void icm_icl_rtd3_veto(struct tb *tb, const struct icm_pkg_header *hdr)
                icm_veto_end(tb);
 }
 
+static bool icm_tgl_is_supported(struct tb *tb)
+{
+       /*
+        * If the firmware is not running use software CM. This platform
+        * should fully support both.
+        */
+       return icm_firmware_running(tb->nhi);
+}
+
 static void icm_handle_notification(struct work_struct *work)
 {
        struct icm_notification *n = container_of(work, typeof(*n), work);
@@ -2269,6 +2278,19 @@ struct tb *icm_probe(struct tb_nhi *nhi)
                icm->rtd3_veto = icm_icl_rtd3_veto;
                tb->cm_ops = &icm_icl_ops;
                break;
+
+       case PCI_DEVICE_ID_INTEL_TGL_NHI0:
+       case PCI_DEVICE_ID_INTEL_TGL_NHI1:
+               icm->is_supported = icm_tgl_is_supported;
+               icm->driver_ready = icm_icl_driver_ready;
+               icm->set_uuid = icm_icl_set_uuid;
+               icm->device_connected = icm_icl_device_connected;
+               icm->device_disconnected = icm_tr_device_disconnected;
+               icm->xdomain_connected = icm_tr_xdomain_connected;
+               icm->xdomain_disconnected = icm_tr_xdomain_disconnected;
+               icm->rtd3_veto = icm_icl_rtd3_veto;
+               tb->cm_ops = &icm_icl_ops;
+               break;
        }
 
        if (!icm->is_supported || !icm->is_supported(tb)) {
index 1be491e..d299dc1 100644 (file)
@@ -1270,6 +1270,10 @@ static struct pci_device_id nhi_ids[] = {
          .driver_data = (kernel_ulong_t)&icl_nhi_ops },
        { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ICL_NHI1),
          .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI0),
+         .driver_data = (kernel_ulong_t)&icl_nhi_ops },
+       { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_TGL_NHI1),
+         .driver_data = (kernel_ulong_t)&icl_nhi_ops },
 
        /* Any USB4 compliant host */
        { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) },
@@ -1285,6 +1289,7 @@ static struct pci_driver nhi_driver = {
        .id_table = nhi_ids,
        .probe = nhi_probe,
        .remove = nhi_remove,
+       .shutdown = nhi_remove,
        .driver.pm = &nhi_pm_ops,
 };
 
index 5d276ee..80162e4 100644 (file)
@@ -73,6 +73,8 @@ extern const struct tb_nhi_ops icl_nhi_ops;
 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_BRIDGE      0x15ef
 #define PCI_DEVICE_ID_INTEL_ICL_NHI1                   0x8a0d
 #define PCI_DEVICE_ID_INTEL_ICL_NHI0                   0x8a17
+#define PCI_DEVICE_ID_INTEL_TGL_NHI0                   0x9a1b
+#define PCI_DEVICE_ID_INTEL_TGL_NHI1                   0x9a1d
 
 #define PCI_CLASS_SERIAL_USB_USB4                      0x0c0340
 
index d92c755..d7d60cd 100644 (file)
@@ -348,12 +348,6 @@ out:
        return ret;
 }
 
-static int tb_switch_nvm_no_read(void *priv, unsigned int offset, void *val,
-                                size_t bytes)
-{
-       return -EPERM;
-}
-
 static int tb_switch_nvm_write(void *priv, unsigned int offset, void *val,
                               size_t bytes)
 {
@@ -399,7 +393,6 @@ static struct nvmem_device *register_nvmem(struct tb_switch *sw, int id,
                config.read_only = true;
        } else {
                config.name = "nvm_non_active";
-               config.reg_read = tb_switch_nvm_no_read;
                config.reg_write = tb_switch_nvm_write;
                config.root_only = true;
        }
index 5685ba1..e701ab5 100644 (file)
@@ -138,7 +138,7 @@ static int cdns_ti_probe(struct platform_device *pdev)
        error = pm_runtime_get_sync(dev);
        if (error < 0) {
                dev_err(dev, "pm_runtime_get_sync failed: %d\n", error);
-               goto err_get;
+               goto err;
        }
 
        /* assert RESET */
@@ -185,7 +185,6 @@ static int cdns_ti_probe(struct platform_device *pdev)
 
 err:
        pm_runtime_put_sync(data->dev);
-err_get:
        pm_runtime_disable(data->dev);
 
        return error;
index 4aafba2..19bbb5b 100644 (file)
@@ -82,8 +82,6 @@ static void cdns3_exit_roles(struct cdns3 *cdns)
        cdns3_drd_exit(cdns);
 }
 
-static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
-
 /**
  * cdns3_core_init_role - initialize role of operation
  * @cdns: Pointer to cdns3 structure
@@ -193,12 +191,12 @@ err:
 }
 
 /**
- * cdsn3_hw_role_state_machine  - role switch state machine based on hw events.
+ * cdns3_hw_role_state_machine  - role switch state machine based on hw events.
  * @cdns: Pointer to controller structure.
  *
  * Returns next role to be entered based on hw events.
  */
-static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
+static enum usb_role cdns3_hw_role_state_machine(struct cdns3 *cdns)
 {
        enum usb_role role;
        int id, vbus;
@@ -291,14 +289,10 @@ int cdns3_hw_role_switch(struct cdns3 *cdns)
        enum usb_role real_role, current_role;
        int ret = 0;
 
-       /* Do nothing if role based on syfs. */
-       if (cdns->role_override)
-               return 0;
-
        pm_runtime_get_sync(cdns->dev);
 
        current_role = cdns->role;
-       real_role = cdsn3_hw_role_state_machine(cdns);
+       real_role = cdns3_hw_role_state_machine(cdns);
 
        /* Do nothing if nothing changed */
        if (current_role == real_role)
@@ -353,39 +347,6 @@ static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
 
        pm_runtime_get_sync(cdns->dev);
 
-       /*
-        * FIXME: switch role framework should be extended to meet
-        * requirements. Driver assumes that role can be controlled
-        * by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
-        * switch from SW to HW control.
-        *
-        * For dr_mode == USB_DR_MODE_OTG:
-        *      if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
-        *      sets role_override flag and forces that role.
-        *      if user sets USB_ROLE_NONE, driver clears role_override and lets
-        *      HW state machine take over.
-        *
-        * For dr_mode != USB_DR_MODE_OTG:
-        *      Assumptions:
-        *      1. Restricted user control between NONE and dr_mode.
-        *      2. Driver doesn't need to rely on role_override flag.
-        *      3. Driver needs to ensure that HW state machine is never called
-        *         if dr_mode != USB_DR_MODE_OTG.
-        */
-       if (role == USB_ROLE_NONE)
-               cdns->role_override = 0;
-       else
-               cdns->role_override = 1;
-
-       /*
-        * HW state might have changed so driver need to trigger
-        * HW state machine if dr_mode == USB_DR_MODE_OTG.
-        */
-       if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
-               cdns3_hw_role_switch(cdns);
-               goto pm_put;
-       }
-
        if (cdns->role == role)
                goto pm_put;
 
@@ -528,6 +489,8 @@ static int cdns3_probe(struct platform_device *pdev)
        sw_desc.get = cdns3_role_get;
        sw_desc.allow_userspace_control = true;
        sw_desc.driver_data = cdns;
+       if (device_property_read_bool(dev, "usb-role-switch"))
+               sw_desc.fwnode = dev->fwnode;
 
        cdns->role_sw = usb_role_switch_register(dev, &sw_desc);
        if (IS_ERR(cdns->role_sw)) {
index 969eb94..1ad1f1f 100644 (file)
@@ -62,7 +62,6 @@ struct cdns3_role_driver {
  *           This field based on firmware setting, kernel configuration
  *           and hardware configuration.
  * @role_sw: pointer to role switch object.
- * @role_override: set 1 if role rely on SW.
  */
 struct cdns3 {
        struct device                   *dev;
@@ -90,7 +89,6 @@ struct cdns3 {
        struct mutex                    mutex;
        enum usb_dr_mode                dr_mode;
        struct usb_role_switch          *role_sw;
-       int                             role_override;
 };
 
 int cdns3_hw_role_switch(struct cdns3 *cdns);
index 16ad485..5808984 100644 (file)
@@ -329,7 +329,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
                cdns->otg_v1_regs = NULL;
                cdns->otg_regs = regs;
                writel(1, &cdns->otg_v0_regs->simulate);
-               dev_info(cdns->dev, "DRD version v0 (%08x)\n",
+               dev_dbg(cdns->dev, "DRD version v0 (%08x)\n",
                         readl(&cdns->otg_v0_regs->version));
        } else {
                cdns->otg_v0_regs = NULL;
@@ -337,7 +337,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
                cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
                cdns->version  = CDNS3_CONTROLLER_V1;
                writel(1, &cdns->otg_v1_regs->simulate);
-               dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
+               dev_dbg(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
                         readl(&cdns->otg_v1_regs->did),
                         readl(&cdns->otg_v1_regs->rid));
        }
index e71240b..82645a2 100644 (file)
@@ -332,13 +332,6 @@ static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
                case TEST_K:
                case TEST_SE0_NAK:
                case TEST_PACKET:
-                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
-                       /**
-                        *  Little delay to give the controller some time
-                        * for sending status stage.
-                        * This time should be less then 3ms.
-                        */
-                       mdelay(1);
                        cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
                                               USB_CMD_STMODE |
                                               USB_STS_TMODE_SEL(tmode - 1));
index 4d43f3b..5e24c2e 100644 (file)
@@ -512,8 +512,8 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
 }
 
 static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_dev,
-                                             struct cdns3_endpoint *priv_ep,
-                                             struct cdns3_request *priv_req)
+                                                    struct cdns3_endpoint *priv_ep,
+                                                    struct cdns3_request *priv_req)
 {
        if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
            priv_req->flags & REQUEST_INTERNAL) {
@@ -552,8 +552,8 @@ static struct usb_request *cdns3_wa2_gadget_giveback(struct cdns3_device *priv_d
 }
 
 static int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
-                             struct cdns3_endpoint *priv_ep,
-                             struct cdns3_request *priv_req)
+                                    struct cdns3_endpoint *priv_ep,
+                                    struct cdns3_request *priv_req)
 {
        int deferred = 0;
 
@@ -1905,7 +1905,7 @@ static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
 }
 
 static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
-                             struct cdns3_endpoint *priv_ep)
+                                    struct cdns3_endpoint *priv_ep)
 {
        if (!priv_ep->use_streams || priv_dev->gadget.speed < USB_SPEED_SUPER)
                return;
@@ -1926,7 +1926,7 @@ static void cdns3_stream_ep_reconfig(struct cdns3_device *priv_dev,
 }
 
 static void cdns3_configure_dmult(struct cdns3_device *priv_dev,
-                          struct cdns3_endpoint *priv_ep)
+                                 struct cdns3_endpoint *priv_ep)
 {
        struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
 
@@ -2965,7 +2965,7 @@ static int cdns3_init_eps(struct cdns3_device *priv_dev)
 
                priv_ep->flags = 0;
 
-               dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
+               dev_dbg(priv_dev->dev, "Initialized  %s support: %s %s\n",
                         priv_ep->name,
                         priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
                         priv_ep->endpoint.caps.type_iso ? "ISO" : "");
@@ -3069,6 +3069,7 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
        priv_dev->gadget.name = "usb-ss-gadget";
        priv_dev->gadget.sg_supported = 1;
        priv_dev->gadget.quirk_avoids_skb_reserve = 1;
+       priv_dev->gadget.irq = cdns->dev_irq;
 
        spin_lock_init(&priv_dev->lock);
        INIT_WORK(&priv_dev->pending_status_wq,
index d53db52..8bafcfc 100644 (file)
@@ -18,17 +18,6 @@ config USB_CHIPIDEA
 
 if USB_CHIPIDEA
 
-config USB_CHIPIDEA_OF
-       tristate
-       depends on OF
-       default USB_CHIPIDEA
-
-config USB_CHIPIDEA_PCI
-       tristate
-       depends on USB_PCI
-       depends on NOP_USB_XCEIV
-       default USB_CHIPIDEA
-
 config USB_CHIPIDEA_UDC
        bool "ChipIdea device controller"
        depends on USB_GADGET
@@ -43,4 +32,30 @@ config USB_CHIPIDEA_HOST
        help
          Say Y here to enable host controller functionality of the
          ChipIdea driver.
+
+config USB_CHIPIDEA_PCI
+       tristate "Enable PCI glue driver" if EMBEDDED
+       depends on USB_PCI
+       depends on NOP_USB_XCEIV
+       default USB_CHIPIDEA
+
+config USB_CHIPIDEA_MSM
+       tristate "Enable MSM hsusb glue driver" if EMBEDDED
+       default USB_CHIPIDEA
+
+config USB_CHIPIDEA_IMX
+       tristate "Enable i.MX USB glue driver" if EMBEDDED
+       depends on OF
+       default USB_CHIPIDEA
+
+config USB_CHIPIDEA_GENERIC
+       tristate "Enable generic USB2 glue driver" if EMBEDDED
+       default USB_CHIPIDEA
+
+config USB_CHIPIDEA_TEGRA
+       tristate "Enable Tegra UDC glue driver" if EMBEDDED
+       depends on OF
+       depends on USB_CHIPIDEA_UDC
+       default USB_CHIPIDEA
+
 endif
index 12df94f..fae779a 100644 (file)
@@ -8,11 +8,8 @@ ci_hdrc-$(CONFIG_USB_OTG_FSM)          += otg_fsm.o
 
 # Glue/Bridge layers go here
 
-obj-$(CONFIG_USB_CHIPIDEA)     += ci_hdrc_usb2.o
-obj-$(CONFIG_USB_CHIPIDEA)     += ci_hdrc_msm.o
-obj-$(CONFIG_USB_CHIPIDEA)     += ci_hdrc_zevio.o
-
-obj-$(CONFIG_USB_CHIPIDEA_PCI) += ci_hdrc_pci.o
-
-obj-$(CONFIG_USB_CHIPIDEA_OF)  += usbmisc_imx.o ci_hdrc_imx.o
-obj-$(CONFIG_USB_CHIPIDEA_OF)  += ci_hdrc_tegra.o
+obj-$(CONFIG_USB_CHIPIDEA_GENERIC)     += ci_hdrc_usb2.o
+obj-$(CONFIG_USB_CHIPIDEA_MSM)         += ci_hdrc_msm.o
+obj-$(CONFIG_USB_CHIPIDEA_PCI)         += ci_hdrc_pci.o
+obj-$(CONFIG_USB_CHIPIDEA_IMX)         += ci_hdrc_imx.o usbmisc_imx.o
+obj-$(CONFIG_USB_CHIPIDEA_TEGRA)       += ci_hdrc_tegra.o
index 644ecae..0697eb9 100644 (file)
@@ -25,6 +25,7 @@
 #define TD_PAGE_COUNT      5
 #define CI_HDRC_PAGE_SIZE  4096ul /* page size for TD's */
 #define ENDPT_MAX          32
+#define CI_MAX_BUF_SIZE        (TD_PAGE_COUNT * CI_HDRC_PAGE_SIZE)
 
 /******************************************************************************
  * REGISTERS
index a479af3..5ae1636 100644 (file)
@@ -271,6 +271,7 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
        struct device *dev = ci->dev->parent;
        struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
        int ret = 0;
+       struct imx_usbmisc_data *mdata = data->usbmisc_data;
 
        switch (event) {
        case CI_HDRC_IMX_HSIC_ACTIVE_EVENT:
@@ -284,11 +285,19 @@ static int ci_hdrc_imx_notify_event(struct ci_hdrc *ci, unsigned int event)
                }
                break;
        case CI_HDRC_IMX_HSIC_SUSPEND_EVENT:
-               ret = imx_usbmisc_hsic_set_connect(data->usbmisc_data);
+               ret = imx_usbmisc_hsic_set_connect(mdata);
                if (ret)
                        dev_err(dev,
                                "hsic_set_connect failed, err=%d\n", ret);
                break;
+       case CI_HDRC_CONTROLLER_VBUS_EVENT:
+               if (ci->vbus_active)
+                       ret = imx_usbmisc_charger_detection(mdata, true);
+               else
+                       ret = imx_usbmisc_charger_detection(mdata, false);
+               if (ci->usb_phy)
+                       schedule_work(&ci->usb_phy->chg_work);
+               break;
        default:
                break;
        }
@@ -414,6 +423,8 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev)
        }
 
        pdata.usb_phy = data->phy;
+       if (data->usbmisc_data)
+               data->usbmisc_data->usb_phy = data->phy;
 
        if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
             of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
index c2051ae..727d02b 100644 (file)
@@ -24,6 +24,7 @@ struct imx_usbmisc_data {
        unsigned int hsic:1; /* HSIC controlller */
        unsigned int ext_id:1; /* ID from exteranl event */
        unsigned int ext_vbus:1; /* Vbus from exteranl event */
+       struct usb_phy *usb_phy;
 };
 
 int imx_usbmisc_init(struct imx_usbmisc_data *data);
@@ -31,5 +32,6 @@ int imx_usbmisc_init_post(struct imx_usbmisc_data *data);
 int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled);
 int imx_usbmisc_hsic_set_connect(struct imx_usbmisc_data *data);
 int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on);
+int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect);
 
 #endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
index c044fba..89e1d82 100644 (file)
@@ -28,13 +28,19 @@ static const struct ci_hdrc_platform_data ci_default_pdata = {
        .flags          = CI_HDRC_DISABLE_STREAMING,
 };
 
-static struct ci_hdrc_platform_data ci_zynq_pdata = {
+static const struct ci_hdrc_platform_data ci_zynq_pdata = {
        .capoffset      = DEF_CAPOFFSET,
 };
 
+static const struct ci_hdrc_platform_data ci_zevio_pdata = {
+       .capoffset      = DEF_CAPOFFSET,
+       .flags          = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
+};
+
 static const struct of_device_id ci_hdrc_usb2_of_match[] = {
-       { .compatible = "chipidea,usb2"},
-       { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata},
+       { .compatible = "chipidea,usb2" },
+       { .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata },
+       { .compatible = "lsi,zevio-usb", .data = &ci_zevio_pdata },
        { }
 };
 MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
@@ -64,13 +70,14 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
        if (!priv)
                return -ENOMEM;
 
-       priv->clk = devm_clk_get(dev, NULL);
-       if (!IS_ERR(priv->clk)) {
-               ret = clk_prepare_enable(priv->clk);
-               if (ret) {
-                       dev_err(dev, "failed to enable the clock: %d\n", ret);
-                       return ret;
-               }
+       priv->clk = devm_clk_get_optional(dev, NULL);
+       if (IS_ERR(priv->clk))
+               return PTR_ERR(priv->clk);
+
+       ret = clk_prepare_enable(priv->clk);
+       if (ret) {
+               dev_err(dev, "failed to enable the clock: %d\n", ret);
+               return ret;
        }
 
        ci_pdata->name = dev_name(dev);
@@ -94,8 +101,7 @@ static int ci_hdrc_usb2_probe(struct platform_device *pdev)
        return 0;
 
 clk_err:
-       if (!IS_ERR(priv->clk))
-               clk_disable_unprepare(priv->clk);
+       clk_disable_unprepare(priv->clk);
        return ret;
 }
 
diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c
deleted file mode 100644 (file)
index e1634da..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- *     Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
- *
- * Based off drivers/usb/chipidea/ci_hdrc_msm.c
- */
-
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/usb/gadget.h>
-#include <linux/usb/chipidea.h>
-
-#include "ci.h"
-
-static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
-       .name                   = "ci_hdrc_zevio",
-       .flags                  = CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
-       .capoffset              = DEF_CAPOFFSET,
-};
-
-static int ci_hdrc_zevio_probe(struct platform_device *pdev)
-{
-       struct platform_device *ci_pdev;
-
-       dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n");
-
-       ci_pdev = ci_hdrc_add_device(&pdev->dev,
-                               pdev->resource, pdev->num_resources,
-                               &ci_hdrc_zevio_platdata);
-
-       if (IS_ERR(ci_pdev)) {
-               dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
-               return PTR_ERR(ci_pdev);
-       }
-
-       platform_set_drvdata(pdev, ci_pdev);
-
-       return 0;
-}
-
-static int ci_hdrc_zevio_remove(struct platform_device *pdev)
-{
-       struct platform_device *ci_pdev = platform_get_drvdata(pdev);
-
-       ci_hdrc_remove_device(ci_pdev);
-
-       return 0;
-}
-
-static const struct of_device_id ci_hdrc_zevio_dt_ids[] = {
-       { .compatible = "lsi,zevio-usb", },
-       { /* sentinel */ }
-};
-
-static struct platform_driver ci_hdrc_zevio_driver = {
-       .probe = ci_hdrc_zevio_probe,
-       .remove = ci_hdrc_zevio_remove,
-       .driver = {
-               .name = "zevio_usb",
-               .of_match_table = ci_hdrc_zevio_dt_ids,
-       },
-};
-
-MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids);
-module_platform_driver(ci_hdrc_zevio_driver);
-
-MODULE_LICENSE("GPL v2");
index ae0bdc0..9a7c53d 100644 (file)
@@ -3,42 +3,16 @@
  * core.c - ChipIdea USB IP core family device controller
  *
  * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ * Copyright (C) 2020 NXP
  *
  * Author: David Lopo
- */
-
-/*
- * Description: ChipIdea USB IP core family device controller
- *
- * This driver is composed of several blocks:
- * - HW:     hardware interface
- * - DBG:    debug facilities (optional)
- * - UTIL:   utilities
- * - ISR:    interrupts handling
- * - ENDPT:  endpoint operations (Gadget API)
- * - GADGET: gadget operations (Gadget API)
- * - BUS:    bus glue code, bus abstraction layer
+ *        Peter Chen <peter.chen@nxp.com>
  *
- * Compile Options
- * - STALL_IN:  non-empty bulk-in pipes cannot be halted
- *              if defined mass storage compliance succeeds but with warnings
- *              => case 4: Hi >  Dn
- *              => case 5: Hi >  Di
- *              => case 8: Hi <> Do
- *              if undefined usbtest 13 fails
- * - TRACE:     enable function tracing (depends on DEBUG)
- *
- * Main Features
- * - Chapter 9 & Mass Storage Compliance with Gadget File Storage
- * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
- * - Normal & LPM support
- *
- * USBTEST Report
- * - OK: 0-12, 13 (STALL_IN defined) & 14
- * - Not Supported: 15 & 16 (ISO)
- *
- * TODO List
- * - Suspend & Remote Wakeup
+ * Main Features:
+ * - Four transfers are supported, usbtest is passed
+ * - USB Certification for gadget: CH9 and Mass Storage are passed
+ * - Low power mode
+ * - USB wakeup
  */
 #include <linux/delay.h>
 #include <linux/device.h>
@@ -272,7 +246,7 @@ static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
        ci->rev = ci_get_revision(ci);
 
        dev_dbg(ci->dev,
-               "ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n",
+               "revision: %d, lpm: %d; cap: %px op: %px\n",
                ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
 
        /* setup lock mode ? */
@@ -666,6 +640,7 @@ static int ci_usb_role_switch_set(struct usb_role_switch *sw,
 static struct usb_role_switch_desc ci_role_switch = {
        .set = ci_usb_role_switch_set,
        .get = ci_usb_role_switch_get,
+       .allow_userspace_control = true,
 };
 
 static int ci_get_platdata(struct device *dev,
@@ -1149,8 +1124,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
 
        if (!ci_otg_is_fsm_mode(ci)) {
                /* only update vbus status for peripheral */
-               if (ci->role == CI_ROLE_GADGET)
+               if (ci->role == CI_ROLE_GADGET) {
+                       /* Pull down DP for possible charger detection */
+                       hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
                        ci_handle_vbus_change(ci);
+               }
 
                ret = ci_role_start(ci, ci->role);
                if (ret) {
index 921bcf1..db0cfde 100644 (file)
@@ -338,7 +338,7 @@ static int hw_usb_reset(struct ci_hdrc *ci)
  *****************************************************************************/
 
 static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
-                         unsigned length)
+                       unsigned int length, struct scatterlist *s)
 {
        int i;
        u32 temp;
@@ -366,7 +366,13 @@ static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
                node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
        }
 
-       temp = (u32) (hwreq->req.dma + hwreq->req.actual);
+       if (s) {
+               temp = (u32) (sg_dma_address(s) + hwreq->req.actual);
+               node->td_remaining_size = CI_MAX_BUF_SIZE - length;
+       } else {
+               temp = (u32) (hwreq->req.dma + hwreq->req.actual);
+       }
+
        if (length) {
                node->ptr->page[0] = cpu_to_le32(temp);
                for (i = 1; i < TD_PAGE_COUNT; i++) {
@@ -400,6 +406,122 @@ static inline u8 _usb_addr(struct ci_hw_ep *ep)
        return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
 }
 
+static int prepare_td_for_non_sg(struct ci_hw_ep *hwep,
+               struct ci_hw_req *hwreq)
+{
+       unsigned int rest = hwreq->req.length;
+       int pages = TD_PAGE_COUNT;
+       int ret = 0;
+
+       if (rest == 0) {
+               ret = add_td_to_list(hwep, hwreq, 0, NULL);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /*
+        * The first buffer could be not page aligned.
+        * In that case we have to span into one extra td.
+        */
+       if (hwreq->req.dma % PAGE_SIZE)
+               pages--;
+
+       while (rest > 0) {
+               unsigned int count = min(hwreq->req.length - hwreq->req.actual,
+                       (unsigned int)(pages * CI_HDRC_PAGE_SIZE));
+
+               ret = add_td_to_list(hwep, hwreq, count, NULL);
+               if (ret < 0)
+                       return ret;
+
+               rest -= count;
+       }
+
+       if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
+           && (hwreq->req.length % hwep->ep.maxpacket == 0)) {
+               ret = add_td_to_list(hwep, hwreq, 0, NULL);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return ret;
+}
+
+static int prepare_td_per_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
+               struct scatterlist *s)
+{
+       unsigned int rest = sg_dma_len(s);
+       int ret = 0;
+
+       hwreq->req.actual = 0;
+       while (rest > 0) {
+               unsigned int count = min_t(unsigned int, rest,
+                               CI_MAX_BUF_SIZE);
+
+               ret = add_td_to_list(hwep, hwreq, count, s);
+               if (ret < 0)
+                       return ret;
+
+               rest -= count;
+       }
+
+       return ret;
+}
+
+static void ci_add_buffer_entry(struct td_node *node, struct scatterlist *s)
+{
+       int empty_td_slot_index = (CI_MAX_BUF_SIZE - node->td_remaining_size)
+                       / CI_HDRC_PAGE_SIZE;
+       int i;
+
+       node->ptr->token +=
+               cpu_to_le32(sg_dma_len(s) << __ffs(TD_TOTAL_BYTES));
+
+       for (i = empty_td_slot_index; i < TD_PAGE_COUNT; i++) {
+               u32 page = (u32) sg_dma_address(s) +
+                       (i - empty_td_slot_index) * CI_HDRC_PAGE_SIZE;
+
+               page &= ~TD_RESERVED_MASK;
+               node->ptr->page[i] = cpu_to_le32(page);
+       }
+}
+
+static int prepare_td_for_sg(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
+{
+       struct usb_request *req = &hwreq->req;
+       struct scatterlist *s = req->sg;
+       int ret = 0, i = 0;
+       struct td_node *node = NULL;
+
+       if (!s || req->zero || req->length == 0) {
+               dev_err(hwep->ci->dev, "not supported operation for sg\n");
+               return -EINVAL;
+       }
+
+       while (i++ < req->num_mapped_sgs) {
+               if (sg_dma_address(s) % PAGE_SIZE) {
+                       dev_err(hwep->ci->dev, "not page aligned sg buffer\n");
+                       return -EINVAL;
+               }
+
+               if (node && (node->td_remaining_size >= sg_dma_len(s))) {
+                       ci_add_buffer_entry(node, s);
+                       node->td_remaining_size -= sg_dma_len(s);
+               } else {
+                       ret = prepare_td_per_sg(hwep, hwreq, s);
+                       if (ret)
+                               return ret;
+
+                       node = list_entry(hwreq->tds.prev,
+                               struct td_node, td);
+               }
+
+               s = sg_next(s);
+       }
+
+       return ret;
+}
+
 /**
  * _hardware_enqueue: configures a request at hardware level
  * @hwep:   endpoint
@@ -411,8 +533,6 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
 {
        struct ci_hdrc *ci = hwep->ci;
        int ret = 0;
-       unsigned rest = hwreq->req.length;
-       int pages = TD_PAGE_COUNT;
        struct td_node *firstnode, *lastnode;
 
        /* don't queue twice */
@@ -426,35 +546,13 @@ static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
        if (ret)
                return ret;
 
-       /*
-        * The first buffer could be not page aligned.
-        * In that case we have to span into one extra td.
-        */
-       if (hwreq->req.dma % PAGE_SIZE)
-               pages--;
-
-       if (rest == 0) {
-               ret = add_td_to_list(hwep, hwreq, 0);
-               if (ret < 0)
-                       goto done;
-       }
-
-       while (rest > 0) {
-               unsigned count = min(hwreq->req.length - hwreq->req.actual,
-                                       (unsigned)(pages * CI_HDRC_PAGE_SIZE));
-               ret = add_td_to_list(hwep, hwreq, count);
-               if (ret < 0)
-                       goto done;
-
-               rest -= count;
-       }
+       if (hwreq->req.num_mapped_sgs)
+               ret = prepare_td_for_sg(hwep, hwreq);
+       else
+               ret = prepare_td_for_non_sg(hwep, hwreq);
 
-       if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
-           && (hwreq->req.length % hwep->ep.maxpacket == 0)) {
-               ret = add_td_to_list(hwep, hwreq, 0);
-               if (ret < 0)
-                       goto done;
-       }
+       if (ret)
+               return ret;
 
        firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
 
@@ -1561,6 +1659,7 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
 {
        struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
        unsigned long flags;
+       int ret = 0;
 
        spin_lock_irqsave(&ci->lock, flags);
        ci->vbus_active = is_active;
@@ -1570,10 +1669,14 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
                usb_phy_set_charger_state(ci->usb_phy, is_active ?
                        USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
 
+       if (ci->platdata->notify_event)
+               ret = ci->platdata->notify_event(ci,
+                               CI_HDRC_CONTROLLER_VBUS_EVENT);
+
        if (ci->driver)
                ci_hdrc_gadget_connect(_gadget, is_active);
 
-       return 0;
+       return ret;
 }
 
 static int ci_udc_wakeup(struct usb_gadget *_gadget)
@@ -1936,6 +2039,7 @@ static int udc_start(struct ci_hdrc *ci)
        ci->gadget.max_speed    = USB_SPEED_HIGH;
        ci->gadget.name         = ci->platdata->name;
        ci->gadget.otg_caps     = otg_caps;
+       ci->gadget.sg_supported = 1;
 
        if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA)
                ci->gadget.quirk_avoids_skb_reserve = 1;
index ebb11b6..5193df1 100644 (file)
@@ -61,16 +61,14 @@ struct td_node {
        struct list_head        td;
        dma_addr_t              dma;
        struct ci_hw_td         *ptr;
+       int                     td_remaining_size;
 };
 
 /**
  * struct ci_hw_req - usb request representation
  * @req: request structure for gadget drivers
  * @queue: link to QH list
- * @ptr: transfer descriptor for this request
- * @dma: dma address for the transfer descriptor
- * @zptr: transfer descriptor for the zero packet
- * @zdma: dma address of the zero packet's transfer descriptor
+ * @tds: link to TD list
  */
 struct ci_hw_req {
        struct usb_request      req;
index e81e33c..f136876 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/delay.h>
+#include <linux/usb/otg.h>
 
 #include "ci_hdrc_imx.h"
 
 #define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID     MX7D_USB_VBUS_WAKEUP_SOURCE(1)
 #define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID     MX7D_USB_VBUS_WAKEUP_SOURCE(2)
 #define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END   MX7D_USB_VBUS_WAKEUP_SOURCE(3)
+#define MX7D_USBNC_AUTO_RESUME                         BIT(2)
+/* The default DM/DP value is pull-down */
+#define MX7D_USBNC_USB_CTRL2_OPMODE(v)                 (v << 6)
+#define MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING        MX7D_USBNC_USB_CTRL2_OPMODE(1)
+#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK      (BIT(7) | BIT(6))
+#define MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN                BIT(8)
+#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_VAL           BIT(12)
+#define MX7D_USBNC_USB_CTRL2_DP_OVERRIDE_EN            BIT(13)
+#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_VAL           BIT(14)
+#define MX7D_USBNC_USB_CTRL2_DM_OVERRIDE_EN            BIT(15)
+#define MX7D_USBNC_USB_CTRL2_DP_DM_MASK                        (BIT(12) | BIT(13) | \
+                                                       BIT(14) | BIT(15))
+
+#define MX7D_USB_OTG_PHY_CFG1          0x30
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL     BIT(0)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 BIT(1)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 BIT(2)
+#define MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB      BIT(3)
+#define MX7D_USB_OTG_PHY_CFG2_DRVVBUS0         BIT(16)
+
+#define MX7D_USB_OTG_PHY_CFG2          0x34
+
+#define MX7D_USB_OTG_PHY_STATUS                0x3c
+#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE0    BIT(0)
+#define MX7D_USB_OTG_PHY_STATUS_LINE_STATE1    BIT(1)
+#define MX7D_USB_OTG_PHY_STATUS_VBUS_VLD       BIT(3)
+#define MX7D_USB_OTG_PHY_STATUS_CHRGDET                BIT(29)
 
 #define MX6_USB_OTG_WAKEUP_BITS (MX6_BM_WAKEUP_ENABLE | MX6_BM_VBUS_WAKEUP | \
                                 MX6_BM_ID_WAKEUP)
@@ -114,6 +142,8 @@ struct usbmisc_ops {
        int (*hsic_set_connect)(struct imx_usbmisc_data *data);
        /* It's called during suspend/resume */
        int (*hsic_set_clk)(struct imx_usbmisc_data *data, bool enabled);
+       /* usb charger detection */
+       int (*charger_detection)(struct imx_usbmisc_data *data);
 };
 
 struct imx_usbmisc {
@@ -609,10 +639,263 @@ static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
                reg |= MX6_BM_PWR_POLARITY;
        writel(reg, usbmisc->base);
 
-       reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
-       reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
-       writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
-                usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       /* SoC non-burst setting */
+       reg = readl(usbmisc->base);
+       writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
+
+       if (!data->hsic) {
+               reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+               reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
+               writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID
+                       | MX7D_USBNC_AUTO_RESUME,
+                       usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       }
+
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+       usbmisc_imx7d_set_wakeup(data, false);
+
+       return 0;
+}
+
+static int imx7d_charger_secondary_detection(struct imx_usbmisc_data *data)
+{
+       struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+       struct usb_phy *usb_phy = data->usb_phy;
+       int val;
+       unsigned long flags;
+
+       /* VDM_SRC is connected to D- and IDP_SINK is connected to D+ */
+       spin_lock_irqsave(&usbmisc->lock, flags);
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+                       MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
+                       MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL,
+                               usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+       usleep_range(1000, 2000);
+
+       /*
+        * Per BC 1.2, check voltage of D+:
+        * DCP: if greater than VDAT_REF;
+        * CDP: if less than VDAT_REF.
+        */
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+       if (val & MX7D_USB_OTG_PHY_STATUS_CHRGDET) {
+               dev_dbg(data->dev, "It is a dedicate charging port\n");
+               usb_phy->chg_type = DCP_TYPE;
+       } else {
+               dev_dbg(data->dev, "It is a charging downstream port\n");
+               usb_phy->chg_type = CDP_TYPE;
+       }
+
+       return 0;
+}
+
+static void imx7_disable_charger_detector(struct imx_usbmisc_data *data)
+{
+       struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+       unsigned long flags;
+       u32 val;
+
+       spin_lock_irqsave(&usbmisc->lock, flags);
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       val &= ~(MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB |
+                       MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+                       MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0 |
+                       MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL);
+       writel(val, usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+
+       /* Set OPMODE to be 2'b00 and disable its override */
+       val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+       writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+
+       val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       writel(val & ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
+                       usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+}
+
+static int imx7d_charger_data_contact_detect(struct imx_usbmisc_data *data)
+{
+       struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+       unsigned long flags;
+       u32 val;
+       int i, data_pin_contact_count = 0;
+
+       /* Enable Data Contact Detect (DCD) per the USB BC 1.2 */
+       spin_lock_irqsave(&usbmisc->lock, flags);
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
+                       usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+       for (i = 0; i < 100; i = i + 1) {
+               val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+               if (!(val & MX7D_USB_OTG_PHY_STATUS_LINE_STATE0)) {
+                       if (data_pin_contact_count++ > 5)
+                               /* Data pin makes contact */
+                               break;
+                       usleep_range(5000, 10000);
+               } else {
+                       data_pin_contact_count = 0;
+                       usleep_range(5000, 6000);
+               }
+       }
+
+       /* Disable DCD after finished data contact check */
+       spin_lock_irqsave(&usbmisc->lock, flags);
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       writel(val & ~MX7D_USB_OTG_PHY_CFG2_CHRG_DCDENB,
+                       usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+       if (i == 100) {
+               dev_err(data->dev,
+                       "VBUS is coming from a dedicated power supply.\n");
+               return -ENXIO;
+       }
+
+       return 0;
+}
+
+static int imx7d_charger_primary_detection(struct imx_usbmisc_data *data)
+{
+       struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+       struct usb_phy *usb_phy = data->usb_phy;
+       unsigned long flags;
+       u32 val;
+
+       /* VDP_SRC is connected to D+ and IDM_SINK is connected to D- */
+       spin_lock_irqsave(&usbmisc->lock, flags);
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       val &= ~MX7D_USB_OTG_PHY_CFG2_CHRG_CHRGSEL;
+       writel(val | MX7D_USB_OTG_PHY_CFG2_CHRG_VDATSRCENB0 |
+                       MX7D_USB_OTG_PHY_CFG2_CHRG_VDATDETENB0,
+                               usbmisc->base + MX7D_USB_OTG_PHY_CFG2);
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+       usleep_range(1000, 2000);
+
+       /* Check if D- is less than VDAT_REF to determine an SDP per BC 1.2 */
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+       if (!(val & MX7D_USB_OTG_PHY_STATUS_CHRGDET)) {
+               dev_dbg(data->dev, "It is a standard downstream port\n");
+               usb_phy->chg_type = SDP_TYPE;
+       }
+
+       return 0;
+}
+
+/**
+ * Whole charger detection process:
+ * 1. OPMODE override to be non-driving
+ * 2. Data contact check
+ * 3. Primary detection
+ * 4. Secondary detection
+ * 5. Disable charger detection
+ */
+static int imx7d_charger_detection(struct imx_usbmisc_data *data)
+{
+       struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+       struct usb_phy *usb_phy = data->usb_phy;
+       unsigned long flags;
+       u32 val;
+       int ret;
+
+       /* Check if vbus is valid */
+       val = readl(usbmisc->base + MX7D_USB_OTG_PHY_STATUS);
+       if (!(val & MX7D_USB_OTG_PHY_STATUS_VBUS_VLD)) {
+               dev_err(data->dev, "vbus is error\n");
+               return -EINVAL;
+       }
+
+       /*
+        * Keep OPMODE to be non-driving mode during the whole
+        * charger detection process.
+        */
+       spin_lock_irqsave(&usbmisc->lock, flags);
+       val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       val &= ~MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_MASK;
+       val |= MX7D_USBNC_USB_CTRL2_OPMODE_NON_DRIVING;
+       writel(val, usbmisc->base + MX7D_USBNC_USB_CTRL2);
+
+       val = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       writel(val | MX7D_USBNC_USB_CTRL2_OPMODE_OVERRIDE_EN,
+                       usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+       ret = imx7d_charger_data_contact_detect(data);
+       if (ret)
+               return ret;
+
+       ret = imx7d_charger_primary_detection(data);
+       if (!ret && usb_phy->chg_type != SDP_TYPE)
+               ret = imx7d_charger_secondary_detection(data);
+
+       imx7_disable_charger_detector(data);
+
+       return ret;
+}
+
+static int usbmisc_imx7ulp_init(struct imx_usbmisc_data *data)
+{
+       struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+       unsigned long flags;
+       u32 reg;
+
+       if (data->index >= 1)
+               return -EINVAL;
+
+       spin_lock_irqsave(&usbmisc->lock, flags);
+       reg = readl(usbmisc->base);
+       if (data->disable_oc) {
+               reg |= MX6_BM_OVER_CUR_DIS;
+       } else {
+               reg &= ~MX6_BM_OVER_CUR_DIS;
+
+               /*
+                * If the polarity is not configured keep it as setup by the
+                * bootloader.
+                */
+               if (data->oc_pol_configured && data->oc_pol_active_low)
+                       reg |= MX6_BM_OVER_CUR_POLARITY;
+               else if (data->oc_pol_configured)
+                       reg &= ~MX6_BM_OVER_CUR_POLARITY;
+       }
+       /* If the polarity is not set keep it as setup by the bootlader */
+       if (data->pwr_pol == 1)
+               reg |= MX6_BM_PWR_POLARITY;
+
+       writel(reg, usbmisc->base);
+
+       /* SoC non-burst setting */
+       reg = readl(usbmisc->base);
+       writel(reg | MX6_BM_NON_BURST_SETTING, usbmisc->base);
+
+       if (data->hsic) {
+               reg = readl(usbmisc->base);
+               writel(reg | MX6_BM_UTMI_ON_CLOCK, usbmisc->base);
+
+               reg = readl(usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
+               reg |= MX6_BM_HSIC_EN | MX6_BM_HSIC_CLK_ON;
+               writel(reg, usbmisc->base + MX6_USB_HSIC_CTRL_OFFSET);
+
+               /*
+                * For non-HSIC controller, the autoresume is enabled
+                * at MXS PHY driver (usbphy_ctrl bit18).
+                */
+               reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+               writel(reg | MX7D_USBNC_AUTO_RESUME,
+                       usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       } else {
+               reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+               reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
+               writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
+                        usbmisc->base + MX7D_USBNC_USB_CTRL2);
+       }
 
        spin_unlock_irqrestore(&usbmisc->lock, flags);
 
@@ -659,6 +942,14 @@ static const struct usbmisc_ops imx6sx_usbmisc_ops = {
 static const struct usbmisc_ops imx7d_usbmisc_ops = {
        .init = usbmisc_imx7d_init,
        .set_wakeup = usbmisc_imx7d_set_wakeup,
+       .charger_detection = imx7d_charger_detection,
+};
+
+static const struct usbmisc_ops imx7ulp_usbmisc_ops = {
+       .init = usbmisc_imx7ulp_init,
+       .set_wakeup = usbmisc_imx7d_set_wakeup,
+       .hsic_set_connect = usbmisc_imx6_hsic_set_connect,
+       .hsic_set_clk = usbmisc_imx6_hsic_set_clk,
 };
 
 static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
@@ -737,6 +1028,39 @@ int imx_usbmisc_hsic_set_clk(struct imx_usbmisc_data *data, bool on)
        return usbmisc->ops->hsic_set_clk(data, on);
 }
 EXPORT_SYMBOL_GPL(imx_usbmisc_hsic_set_clk);
+
+int imx_usbmisc_charger_detection(struct imx_usbmisc_data *data, bool connect)
+{
+       struct imx_usbmisc *usbmisc;
+       struct usb_phy *usb_phy;
+       int ret = 0;
+
+       if (!data)
+               return -EINVAL;
+
+       usbmisc = dev_get_drvdata(data->dev);
+       usb_phy = data->usb_phy;
+       if (!usbmisc->ops->charger_detection)
+               return -ENOTSUPP;
+
+       if (connect) {
+               ret = usbmisc->ops->charger_detection(data);
+               if (ret) {
+                       dev_err(data->dev,
+                                       "Error occurs during detection: %d\n",
+                                       ret);
+                       usb_phy->chg_state = USB_CHARGER_ABSENT;
+               } else {
+                       usb_phy->chg_state = USB_CHARGER_PRESENT;
+               }
+       } else {
+               usb_phy->chg_state = USB_CHARGER_ABSENT;
+               usb_phy->chg_type = UNKNOWN_TYPE;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_charger_detection);
+
 static const struct of_device_id usbmisc_imx_dt_ids[] = {
        {
                .compatible = "fsl,imx25-usbmisc",
@@ -780,7 +1104,7 @@ static const struct of_device_id usbmisc_imx_dt_ids[] = {
        },
        {
                .compatible = "fsl,imx7ulp-usbmisc",
-               .data = &imx7d_usbmisc_ops,
+               .data = &imx7ulp_usbmisc_ops,
        },
        { /* sentinel */ }
 };
index ded8d93..f67088b 100644 (file)
@@ -584,7 +584,7 @@ static void acm_softint(struct work_struct *work)
        }
 
        if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) {
-               for (i = 0; i < ACM_NR; i++) 
+               for (i = 0; i < acm->rx_buflimit; i++)
                        if (test_and_clear_bit(i, &acm->urbs_in_error_delay))
                                        acm_submit_read_urb(acm, i, GFP_NOIO);
        }
index 0d8e3f3..084c48c 100644 (file)
@@ -468,7 +468,8 @@ static int usblp_release(struct inode *inode, struct file *file)
        usb_autopm_put_interface(usblp->intf);
 
        if (!usblp->present)            /* finish cleanup from disconnect */
-               usblp_cleanup(usblp);
+               usblp_cleanup(usblp);   /* any URBs must be dead */
+
        mutex_unlock(&usblp_mutex);
        return 0;
 }
@@ -1375,9 +1376,11 @@ static void usblp_disconnect(struct usb_interface *intf)
 
        usblp_unlink_urbs(usblp);
        mutex_unlock(&usblp->mut);
+       usb_poison_anchored_urbs(&usblp->urbs);
 
        if (!usblp->used)
                usblp_cleanup(usblp);
+
        mutex_unlock(&usblp_mutex);
 }
 
index f0a2599..1547aa6 100644 (file)
@@ -159,6 +159,7 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
  * usb_hcd_pci_probe - initialize PCI-based HCDs
  * @dev: USB Host Controller being probed
  * @id: pci hotplug id connecting controller to HCD framework
+ * @driver: USB HC driver handle
  * Context: !in_interrupt()
  *
  * Allocates basic PCI resources for this USB host controller, and
@@ -169,9 +170,9 @@ static void ehci_wait_for_companions(struct pci_dev *pdev, struct usb_hcd *hcd,
  *
  * Return: 0 if successful.
  */
-int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id,
+                     const struct hc_driver *driver)
 {
-       struct hc_driver        *driver;
        struct usb_hcd          *hcd;
        int                     retval;
        int                     hcd_irq = 0;
@@ -181,7 +182,7 @@ int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 
        if (!id)
                return -EINVAL;
-       driver = (struct hc_driver *)id->driver_data;
+
        if (!driver)
                return -EINVAL;
 
index fc748c7..b1e14be 100644 (file)
@@ -93,7 +93,7 @@ module_param(old_scheme_first, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(old_scheme_first,
                 "start with the old device initialization scheme");
 
-static bool use_both_schemes = 1;
+static bool use_both_schemes = true;
 module_param(use_both_schemes, bool, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(use_both_schemes,
                "try the other device initialization scheme if the "
index a97dd1b..73f4482 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * usb hub driver head file
  *
index 2ae9015..fdd4897 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * drivers/usb/core/otg_whitelist.h
  *
index 9f4320b..a2ca38e 100644 (file)
@@ -1262,8 +1262,10 @@ void usb_create_sysfs_intf_files(struct usb_interface *intf)
 
        if (!alt->string && !(udev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
                alt->string = usb_cache_string(udev, alt->desc.iInterface);
-       if (alt->string && device_create_file(&intf->dev, &dev_attr_interface))
-               ;       /* We don't actually care if the function fails. */
+       if (alt->string && device_create_file(&intf->dev, &dev_attr_interface)) {
+               /* This is not a serious error */
+               dev_dbg(&intf->dev, "interface string descriptor file not created\n");
+       }
        intf->sysfs_files_created = 1;
 }
 
index 64ed402..19e4c55 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Released under the GPLv2 only.
  */
index 78a4925..fec17a2 100644 (file)
@@ -524,10 +524,25 @@ int dwc2_core_reset(struct dwc2_hsotg *hsotg, bool skip_wait)
        greset |= GRSTCTL_CSFTRST;
        dwc2_writel(hsotg, greset, GRSTCTL);
 
-       if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL, GRSTCTL_CSFTRST, 10000)) {
-               dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL GRSTCTL_CSFTRST\n",
-                        __func__);
-               return -EBUSY;
+       if ((hsotg->hw_params.snpsid & DWC2_CORE_REV_MASK) <
+               (DWC2_CORE_REV_4_20a & DWC2_CORE_REV_MASK)) {
+               if (dwc2_hsotg_wait_bit_clear(hsotg, GRSTCTL,
+                                             GRSTCTL_CSFTRST, 10000)) {
+                       dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST\n",
+                                __func__);
+                       return -EBUSY;
+               }
+       } else {
+               if (dwc2_hsotg_wait_bit_set(hsotg, GRSTCTL,
+                                           GRSTCTL_CSFTRST_DONE, 10000)) {
+                       dev_warn(hsotg->dev, "%s: HANG! Soft Reset timeout GRSTCTL_CSFTRST_DONE\n",
+                                __func__);
+                       return -EBUSY;
+               }
+               greset = dwc2_readl(hsotg, GRSTCTL);
+               greset &= ~GRSTCTL_CSFTRST;
+               greset |= GRSTCTL_CSFTRST_DONE;
+               dwc2_writel(hsotg, greset, GRSTCTL);
        }
 
        /* Wait for AHB master IDLE state */
index 99b0bdf..132d687 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
 /*
  * core.h - DesignWare HS OTG Controller common declarations
  *
@@ -1103,8 +1103,10 @@ struct dwc2_hsotg {
 #define DWC2_CORE_REV_3_00a    0x4f54300a
 #define DWC2_CORE_REV_3_10a    0x4f54310a
 #define DWC2_CORE_REV_4_00a    0x4f54400a
+#define DWC2_CORE_REV_4_20a    0x4f54420a
 #define DWC2_FS_IOT_REV_1_00a  0x5531100a
 #define DWC2_HS_IOT_REV_1_00a  0x5532100a
+#define DWC2_CORE_REV_MASK     0x0000ffff
 
        /* DWC OTG HW Core ID */
 #define DWC2_OTG_ID            0x4f540000
@@ -1309,6 +1311,8 @@ void dwc2_force_dr_mode(struct dwc2_hsotg *hsotg);
 
 bool dwc2_is_controller_alive(struct dwc2_hsotg *hsotg);
 
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg);
+
 /*
  * Common core Functions.
  * The following functions support managing the DWC_otg controller in either
index 876ff31..55f1d14 100644 (file)
@@ -416,10 +416,13 @@ static void dwc2_handle_wakeup_detected_intr(struct dwc2_hsotg *hsotg)
                        if (ret && (ret != -ENOTSUPP))
                                dev_err(hsotg->dev, "exit power_down failed\n");
 
+                       /* Change to L0 state */
+                       hsotg->lx_state = DWC2_L0;
                        call_gadget(hsotg, resume);
+               } else {
+                       /* Change to L0 state */
+                       hsotg->lx_state = DWC2_L0;
                }
-               /* Change to L0 state */
-               hsotg->lx_state = DWC2_L0;
        } else {
                if (hsotg->params.power_down)
                        return;
index a8c565b..47252c5 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * debug.h - Designware USB2 DRD controller debug header
  *
index 1224fa9..ea02ee6 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
 /*
  * hcd.h - DesignWare HS OTG Controller host-mode declarations
  *
index c4027bb..c3d6dde 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
 /*
  * hw.h - DesignWare HS OTG Controller hardware definitions
  *
 #define GRSTCTL                                HSOTG_REG(0x010)
 #define GRSTCTL_AHBIDLE                        BIT(31)
 #define GRSTCTL_DMAREQ                 BIT(30)
+#define GRSTCTL_CSFTRST_DONE           BIT(29)
 #define GRSTCTL_TXFNUM_MASK            (0x1f << 6)
 #define GRSTCTL_TXFNUM_SHIFT           6
 #define GRSTCTL_TXFNUM_LIMIT           0x1f
index 8ccc83f..ce736d6 100644 (file)
@@ -782,25 +782,6 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg)
        u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4;
        u32 grxfsiz;
 
-       /*
-        * Attempt to ensure this device is really a DWC_otg Controller.
-        * Read and verify the GSNPSID register contents. The value should be
-        * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
-        */
-
-       hw->snpsid = dwc2_readl(hsotg, GSNPSID);
-       if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
-           (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
-           (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
-               dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
-                       hw->snpsid);
-               return -ENODEV;
-       }
-
-       dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
-               hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
-               hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
-
        hwcfg1 = dwc2_readl(hsotg, GHWCFG1);
        hwcfg2 = dwc2_readl(hsotg, GHWCFG2);
        hwcfg3 = dwc2_readl(hsotg, GHWCFG3);
index 6997275..e571c8a 100644 (file)
@@ -363,6 +363,37 @@ static bool dwc2_check_core_endianness(struct dwc2_hsotg *hsotg)
 }
 
 /**
+ * Check core version
+ *
+ * @hsotg: Programming view of the DWC_otg controller
+ *
+ */
+int dwc2_check_core_version(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_hw_params *hw = &hsotg->hw_params;
+
+       /*
+        * Attempt to ensure this device is really a DWC_otg Controller.
+        * Read and verify the GSNPSID register contents. The value should be
+        * 0x45f4xxxx, 0x5531xxxx or 0x5532xxxx
+        */
+
+       hw->snpsid = dwc2_readl(hsotg, GSNPSID);
+       if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID &&
+           (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID &&
+           (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) {
+               dev_err(hsotg->dev, "Bad value for GSNPSID: 0x%08x\n",
+                       hw->snpsid);
+               return -ENODEV;
+       }
+
+       dev_dbg(hsotg->dev, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n",
+               hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf,
+               hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid);
+       return 0;
+}
+
+/**
  * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
  * driver
  *
@@ -445,6 +476,14 @@ static int dwc2_driver_probe(struct platform_device *dev)
                                      "snps,need-phy-for-wake");
 
        /*
+        * Before performing any core related operations
+        * check core version.
+        */
+       retval = dwc2_check_core_version(hsotg);
+       if (retval)
+               goto error;
+
+       /*
         * Reset before dwc2_get_hwparams() then it could get power-on real
         * reset value form registers.
         */
index edc1715..25c686a 100644 (file)
@@ -85,7 +85,9 @@ static int dwc3_get_dr_mode(struct dwc3 *dwc)
                 * specified or set to OTG, then set the mode to peripheral.
                 */
                if (mode == USB_DR_MODE_OTG &&
-                   dwc->revision >= DWC3_REVISION_330A)
+                   (!IS_ENABLED(CONFIG_USB_ROLE_SWITCH) ||
+                    !device_property_read_bool(dwc->dev, "usb-role-switch")) &&
+                   !DWC3_VER_IS_PRIOR(DWC3, 330A))
                        mode = USB_DR_MODE_PERIPHERAL;
        }
 
@@ -121,17 +123,19 @@ static void __dwc3_set_mode(struct work_struct *work)
        if (dwc->dr_mode != USB_DR_MODE_OTG)
                return;
 
+       pm_runtime_get_sync(dwc->dev);
+
        if (dwc->current_dr_role == DWC3_GCTL_PRTCAP_OTG)
                dwc3_otg_update(dwc, 0);
 
        if (!dwc->desired_dr_role)
-               return;
+               goto out;
 
        if (dwc->desired_dr_role == dwc->current_dr_role)
-               return;
+               goto out;
 
        if (dwc->desired_dr_role == DWC3_GCTL_PRTCAP_OTG && dwc->edev)
-               return;
+               goto out;
 
        switch (dwc->current_dr_role) {
        case DWC3_GCTL_PRTCAP_HOST:
@@ -190,6 +194,9 @@ static void __dwc3_set_mode(struct work_struct *work)
                break;
        }
 
+out:
+       pm_runtime_mark_last_busy(dwc->dev);
+       pm_runtime_put_autosuspend(dwc->dev);
 }
 
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode)
@@ -257,7 +264,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
         * take a little more than 50ms. Set the polling rate at 20ms
         * for 10 times instead.
         */
-       if (dwc3_is_usb31(dwc) && dwc->revision >= DWC3_USB31_REVISION_190A)
+       if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
                retries = 10;
 
        do {
@@ -265,8 +272,7 @@ static int dwc3_core_soft_reset(struct dwc3 *dwc)
                if (!(reg & DWC3_DCTL_CSFTRST))
                        goto done;
 
-               if (dwc3_is_usb31(dwc) &&
-                   dwc->revision >= DWC3_USB31_REVISION_190A)
+               if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32))
                        msleep(20);
                else
                        udelay(1);
@@ -283,7 +289,7 @@ done:
         * is cleared, we must wait at least 50ms before accessing the PHY
         * domain (synchronization delay).
         */
-       if (dwc3_is_usb31(dwc) && dwc->revision <= DWC3_USB31_REVISION_180A)
+       if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A))
                msleep(50);
 
        return 0;
@@ -298,7 +304,7 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc)
        u32 reg;
        u32 dft;
 
-       if (dwc->revision < DWC3_REVISION_250A)
+       if (DWC3_VER_IS_PRIOR(DWC3, 250A))
                return;
 
        if (dwc->fladj == 0)
@@ -579,7 +585,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
         * will be '0' when the core is reset. Application needs to set it
         * to '1' after the core initialization is completed.
         */
-       if (dwc->revision > DWC3_REVISION_194A)
+       if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
                reg |= DWC3_GUSB3PIPECTL_SUSPHY;
 
        /*
@@ -670,7 +676,7 @@ static int dwc3_phy_setup(struct dwc3 *dwc)
         * be '0' when the core is reset. Application needs to set it to
         * '1' after the core initialization is completed.
         */
-       if (dwc->revision > DWC3_REVISION_194A)
+       if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A))
                reg |= DWC3_GUSB2PHYCFG_SUSPHY;
 
        /*
@@ -719,15 +725,13 @@ static bool dwc3_core_is_valid(struct dwc3 *dwc)
        u32 reg;
 
        reg = dwc3_readl(dwc->regs, DWC3_GSNPSID);
+       dwc->ip = DWC3_GSNPS_ID(reg);
 
        /* This should read as U3 followed by revision number */
-       if ((reg & DWC3_GSNPSID_MASK) == 0x55330000) {
-               /* Detected DWC_usb3 IP */
+       if (DWC3_IP_IS(DWC3)) {
                dwc->revision = reg;
-       } else if ((reg & DWC3_GSNPSID_MASK) == 0x33310000) {
-               /* Detected DWC_usb31 IP */
+       } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) {
                dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER);
-               dwc->revision |= DWC3_REVISION_IS_DWC31;
                dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE);
        } else {
                return false;
@@ -760,8 +764,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
                 */
                if ((dwc->dr_mode == USB_DR_MODE_HOST ||
                                dwc->dr_mode == USB_DR_MODE_OTG) &&
-                               (dwc->revision >= DWC3_REVISION_210A &&
-                               dwc->revision <= DWC3_REVISION_250A))
+                               DWC3_VER_IS_WITHIN(DWC3, 210A, 250A))
                        reg |= DWC3_GCTL_DSBLCLKGTNG | DWC3_GCTL_SOFITPSYNC;
                else
                        reg &= ~DWC3_GCTL_DSBLCLKGTNG;
@@ -804,7 +807,7 @@ static void dwc3_core_setup_global_control(struct dwc3 *dwc)
         * and falls back to high-speed mode which causes
         * the device to enter a Connect/Disconnect loop
         */
-       if (dwc->revision < DWC3_REVISION_190A)
+       if (DWC3_VER_IS_PRIOR(DWC3, 190A))
                reg |= DWC3_GCTL_U2RSTECN;
 
        dwc3_writel(dwc->regs, DWC3_GCTL, reg);
@@ -957,7 +960,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
                goto err0a;
 
        if (hw_mode == DWC3_GHWPARAMS0_MODE_DRD &&
-           dwc->revision > DWC3_REVISION_194A) {
+           !DWC3_VER_IS_WITHIN(DWC3, ANY, 194A)) {
                if (!dwc->dis_u3_susphy_quirk) {
                        reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0));
                        reg |= DWC3_GUSB3PIPECTL_SUSPHY;
@@ -1004,20 +1007,20 @@ static int dwc3_core_init(struct dwc3 *dwc)
         * the DWC_usb3 controller. It is NOT available in the
         * DWC_usb31 controller.
         */
-       if (!dwc3_is_usb31(dwc) && dwc->revision >= DWC3_REVISION_310A) {
+       if (DWC3_VER_IS_WITHIN(DWC3, 310A, ANY)) {
                reg = dwc3_readl(dwc->regs, DWC3_GUCTL2);
                reg |= DWC3_GUCTL2_RST_ACTBITLATER;
                dwc3_writel(dwc->regs, DWC3_GUCTL2, reg);
        }
 
-       if (dwc->revision >= DWC3_REVISION_250A) {
+       if (!DWC3_VER_IS_PRIOR(DWC3, 250A)) {
                reg = dwc3_readl(dwc->regs, DWC3_GUCTL1);
 
                /*
                 * Enable hardware control of sending remote wakeup
                 * in HS when the device is in the L1 state.
                 */
-               if (dwc->revision >= DWC3_REVISION_290A)
+               if (!DWC3_VER_IS_PRIOR(DWC3, 290A))
                        reg |= DWC3_GUCTL1_DEV_L1_EXIT_BY_HW;
 
                if (dwc->dis_tx_ipgap_linecheck_quirk)
@@ -1049,7 +1052,7 @@ static int dwc3_core_init(struct dwc3 *dwc)
         * Must config both number of packets and max burst settings to enable
         * RX and/or TX threshold.
         */
-       if (dwc3_is_usb31(dwc) && dwc->dr_mode == USB_DR_MODE_HOST) {
+       if (!DWC3_IP_IS(DWC3) && dwc->dr_mode == USB_DR_MODE_HOST) {
                u8 rx_thr_num = dwc->rx_thr_num_pkt_prd;
                u8 rx_maxburst = dwc->rx_max_burst_prd;
                u8 tx_thr_num = dwc->tx_thr_num_pkt_prd;
@@ -1371,10 +1374,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
 /* check whether the core supports IMOD */
 bool dwc3_has_imod(struct dwc3 *dwc)
 {
-       return ((dwc3_is_usb3(dwc) &&
-                dwc->revision >= DWC3_REVISION_300A) ||
-               (dwc3_is_usb31(dwc) &&
-                dwc->revision >= DWC3_USB31_REVISION_120A));
+       return DWC3_VER_IS_WITHIN(DWC3, 300A, ANY) ||
+               DWC3_VER_IS_WITHIN(DWC31, 120A, ANY) ||
+               DWC3_IP_IS(DWC32);
 }
 
 static void dwc3_check_params(struct dwc3 *dwc)
@@ -1395,7 +1397,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
         * affected version.
         */
        if (!dwc->imod_interval &&
-           (dwc->revision == DWC3_REVISION_300A))
+           DWC3_VER_IS(DWC3, 300A))
                dwc->imod_interval = 1;
 
        /* Check the maximum_speed parameter */
@@ -1417,7 +1419,7 @@ static void dwc3_check_params(struct dwc3 *dwc)
                /*
                 * default to superspeed plus if we are capable.
                 */
-               if (dwc3_is_usb31(dwc) &&
+               if ((DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) &&
                    (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
                     DWC3_GHWPARAMS3_SSPHY_IFC_GEN2))
                        dwc->maximum_speed = USB_SPEED_SUPER_PLUS;
index 4c171a8..013f42a 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * core.h - DesignWare USB3 DRD Core Header
  *
@@ -69,6 +69,7 @@
 #define DWC3_GEVNTCOUNT_EHB    BIT(31)
 #define DWC3_GSNPSID_MASK      0xffff0000
 #define DWC3_GSNPSREV_MASK     0xffff
+#define DWC3_GSNPS_ID(p)       (((p) & DWC3_GSNPSID_MASK) >> 16)
 
 /* DWC3 registers memory space boundries */
 #define DWC3_XHCI_REGS_START           0x0
 #define DWC3_GHWPARAMS6_SRPSUPPORT             BIT(10)
 #define DWC3_GHWPARAMS6_EN_FPGA                        BIT(7)
 
+/* DWC_usb32 only */
+#define DWC3_GHWPARAMS6_MDWIDTH(n)             ((n) & (0x3 << 8))
+
 /* Global HWPARAMS7 Register */
 #define DWC3_GHWPARAMS7_RAM1_DEPTH(n)  ((n) & 0xffff)
 #define DWC3_GHWPARAMS7_RAM2_DEPTH(n)  (((n) >> 16) & 0xffff)
 #define DWC3_DGCMD_SELECTED_FIFO_FLUSH 0x09
 #define DWC3_DGCMD_ALL_FIFO_FLUSH      0x0a
 #define DWC3_DGCMD_SET_ENDPOINT_NRDY   0x0c
+#define DWC3_DGCMD_SET_ENDPOINT_PRIME  0x0d
 #define DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK        0x10
 
 #define DWC3_DGCMD_STATUS(n)           (((n) >> 12) & 0x0F)
@@ -697,6 +702,10 @@ struct dwc3_ep {
 #define DWC3_EP_END_TRANSFER_PENDING BIT(4)
 #define DWC3_EP_PENDING_REQUEST        BIT(5)
 #define DWC3_EP_DELAY_START    BIT(6)
+#define DWC3_EP_WAIT_TRANSFER_COMPLETE BIT(7)
+#define DWC3_EP_IGNORE_NEXT_NOSTREAM   BIT(8)
+#define DWC3_EP_FORCE_RESTART_STREAM   BIT(9)
+#define DWC3_EP_FIRST_STREAM_PRIMED    BIT(10)
 
        /* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN                BIT(31)
@@ -949,7 +958,8 @@ struct dwc3_scratchpad_array {
  * @nr_scratch: number of scratch buffers
  * @u1u2: only used on revisions <1.83a for workaround
  * @maximum_speed: maximum speed requested (mainly for testing purposes)
- * @revision: revision register contents
+ * @ip: controller's ID
+ * @revision: controller's version of an IP
  * @version_type: VERSIONTYPE register contents, a sub release of a revision
  * @dr_mode: requested mode of operation
  * @current_dr_role: current role of operation when in dual-role mode
@@ -1110,15 +1120,15 @@ struct dwc3 {
        u32                     u1u2;
        u32                     maximum_speed;
 
-       /*
-        * All 3.1 IP version constants are greater than the 3.0 IP
-        * version constants. This works for most version checks in
-        * dwc3. However, in the future, this may not apply as
-        * features may be developed on newer versions of the 3.0 IP
-        * that are not in the 3.1 IP.
-        */
+       u32                     ip;
+
+#define DWC3_IP                        0x5533
+#define DWC31_IP               0x3331
+#define DWC32_IP               0x3332
+
        u32                     revision;
 
+#define DWC3_REVISION_ANY      0x0
 #define DWC3_REVISION_173A     0x5533173a
 #define DWC3_REVISION_175A     0x5533175a
 #define DWC3_REVISION_180A     0x5533180a
@@ -1143,20 +1153,20 @@ struct dwc3 {
 #define DWC3_REVISION_310A     0x5533310a
 #define DWC3_REVISION_330A     0x5533330a
 
-/*
- * NOTICE: we're using bit 31 as a "is usb 3.1" flag. This is really
- * just so dwc31 revisions are always larger than dwc3.
- */
-#define DWC3_REVISION_IS_DWC31         0x80000000
-#define DWC3_USB31_REVISION_110A       (0x3131302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_120A       (0x3132302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_160A       (0x3136302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_170A       (0x3137302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_180A       (0x3138302a | DWC3_REVISION_IS_DWC31)
-#define DWC3_USB31_REVISION_190A       (0x3139302a | DWC3_REVISION_IS_DWC31)
+#define DWC31_REVISION_ANY     0x0
+#define DWC31_REVISION_110A    0x3131302a
+#define DWC31_REVISION_120A    0x3132302a
+#define DWC31_REVISION_160A    0x3136302a
+#define DWC31_REVISION_170A    0x3137302a
+#define DWC31_REVISION_180A    0x3138302a
+#define DWC31_REVISION_190A    0x3139302a
+
+#define DWC32_REVISION_ANY     0x0
+#define DWC32_REVISION_100A    0x3130302a
 
        u32                     version_type;
 
+#define DWC31_VERSIONTYPE_ANY          0x0
 #define DWC31_VERSIONTYPE_EA01         0x65613031
 #define DWC31_VERSIONTYPE_EA02         0x65613032
 #define DWC31_VERSIONTYPE_EA03         0x65613033
@@ -1298,6 +1308,10 @@ struct dwc3_event_depevt {
 #define DEPEVT_STREAMEVT_FOUND         1
 #define DEPEVT_STREAMEVT_NOTFOUND      2
 
+/* Stream event parameter */
+#define DEPEVT_STREAM_PRIME            0xfffe
+#define DEPEVT_STREAM_NOSTREAM         0x0
+
 /* Control-only Status */
 #define DEPEVT_STATUS_CONTROL_DATA     1
 #define DEPEVT_STATUS_CONTROL_STATUS   2
@@ -1400,17 +1414,26 @@ void dwc3_set_prtcap(struct dwc3 *dwc, u32 mode);
 void dwc3_set_mode(struct dwc3 *dwc, u32 mode);
 u32 dwc3_core_fifo_space(struct dwc3_ep *dep, u8 type);
 
-/* check whether we are on the DWC_usb3 core */
-static inline bool dwc3_is_usb3(struct dwc3 *dwc)
-{
-       return !(dwc->revision & DWC3_REVISION_IS_DWC31);
-}
+#define DWC3_IP_IS(_ip)                                                        \
+       (dwc->ip == _ip##_IP)
 
-/* check whether we are on the DWC_usb31 core */
-static inline bool dwc3_is_usb31(struct dwc3 *dwc)
-{
-       return !!(dwc->revision & DWC3_REVISION_IS_DWC31);
-}
+#define DWC3_VER_IS(_ip, _ver)                                         \
+       (DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver)
+
+#define DWC3_VER_IS_PRIOR(_ip, _ver)                                   \
+       (DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver)
+
+#define DWC3_VER_IS_WITHIN(_ip, _from, _to)                            \
+       (DWC3_IP_IS(_ip) &&                                             \
+        dwc->revision >= _ip##_REVISION_##_from &&                     \
+        (!(_ip##_REVISION_##_to) ||                                    \
+         dwc->revision <= _ip##_REVISION_##_to))
+
+#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to)                 \
+       (DWC3_VER_IS(_ip, _ver) &&                                      \
+        dwc->version_type >= _ip##_VERSIONTYPE_##_from &&              \
+        (!(_ip##_VERSIONTYPE_##_to) ||                                 \
+         dwc->version_type <= _ip##_VERSIONTYPE_##_to))
 
 bool dwc3_has_imod(struct dwc3 *dwc);
 
index 4a13cea..d8f600e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /**
  * debug.h - DesignWare USB3 DRD Controller Debug Header
  *
@@ -68,6 +68,8 @@ dwc3_gadget_generic_cmd_string(u8 cmd)
                return "All FIFO Flush";
        case DWC3_DGCMD_SET_ENDPOINT_NRDY:
                return "Set Endpoint NRDY";
+       case DWC3_DGCMD_SET_ENDPOINT_PRIME:
+               return "Set Endpoint Prime";
        case DWC3_DGCMD_RUN_SOC_BUS_LOOPBACK:
                return "Run SoC Bus Loopback Test";
        default:
index 4fe8b1e..6d9de33 100644 (file)
@@ -635,13 +635,18 @@ static int dwc3_tx_fifo_size_show(struct seq_file *s, void *unused)
        struct dwc3_ep          *dep = s->private;
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
+       int                     mdwidth;
        u32                     val;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_TXFIFO);
 
        /* Convert to bytes */
-       val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+       mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+       if (DWC3_IP_IS(DWC32))
+               mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+       val *= mdwidth;
        val >>= 3;
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
@@ -654,13 +659,18 @@ static int dwc3_rx_fifo_size_show(struct seq_file *s, void *unused)
        struct dwc3_ep          *dep = s->private;
        struct dwc3             *dwc = dep->dwc;
        unsigned long           flags;
+       int                     mdwidth;
        u32                     val;
 
        spin_lock_irqsave(&dwc->lock, flags);
        val = dwc3_core_fifo_space(dep, DWC3_RXFIFO);
 
        /* Convert to bytes */
-       val *= DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+       mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+       if (DWC3_IP_IS(DWC32))
+               mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
+       val *= mdwidth;
        val >>= 3;
        seq_printf(s, "%u\n", val);
        spin_unlock_irqrestore(&dwc->lock, flags);
index 7db1ffc..2e48344 100644 (file)
@@ -56,7 +56,7 @@ static irqreturn_t dwc3_otg_thread_irq(int irq, void *_dwc)
        spin_lock(&dwc->lock);
        if (dwc->otg_restart_host) {
                dwc3_otg_host_init(dwc);
-               dwc->otg_restart_host = 0;
+               dwc->otg_restart_host = false;
        }
 
        spin_unlock(&dwc->lock);
@@ -82,7 +82,7 @@ static irqreturn_t dwc3_otg_irq(int irq, void *_dwc)
 
                if (dwc->current_otg_role == DWC3_OTG_ROLE_HOST &&
                    !(reg & DWC3_OEVT_DEVICEMODE))
-                       dwc->otg_restart_host = 1;
+                       dwc->otg_restart_host = true;
                dwc3_writel(dwc->regs, DWC3_OEVT, reg);
                ret = IRQ_WAKE_THREAD;
        }
@@ -653,6 +653,6 @@ void dwc3_drd_exit(struct dwc3 *dwc)
                break;
        }
 
-       if (!dwc->edev)
+       if (dwc->otg_irq)
                free_irq(dwc->otg_irq, dwc);
 }
index 1e14a6f..6505f7b 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/io.h>
 #include <linux/of_platform.h>
+#include <linux/phy/phy.h>
 #include <linux/pm_runtime.h>
 
 /* USBSS register offsets */
@@ -34,6 +35,7 @@
 struct dwc3_keystone {
        struct device                   *dev;
        void __iomem                    *usbss;
+       struct phy                      *usb3_phy;
 };
 
 static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
@@ -95,8 +97,38 @@ static int kdwc3_probe(struct platform_device *pdev)
        if (IS_ERR(kdwc->usbss))
                return PTR_ERR(kdwc->usbss);
 
-       pm_runtime_enable(kdwc->dev);
+       /* PSC dependency on AM65 needs SERDES0 to be powered before USB0 */
+       kdwc->usb3_phy = devm_phy_optional_get(dev, "usb3-phy");
+       if (IS_ERR(kdwc->usb3_phy)) {
+               error = PTR_ERR(kdwc->usb3_phy);
+               if (error != -EPROBE_DEFER)
+                       dev_err(dev, "couldn't get usb3 phy: %d\n", error);
+
+               return error;
+       }
+
+       phy_pm_runtime_get_sync(kdwc->usb3_phy);
 
+       error = phy_reset(kdwc->usb3_phy);
+       if (error < 0) {
+               dev_err(dev, "usb3 phy reset failed: %d\n", error);
+               return error;
+       }
+
+       error = phy_init(kdwc->usb3_phy);
+       if (error < 0) {
+               dev_err(dev, "usb3 phy init failed: %d\n", error);
+               return error;
+       }
+
+       error = phy_power_on(kdwc->usb3_phy);
+       if (error < 0) {
+               dev_err(dev, "usb3 phy power on failed: %d\n", error);
+               phy_exit(kdwc->usb3_phy);
+               return error;
+       }
+
+       pm_runtime_enable(kdwc->dev);
        error = pm_runtime_get_sync(kdwc->dev);
        if (error < 0) {
                dev_err(kdwc->dev, "pm_runtime_get_sync failed, error %d\n",
@@ -138,6 +170,9 @@ err_core:
 err_irq:
        pm_runtime_put_sync(kdwc->dev);
        pm_runtime_disable(kdwc->dev);
+       phy_power_off(kdwc->usb3_phy);
+       phy_exit(kdwc->usb3_phy);
+       phy_pm_runtime_put_sync(kdwc->usb3_phy);
 
        return error;
 }
@@ -163,6 +198,10 @@ static int kdwc3_remove(struct platform_device *pdev)
        pm_runtime_put_sync(kdwc->dev);
        pm_runtime_disable(kdwc->dev);
 
+       phy_power_off(kdwc->usb3_phy);
+       phy_exit(kdwc->usb3_phy);
+       phy_pm_runtime_put_sync(kdwc->usb3_phy);
+
        platform_set_drvdata(pdev, NULL);
 
        return 0;
index b81d085..1f7f4d8 100644 (file)
@@ -30,7 +30,7 @@
 #include <linux/usb/role.h>
 #include <linux/regulator/consumer.h>
 
-/* USB2 Ports Control Registers */
+/* USB2 Ports Control Registers, offsets are per-port */
 
 #define U2P_REG_SIZE                                           0x20
 
 
 /* USB Glue Control Registers */
 
-#define USB_R0                                                 0x80
+#define G12A_GLUE_OFFSET                                       0x80
+
+#define USB_R0                                                 0x00
        #define USB_R0_P30_LANE0_TX2RX_LOOPBACK                 BIT(17)
        #define USB_R0_P30_LANE0_EXT_PCLK_REQ                   BIT(18)
        #define USB_R0_P30_PCS_RX_LOS_MASK_VAL_MASK             GENMASK(28, 19)
        #define USB_R0_U2D_SS_SCALEDOWN_MODE_MASK               GENMASK(30, 29)
        #define USB_R0_U2D_ACT                                  BIT(31)
 
-#define USB_R1                                                 0x84
+#define USB_R1                                                 0x04
        #define USB_R1_U3H_BIGENDIAN_GS                         BIT(0)
        #define USB_R1_U3H_PME_ENABLE                           BIT(1)
        #define USB_R1_U3H_HUB_PORT_OVERCURRENT_MASK            GENMASK(4, 2)
        #define USB_R1_U3H_FLADJ_30MHZ_REG_MASK                 GENMASK(24, 19)
        #define USB_R1_P30_PCS_TX_SWING_FULL_MASK               GENMASK(31, 25)
 
-#define USB_R2                                                 0x88
+#define USB_R2                                                 0x08
        #define USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK             GENMASK(25, 20)
        #define USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK               GENMASK(31, 26)
 
-#define USB_R3                                                 0x8c
+#define USB_R3                                                 0x0c
        #define USB_R3_P30_SSC_ENABLE                           BIT(0)
        #define USB_R3_P30_SSC_RANGE_MASK                       GENMASK(3, 1)
        #define USB_R3_P30_SSC_REF_CLK_SEL_MASK                 GENMASK(12, 4)
        #define USB_R3_P30_REF_SSP_EN                           BIT(13)
 
-#define USB_R4                                                 0x90
+#define USB_R4                                                 0x10
        #define USB_R4_P21_PORT_RESET_0                         BIT(0)
        #define USB_R4_P21_SLEEP_M0                             BIT(1)
        #define USB_R4_MEM_PD_MASK                              GENMASK(3, 2)
        #define USB_R4_P21_ONLY                                 BIT(4)
 
-#define USB_R5                                                 0x94
+#define USB_R5                                                 0x14
        #define USB_R5_ID_DIG_SYNC                              BIT(0)
        #define USB_R5_ID_DIG_REG                               BIT(1)
        #define USB_R5_ID_DIG_CFG_MASK                          GENMASK(3, 2)
        #define USB_R5_ID_DIG_TH_MASK                           GENMASK(15, 8)
        #define USB_R5_ID_DIG_CNT_MASK                          GENMASK(23, 16)
 
-enum {
-       USB2_HOST_PHY = 0,
-       USB2_OTG_PHY,
-       USB3_HOST_PHY,
-       PHY_COUNT,
-};
+#define PHY_COUNT                                              3
+#define USB2_OTG_PHY                                           1
 
-static const char *phy_names[PHY_COUNT] = {
-       "usb2-phy0", "usb2-phy1", "usb3-phy0",
+static struct clk_bulk_data meson_gxl_clocks[] = {
+       { .id = "usb_ctrl" },
+       { .id = "ddr" },
 };
 
 static struct clk_bulk_data meson_g12a_clocks[] = {
@@ -117,27 +116,133 @@ static struct clk_bulk_data meson_a1_clocks[] = {
        { .id = "xtal_usb_ctrl" },
 };
 
+static const char *meson_gxm_phy_names[] = {
+       "usb2-phy0", "usb2-phy1", "usb2-phy2",
+};
+
+static const char *meson_g12a_phy_names[] = {
+       "usb2-phy0", "usb2-phy1", "usb3-phy0",
+};
+
+/*
+ * Amlogic A1 has a single physical PHY, in slot 1, but still has the
+ * two U2 PHY controls register blocks like G12A.
+ * Handling the first PHY on slot 1 would need a large amount of code
+ * changes, and the current management is generic enough to handle it
+ * correctly when only the "usb2-phy1" phy is specified on-par with the
+ * DT bindings.
+ */
+static const char *meson_a1_phy_names[] = {
+       "usb2-phy0", "usb2-phy1"
+};
+
+struct dwc3_meson_g12a;
+
 struct dwc3_meson_g12a_drvdata {
        bool otg_switch_supported;
+       bool otg_phy_host_port_disable;
        struct clk_bulk_data *clks;
        int num_clks;
+       const char **phy_names;
+       int num_phys;
+       int (*setup_regmaps)(struct dwc3_meson_g12a *priv, void __iomem *base);
+       int (*usb2_init_phy)(struct dwc3_meson_g12a *priv, int i,
+                            enum phy_mode mode);
+       int (*set_phy_mode)(struct dwc3_meson_g12a *priv, int i,
+                           enum phy_mode mode);
+       int (*usb_init)(struct dwc3_meson_g12a *priv);
+       int (*usb_post_init)(struct dwc3_meson_g12a *priv);
+};
+
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+                                       void __iomem *base);
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+                                        void __iomem *base);
+
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+                                        enum phy_mode mode);
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+                                       enum phy_mode mode);
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+                                       int i, enum phy_mode mode);
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+                                      int i, enum phy_mode mode);
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv);
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv);
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv);
+
+/*
+ * For GXL and GXM SoCs:
+ * USB Phy muxing between the DWC2 Device controller and the DWC3 Host
+ * controller is buggy when switching from Device to Host when USB port
+ * is unpopulated, it causes the DWC3 to hard crash.
+ * When populated (including OTG switching with ID pin), the switch works
+ * like a charm like on the G12A platforms.
+ * In order to still switch from Host to Device on an USB Type-A port,
+ * an U2_PORT_DISABLE bit has been added to disconnect the DWC3 Host
+ * controller from the port, but when used the DWC3 controller must be
+ * reset to recover usage of the port.
+ */
+
+static struct dwc3_meson_g12a_drvdata gxl_drvdata = {
+       .otg_switch_supported = true,
+       .otg_phy_host_port_disable = true,
+       .clks = meson_gxl_clocks,
+       .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+       .phy_names = meson_a1_phy_names,
+       .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+       .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+       .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+       .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+       .usb_init = dwc3_meson_gxl_usb_init,
+       .usb_post_init = dwc3_meson_gxl_usb_post_init,
+};
+
+static struct dwc3_meson_g12a_drvdata gxm_drvdata = {
+       .otg_switch_supported = true,
+       .otg_phy_host_port_disable = true,
+       .clks = meson_gxl_clocks,
+       .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+       .phy_names = meson_gxm_phy_names,
+       .num_phys = ARRAY_SIZE(meson_gxm_phy_names),
+       .setup_regmaps = dwc3_meson_gxl_setup_regmaps,
+       .usb2_init_phy = dwc3_meson_gxl_usb2_init_phy,
+       .set_phy_mode = dwc3_meson_gxl_set_phy_mode,
+       .usb_init = dwc3_meson_gxl_usb_init,
+       .usb_post_init = dwc3_meson_gxl_usb_post_init,
 };
 
 static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
        .otg_switch_supported = true,
        .clks = meson_g12a_clocks,
        .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+       .phy_names = meson_g12a_phy_names,
+       .num_phys = ARRAY_SIZE(meson_g12a_phy_names),
+       .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+       .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+       .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+       .usb_init = dwc3_meson_g12a_usb_init,
 };
 
 static struct dwc3_meson_g12a_drvdata a1_drvdata = {
        .otg_switch_supported = false,
        .clks = meson_a1_clocks,
        .num_clks = ARRAY_SIZE(meson_a1_clocks),
+       .phy_names = meson_a1_phy_names,
+       .num_phys = ARRAY_SIZE(meson_a1_phy_names),
+       .setup_regmaps = dwc3_meson_g12a_setup_regmaps,
+       .usb2_init_phy = dwc3_meson_g12a_usb2_init_phy,
+       .set_phy_mode = dwc3_meson_g12a_set_phy_mode,
+       .usb_init = dwc3_meson_g12a_usb_init,
 };
 
 struct dwc3_meson_g12a {
        struct device           *dev;
-       struct regmap           *regmap;
+       struct regmap           *u2p_regmap[PHY_COUNT];
+       struct regmap           *usb_glue_regmap;
        struct reset_control    *reset;
        struct phy              *phys[PHY_COUNT];
        enum usb_dr_mode        otg_mode;
@@ -150,49 +255,78 @@ struct dwc3_meson_g12a {
        const struct dwc3_meson_g12a_drvdata *drvdata;
 };
 
-static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
-                                         int i, enum phy_mode mode)
+static int dwc3_meson_gxl_set_phy_mode(struct dwc3_meson_g12a *priv,
+                                        int i, enum phy_mode mode)
+{
+       return phy_set_mode(priv->phys[i], mode);
+}
+
+static int dwc3_meson_gxl_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+                                       enum phy_mode mode)
+{
+       /* On GXL PHY must be started in device mode for DWC2 init */
+       return priv->drvdata->set_phy_mode(priv, i,
+                               (i == USB2_OTG_PHY) ? PHY_MODE_USB_DEVICE
+                                                   : PHY_MODE_USB_HOST);
+}
+
+static int dwc3_meson_g12a_set_phy_mode(struct dwc3_meson_g12a *priv,
+                                        int i, enum phy_mode mode)
 {
        if (mode == PHY_MODE_USB_HOST)
-               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+               regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
                                U2P_R0_HOST_DEVICE,
                                U2P_R0_HOST_DEVICE);
        else
-               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
+               regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
                                U2P_R0_HOST_DEVICE, 0);
+
+       return 0;
 }
 
-static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb2_init_phy(struct dwc3_meson_g12a *priv, int i,
+                                        enum phy_mode mode)
 {
-       int i;
+       int ret;
 
-       if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
-               priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
-       else
-               priv->otg_phy_mode = PHY_MODE_USB_HOST;
+       regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+                       U2P_R0_POWER_ON_RESET,
+                       U2P_R0_POWER_ON_RESET);
 
-       for (i = 0 ; i < USB3_HOST_PHY ; ++i) {
-               if (!priv->phys[i])
-                       continue;
+       if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
+               regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+                                  U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
+                                  U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
 
-               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
-                                  U2P_R0_POWER_ON_RESET,
-                                  U2P_R0_POWER_ON_RESET);
+               ret = priv->drvdata->set_phy_mode(priv, i, mode);
+       } else
+               ret = priv->drvdata->set_phy_mode(priv, i,
+                                                 PHY_MODE_USB_HOST);
 
-               if (priv->drvdata->otg_switch_supported && i == USB2_OTG_PHY) {
-                       regmap_update_bits(priv->regmap,
-                               U2P_R0 + (U2P_REG_SIZE * i),
-                               U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS,
-                               U2P_R0_ID_PULLUP | U2P_R0_DRV_VBUS);
+       if (ret)
+               return ret;
+
+       regmap_update_bits(priv->u2p_regmap[i], U2P_R0,
+                       U2P_R0_POWER_ON_RESET, 0);
+
+       return 0;
+}
 
-                       dwc3_meson_g12a_usb2_set_mode(priv, i,
-                                                     priv->otg_phy_mode);
-               } else
-                       dwc3_meson_g12a_usb2_set_mode(priv, i,
-                                                     PHY_MODE_USB_HOST);
+static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv,
+                                    enum phy_mode mode)
+{
+       int i, ret;
 
-               regmap_update_bits(priv->regmap, U2P_R0 + (U2P_REG_SIZE * i),
-                                  U2P_R0_POWER_ON_RESET, 0);
+       for (i = 0; i < priv->drvdata->num_phys; ++i) {
+               if (!priv->phys[i])
+                       continue;
+
+               if (!strstr(priv->drvdata->phy_names[i], "usb2"))
+                       continue;
+
+               ret = priv->drvdata->usb2_init_phy(priv, i, mode);
+               if (ret)
+                       return ret;
        }
 
        return 0;
@@ -200,7 +334,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
 
 static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
 {
-       regmap_update_bits(priv->regmap, USB_R3,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R3,
                        USB_R3_P30_SSC_RANGE_MASK |
                        USB_R3_P30_REF_SSP_EN,
                        USB_R3_P30_SSC_ENABLE |
@@ -208,61 +342,77 @@ static void dwc3_meson_g12a_usb3_init(struct dwc3_meson_g12a *priv)
                        USB_R3_P30_REF_SSP_EN);
        udelay(2);
 
-       regmap_update_bits(priv->regmap, USB_R2,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R2,
                        USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK,
                        FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_3P5DB_MASK, 0x15));
 
-       regmap_update_bits(priv->regmap, USB_R2,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R2,
                        USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK,
                        FIELD_PREP(USB_R2_P30_PCS_TX_DEEMPH_6DB_MASK, 0x20));
 
        udelay(2);
 
-       regmap_update_bits(priv->regmap, USB_R1,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R1,
                        USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT,
                        USB_R1_U3H_HOST_PORT_POWER_CONTROL_PRESENT);
 
-       regmap_update_bits(priv->regmap, USB_R1,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R1,
                        USB_R1_P30_PCS_TX_SWING_FULL_MASK,
                        FIELD_PREP(USB_R1_P30_PCS_TX_SWING_FULL_MASK, 127));
 }
 
-static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv)
+static void dwc3_meson_g12a_usb_otg_apply_mode(struct dwc3_meson_g12a *priv,
+                                              enum phy_mode mode)
 {
-       if (priv->otg_phy_mode == PHY_MODE_USB_DEVICE) {
-               regmap_update_bits(priv->regmap, USB_R0,
+       if (mode == PHY_MODE_USB_DEVICE) {
+               if (priv->otg_mode != USB_DR_MODE_OTG &&
+                   priv->drvdata->otg_phy_host_port_disable)
+                       /* Isolate the OTG PHY port from the Host Controller */
+                       regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+                               USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+                               FIELD_PREP(USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK,
+                                          BIT(USB2_OTG_PHY)));
+
+               regmap_update_bits(priv->usb_glue_regmap, USB_R0,
                                USB_R0_U2D_ACT, USB_R0_U2D_ACT);
-               regmap_update_bits(priv->regmap, USB_R0,
+               regmap_update_bits(priv->usb_glue_regmap, USB_R0,
                                USB_R0_U2D_SS_SCALEDOWN_MODE_MASK, 0);
-               regmap_update_bits(priv->regmap, USB_R4,
+               regmap_update_bits(priv->usb_glue_regmap, USB_R4,
                                USB_R4_P21_SLEEP_M0, USB_R4_P21_SLEEP_M0);
        } else {
-               regmap_update_bits(priv->regmap, USB_R0,
+               if (priv->otg_mode != USB_DR_MODE_OTG &&
+                   priv->drvdata->otg_phy_host_port_disable) {
+                       regmap_update_bits(priv->usb_glue_regmap, USB_R1,
+                               USB_R1_U3H_HOST_U2_PORT_DISABLE_MASK, 0);
+                       msleep(500);
+               }
+               regmap_update_bits(priv->usb_glue_regmap, USB_R0,
                                USB_R0_U2D_ACT, 0);
-               regmap_update_bits(priv->regmap, USB_R4,
+               regmap_update_bits(priv->usb_glue_regmap, USB_R4,
                                USB_R4_P21_SLEEP_M0, 0);
        }
 }
 
-static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+static int dwc3_meson_g12a_usb_init_glue(struct dwc3_meson_g12a *priv,
+                                        enum phy_mode mode)
 {
        int ret;
 
-       ret = dwc3_meson_g12a_usb2_init(priv);
+       ret = dwc3_meson_g12a_usb2_init(priv, mode);
        if (ret)
                return ret;
 
-       regmap_update_bits(priv->regmap, USB_R1,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R1,
                        USB_R1_U3H_FLADJ_30MHZ_REG_MASK,
                        FIELD_PREP(USB_R1_U3H_FLADJ_30MHZ_REG_MASK, 0x20));
 
-       regmap_update_bits(priv->regmap, USB_R5,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R5,
                        USB_R5_ID_DIG_EN_0,
                        USB_R5_ID_DIG_EN_0);
-       regmap_update_bits(priv->regmap, USB_R5,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R5,
                        USB_R5_ID_DIG_EN_1,
                        USB_R5_ID_DIG_EN_1);
-       regmap_update_bits(priv->regmap, USB_R5,
+       regmap_update_bits(priv->usb_glue_regmap, USB_R5,
                        USB_R5_ID_DIG_TH_MASK,
                        FIELD_PREP(USB_R5_ID_DIG_TH_MASK, 0xff));
 
@@ -270,12 +420,13 @@ static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
        if (priv->usb3_ports)
                dwc3_meson_g12a_usb3_init(priv);
 
-       dwc3_meson_g12a_usb_otg_apply_mode(priv);
+       dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
 
        return 0;
 }
 
-static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
+static const struct regmap_config phy_meson_g12a_usb_glue_regmap_conf = {
+       .name = "usb-glue",
        .reg_bits = 8,
        .val_bits = 32,
        .reg_stride = 4,
@@ -284,17 +435,19 @@ static const struct regmap_config phy_meson_g12a_usb3_regmap_conf = {
 
 static int dwc3_meson_g12a_get_phys(struct dwc3_meson_g12a *priv)
 {
+       const char *phy_name;
        int i;
 
-       for (i = 0 ; i < PHY_COUNT ; ++i) {
-               priv->phys[i] = devm_phy_optional_get(priv->dev, phy_names[i]);
+       for (i = 0 ; i < priv->drvdata->num_phys ; ++i) {
+               phy_name = priv->drvdata->phy_names[i];
+               priv->phys[i] = devm_phy_optional_get(priv->dev, phy_name);
                if (!priv->phys[i])
                        continue;
 
                if (IS_ERR(priv->phys[i]))
                        return PTR_ERR(priv->phys[i]);
 
-               if (i == USB3_HOST_PHY)
+               if (strstr(phy_name, "usb3"))
                        priv->usb3_ports++;
                else
                        priv->usb2_ports++;
@@ -310,7 +463,7 @@ static enum phy_mode dwc3_meson_g12a_get_id(struct dwc3_meson_g12a *priv)
 {
        u32 reg;
 
-       regmap_read(priv->regmap, USB_R5, &reg);
+       regmap_read(priv->usb_glue_regmap, USB_R5, &reg);
 
        if (reg & (USB_R5_ID_DIG_SYNC | USB_R5_ID_DIG_REG))
                return PHY_MODE_USB_DEVICE;
@@ -342,9 +495,11 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
 
        priv->otg_phy_mode = mode;
 
-       dwc3_meson_g12a_usb2_set_mode(priv, USB2_OTG_PHY, mode);
+       ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY, mode);
+       if (ret)
+               return ret;
 
-       dwc3_meson_g12a_usb_otg_apply_mode(priv);
+       dwc3_meson_g12a_usb_otg_apply_mode(priv, mode);
 
        return 0;
 }
@@ -364,6 +519,13 @@ static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
        if (mode == priv->otg_phy_mode)
                return 0;
 
+       if (priv->drvdata->otg_phy_host_port_disable)
+               dev_warn_once(priv->dev, "Manual OTG switch is broken on this "\
+                                        "SoC, when manual switching from "\
+                                        "Host to device, DWC3 controller "\
+                                        "will need to be resetted in order "\
+                                        "to recover usage of the Host port");
+
        return dwc3_meson_g12a_otg_mode_set(priv, mode);
 }
 
@@ -386,7 +548,8 @@ static irqreturn_t dwc3_meson_g12a_irq_thread(int irq, void *data)
                        dev_warn(priv->dev, "Failed to switch OTG mode\n");
        }
 
-       regmap_update_bits(priv->regmap, USB_R5, USB_R5_ID_DIG_IRQ, 0);
+       regmap_update_bits(priv->usb_glue_regmap, USB_R5,
+                          USB_R5_ID_DIG_IRQ, 0);
 
        return IRQ_HANDLED;
 }
@@ -421,7 +584,7 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
 
        if (priv->otg_mode == USB_DR_MODE_OTG) {
                /* Ack irq before registering */
-               regmap_update_bits(priv->regmap, USB_R5,
+               regmap_update_bits(priv->usb_glue_regmap, USB_R5,
                                   USB_R5_ID_DIG_IRQ, 0);
 
                irq = platform_get_irq(pdev, 0);
@@ -457,6 +620,77 @@ static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
        return 0;
 }
 
+static int dwc3_meson_gxl_setup_regmaps(struct dwc3_meson_g12a *priv,
+                                       void __iomem *base)
+{
+       /* GXL controls the PHY mode in the PHY registers unlike G12A */
+       priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev, base,
+                                       &phy_meson_g12a_usb_glue_regmap_conf);
+       if (IS_ERR(priv->usb_glue_regmap))
+               return PTR_ERR(priv->usb_glue_regmap);
+
+       return 0;
+}
+
+static int dwc3_meson_g12a_setup_regmaps(struct dwc3_meson_g12a *priv,
+                                        void __iomem *base)
+{
+       int i;
+
+       priv->usb_glue_regmap = devm_regmap_init_mmio(priv->dev,
+                                       base + G12A_GLUE_OFFSET,
+                                       &phy_meson_g12a_usb_glue_regmap_conf);
+       if (IS_ERR(priv->usb_glue_regmap))
+               return PTR_ERR(priv->usb_glue_regmap);
+
+       /* Create a regmap for each USB2 PHY control register set */
+       for (i = 0; i < priv->usb2_ports; i++) {
+               struct regmap_config u2p_regmap_config = {
+                       .reg_bits = 8,
+                       .val_bits = 32,
+                       .reg_stride = 4,
+                       .max_register = U2P_R1,
+               };
+
+               u2p_regmap_config.name = devm_kasprintf(priv->dev, GFP_KERNEL,
+                                                       "u2p-%d", i);
+               if (!u2p_regmap_config.name)
+                       return -ENOMEM;
+
+               priv->u2p_regmap[i] = devm_regmap_init_mmio(priv->dev,
+                                               base + (i * U2P_REG_SIZE),
+                                               &u2p_regmap_config);
+               if (IS_ERR(priv->u2p_regmap[i]))
+                       return PTR_ERR(priv->u2p_regmap[i]);
+       }
+
+       return 0;
+}
+
+static int dwc3_meson_g12a_usb_init(struct dwc3_meson_g12a *priv)
+{
+       return dwc3_meson_g12a_usb_init_glue(priv, priv->otg_phy_mode);
+}
+
+static int dwc3_meson_gxl_usb_init(struct dwc3_meson_g12a *priv)
+{
+       return dwc3_meson_g12a_usb_init_glue(priv, PHY_MODE_USB_DEVICE);
+}
+
+static int dwc3_meson_gxl_usb_post_init(struct dwc3_meson_g12a *priv)
+{
+       int ret;
+
+       ret = priv->drvdata->set_phy_mode(priv, USB2_OTG_PHY,
+                                         priv->otg_phy_mode);
+       if (ret)
+               return ret;
+
+       dwc3_meson_g12a_usb_otg_apply_mode(priv, priv->otg_phy_mode);
+
+       return 0;
+}
+
 static int dwc3_meson_g12a_probe(struct platform_device *pdev)
 {
        struct dwc3_meson_g12a  *priv;
@@ -473,10 +707,8 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
        if (IS_ERR(base))
                return PTR_ERR(base);
 
-       priv->regmap = devm_regmap_init_mmio(dev, base,
-                                            &phy_meson_g12a_usb3_regmap_conf);
-       if (IS_ERR(priv->regmap))
-               return PTR_ERR(priv->regmap);
+       priv->drvdata = of_device_get_match_data(&pdev->dev);
+       priv->dev = dev;
 
        priv->vbus = devm_regulator_get_optional(dev, "vbus");
        if (IS_ERR(priv->vbus)) {
@@ -485,8 +717,6 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
                priv->vbus = NULL;
        }
 
-       priv->drvdata = of_device_get_match_data(&pdev->dev);
-
        ret = devm_clk_bulk_get(dev,
                                priv->drvdata->num_clks,
                                priv->drvdata->clks);
@@ -499,13 +729,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
                return ret;
 
        platform_set_drvdata(pdev, priv);
-       priv->dev = dev;
 
-       priv->reset = devm_reset_control_get(dev, NULL);
+       priv->reset = devm_reset_control_get_shared(dev, NULL);
        if (IS_ERR(priv->reset)) {
                ret = PTR_ERR(priv->reset);
                dev_err(dev, "failed to get device reset, err=%d\n", ret);
-               return ret;
+               goto err_disable_clks;
        }
 
        ret = reset_control_reset(priv->reset);
@@ -516,6 +745,10 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
        if (ret)
                goto err_disable_clks;
 
+       ret = priv->drvdata->setup_regmaps(priv, base);
+       if (ret)
+               return ret;
+
        if (priv->vbus) {
                ret = regulator_enable(priv->vbus);
                if (ret)
@@ -525,7 +758,14 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
        /* Get dr_mode */
        priv->otg_mode = usb_get_dr_mode(dev);
 
-       dwc3_meson_g12a_usb_init(priv);
+       if (priv->otg_mode == USB_DR_MODE_PERIPHERAL)
+               priv->otg_phy_mode = PHY_MODE_USB_DEVICE;
+       else
+               priv->otg_phy_mode = PHY_MODE_USB_HOST;
+
+       ret = priv->drvdata->usb_init(priv);
+       if (ret)
+               goto err_disable_clks;
 
        /* Init PHYs */
        for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -541,6 +781,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
                        goto err_phys_exit;
        }
 
+       if (priv->drvdata->usb_post_init) {
+               ret = priv->drvdata->usb_post_init(priv);
+               if (ret)
+                       goto err_phys_power;
+       }
+
        ret = of_platform_populate(np, NULL, NULL, dev);
        if (ret)
                goto err_phys_power;
@@ -642,7 +888,9 @@ static int __maybe_unused dwc3_meson_g12a_resume(struct device *dev)
 
        reset_control_deassert(priv->reset);
 
-       dwc3_meson_g12a_usb_init(priv);
+       ret = priv->drvdata->usb_init(priv);
+       if (ret)
+               return ret;
 
        /* Init PHYs */
        for (i = 0 ; i < PHY_COUNT ; ++i) {
@@ -675,6 +923,14 @@ static const struct dev_pm_ops dwc3_meson_g12a_dev_pm_ops = {
 
 static const struct of_device_id dwc3_meson_g12a_match[] = {
        {
+               .compatible = "amlogic,meson-gxl-usb-ctrl",
+               .data = &gxl_drvdata,
+       },
+       {
+               .compatible = "amlogic,meson-gxm-usb-ctrl",
+               .data = &gxm_drvdata,
+       },
+       {
                .compatible = "amlogic,meson-g12a-usb-ctrl",
                .data = &g12a_drvdata,
        },
index e64754b..8852fbf 100644 (file)
@@ -27,7 +27,6 @@ struct dwc3_of_simple {
        struct clk_bulk_data    *clks;
        int                     num_clocks;
        struct reset_control    *resets;
-       bool                    pulse_resets;
        bool                    need_reset;
 };
 
@@ -38,7 +37,6 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
        struct device_node      *np = dev->of_node;
 
        int                     ret;
-       bool                    shared_resets = false;
 
        simple = devm_kzalloc(dev, sizeof(*simple), GFP_KERNEL);
        if (!simple)
@@ -54,13 +52,7 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
        if (of_device_is_compatible(np, "rockchip,rk3399-dwc3"))
                simple->need_reset = true;
 
-       if (of_device_is_compatible(np, "amlogic,meson-axg-dwc3") ||
-           of_device_is_compatible(np, "amlogic,meson-gxl-dwc3")) {
-               shared_resets = true;
-               simple->pulse_resets = true;
-       }
-
-       simple->resets = of_reset_control_array_get(np, shared_resets, true,
+       simple->resets = of_reset_control_array_get(np, false, true,
                                                    true);
        if (IS_ERR(simple->resets)) {
                ret = PTR_ERR(simple->resets);
@@ -68,15 +60,9 @@ static int dwc3_of_simple_probe(struct platform_device *pdev)
                return ret;
        }
 
-       if (simple->pulse_resets) {
-               ret = reset_control_reset(simple->resets);
-               if (ret)
-                       goto err_resetc_put;
-       } else {
-               ret = reset_control_deassert(simple->resets);
-               if (ret)
-                       goto err_resetc_put;
-       }
+       ret = reset_control_deassert(simple->resets);
+       if (ret)
+               goto err_resetc_put;
 
        ret = clk_bulk_get_all(simple->dev, &simple->clks);
        if (ret < 0)
@@ -102,8 +88,7 @@ err_clk_put:
        clk_bulk_put_all(simple->num_clocks, simple->clks);
 
 err_resetc_assert:
-       if (!simple->pulse_resets)
-               reset_control_assert(simple->resets);
+       reset_control_assert(simple->resets);
 
 err_resetc_put:
        reset_control_put(simple->resets);
@@ -118,8 +103,7 @@ static void __dwc3_of_simple_teardown(struct dwc3_of_simple *simple)
        clk_bulk_put_all(simple->num_clocks, simple->clks);
        simple->num_clocks = 0;
 
-       if (!simple->pulse_resets)
-               reset_control_assert(simple->resets);
+       reset_control_assert(simple->resets);
 
        reset_control_put(simple->resets);
 
@@ -191,8 +175,6 @@ static const struct of_device_id of_dwc3_simple_match[] = {
        { .compatible = "xlnx,zynqmp-dwc3" },
        { .compatible = "cavium,octeon-7130-usb-uctl" },
        { .compatible = "sprd,sc9860-dwc3" },
-       { .compatible = "amlogic,meson-axg-dwc3" },
-       { .compatible = "amlogic,meson-gxl-dwc3" },
        { .compatible = "allwinner,sun50i-h6-dwc3" },
        { /* Sentinel */ }
 };
index 585cb3d..80c3ef1 100644 (file)
@@ -95,7 +95,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
         * Wait until device controller is ready. Only applies to 1.94a and
         * later RTL.
         */
-       if (dwc->revision >= DWC3_REVISION_194A) {
+       if (!DWC3_VER_IS_PRIOR(DWC3, 194A)) {
                while (--retries) {
                        reg = dwc3_readl(dwc->regs, DWC3_DSTS);
                        if (reg & DWC3_DSTS_DCNRD)
@@ -122,7 +122,7 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
         * The following code is racy when called from dwc3_gadget_wakeup,
         * and is not needed, at least on newer versions
         */
-       if (dwc->revision >= DWC3_REVISION_194A)
+       if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
                return 0;
 
        /* wait for a change in DSTS */
@@ -273,7 +273,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
 {
        const struct usb_endpoint_descriptor *desc = dep->endpoint.desc;
        struct dwc3             *dwc = dep->dwc;
-       u32                     timeout = 1000;
+       u32                     timeout = 5000;
        u32                     saved_config = 0;
        u32                     reg;
 
@@ -356,6 +356,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
                                ret = 0;
                                break;
                        case DEPEVT_TRANSFER_NO_RESOURCE:
+                               dev_WARN(dwc->dev, "No resource for %s\n",
+                                        dep->name);
                                ret = -EINVAL;
                                break;
                        case DEPEVT_TRANSFER_BUS_EXPIRY:
@@ -387,9 +389,12 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd,
 
        trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status);
 
-       if (ret == 0 && DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
-               dep->flags |= DWC3_EP_TRANSFER_STARTED;
-               dwc3_gadget_ep_get_transfer_index(dep);
+       if (DWC3_DEPCMD_CMD(cmd) == DWC3_DEPCMD_STARTTRANSFER) {
+               if (ret == 0)
+                       dep->flags |= DWC3_EP_TRANSFER_STARTED;
+
+               if (ret != -ETIMEDOUT)
+                       dwc3_gadget_ep_get_transfer_index(dep);
        }
 
        if (saved_config) {
@@ -415,7 +420,8 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep)
         * IN transfers due to a mishandled error condition. Synopsys
         * STAR 9000614252.
         */
-       if (dep->direction && (dwc->revision >= DWC3_REVISION_260A) &&
+       if (dep->direction &&
+           !DWC3_VER_IS_PRIOR(DWC3, 260A) &&
            (dwc->gadget.speed >= USB_SPEED_SUPER))
                cmd |= DWC3_DEPCMD_CLEARPENDIN;
 
@@ -573,6 +579,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
 
        if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) {
                params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE
+                       | DWC3_DEPCFG_XFER_COMPLETE_EN
                        | DWC3_DEPCFG_STREAM_EVENT_EN;
                dep->stream_capable = true;
        }
@@ -603,6 +610,9 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action)
        return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, &params);
 }
 
+static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
+               bool interrupt);
+
 /**
  * __dwc3_gadget_ep_enable - initializes a hw endpoint
  * @dep: endpoint to be initialized
@@ -663,7 +673,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
         * Issue StartTransfer here with no-op TRB so we can always rely on No
         * Response Update Transfer command.
         */
-       if ((usb_endpoint_xfer_bulk(desc) && !dep->stream_capable) ||
+       if (usb_endpoint_xfer_bulk(desc) ||
                        usb_endpoint_xfer_int(desc)) {
                struct dwc3_gadget_ep_cmd_params params;
                struct dwc3_trb *trb;
@@ -682,6 +692,29 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, unsigned int action)
                ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
                if (ret < 0)
                        return ret;
+
+               if (dep->stream_capable) {
+                       /*
+                        * For streams, at start, there maybe a race where the
+                        * host primes the endpoint before the function driver
+                        * queues a request to initiate a stream. In that case,
+                        * the controller will not see the prime to generate the
+                        * ERDY and start stream. To workaround this, issue a
+                        * no-op TRB as normal, but end it immediately. As a
+                        * result, when the function driver queues the request,
+                        * the next START_TRANSFER command will cause the
+                        * controller to generate an ERDY to initiate the
+                        * stream.
+                        */
+                       dwc3_stop_active_transfer(dep, true, true);
+
+                       /*
+                        * All stream eps will reinitiate stream on NoStream
+                        * rejection until we can determine that the host can
+                        * prime after the first transfer.
+                        */
+                       dep->flags |= DWC3_EP_FORCE_RESTART_STREAM;
+               }
        }
 
 out:
@@ -690,8 +723,6 @@ out:
        return 0;
 }
 
-static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
-               bool interrupt);
 static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep)
 {
        struct dwc3_request             *req;
@@ -912,7 +943,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep)
 
 static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
                dma_addr_t dma, unsigned length, unsigned chain, unsigned node,
-               unsigned stream_id, unsigned short_not_ok, unsigned no_interrupt)
+               unsigned stream_id, unsigned short_not_ok,
+               unsigned no_interrupt, unsigned is_last)
 {
        struct dwc3             *dwc = dep->dwc;
        struct usb_gadget       *gadget = &dwc->gadget;
@@ -1005,6 +1037,8 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb,
 
        if (chain)
                trb->ctrl |= DWC3_TRB_CTRL_CHN;
+       else if (dep->stream_capable && is_last)
+               trb->ctrl |= DWC3_TRB_CTRL_LST;
 
        if (usb_endpoint_xfer_bulk(dep->endpoint.desc) && dep->stream_capable)
                trb->ctrl |= DWC3_TRB_CTRL_SID_SOFN(stream_id);
@@ -1032,6 +1066,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        unsigned                stream_id = req->request.stream_id;
        unsigned                short_not_ok = req->request.short_not_ok;
        unsigned                no_interrupt = req->request.no_interrupt;
+       unsigned                is_last = req->request.is_last;
 
        if (req->request.num_sgs > 0) {
                length = sg_dma_len(req->start_sg);
@@ -1052,7 +1087,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
        req->num_trbs++;
 
        __dwc3_prepare_one_trb(dep, trb, dma, length, chain, node,
-                       stream_id, short_not_ok, no_interrupt);
+                       stream_id, short_not_ok, no_interrupt, is_last);
 }
 
 static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
@@ -1097,7 +1132,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep,
                                        maxp - rem, false, 1,
                                        req->request.stream_id,
                                        req->request.short_not_ok,
-                                       req->request.no_interrupt);
+                                       req->request.no_interrupt,
+                                       req->request.is_last);
                } else {
                        dwc3_prepare_one_trb(dep, req, chain, i);
                }
@@ -1141,7 +1177,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, maxp - rem,
                                false, 1, req->request.stream_id,
                                req->request.short_not_ok,
-                               req->request.no_interrupt);
+                               req->request.no_interrupt,
+                               req->request.is_last);
        } else if (req->request.zero && req->request.length &&
                   (IS_ALIGNED(req->request.length, maxp))) {
                struct dwc3     *dwc = dep->dwc;
@@ -1158,7 +1195,8 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep,
                __dwc3_prepare_one_trb(dep, trb, dwc->bounce_addr, 0,
                                false, 1, req->request.stream_id,
                                req->request.short_not_ok,
-                               req->request.no_interrupt);
+                               req->request.no_interrupt,
+                               req->request.is_last);
        } else {
                dwc3_prepare_one_trb(dep, req, false, 0);
        }
@@ -1194,6 +1232,14 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
 
                if (!dwc3_calc_trbs_left(dep))
                        return;
+
+               /*
+                * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+                * burst capability may try to read and use TRBs beyond the
+                * active transfer instead of stopping.
+                */
+               if (dep->stream_capable && req->request.is_last)
+                       return;
        }
 
        list_for_each_entry_safe(req, n, &dep->pending_list, list) {
@@ -1217,9 +1263,19 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep)
 
                if (!dwc3_calc_trbs_left(dep))
                        return;
+
+               /*
+                * Don't prepare beyond a transfer. In DWC_usb32, its transfer
+                * burst capability may try to read and use TRBs beyond the
+                * active transfer instead of stopping.
+                */
+               if (dep->stream_capable && req->request.is_last)
+                       return;
        }
 }
 
+static void dwc3_gadget_ep_cleanup_cancelled_requests(struct dwc3_ep *dep);
+
 static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
 {
        struct dwc3_gadget_ep_cmd_params params;
@@ -1259,17 +1315,26 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep)
 
        ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
        if (ret < 0) {
-               /*
-                * FIXME we need to iterate over the list of requests
-                * here and stop, unmap, free and del each of the linked
-                * requests instead of what we do now.
-                */
-               if (req->trb)
-                       memset(req->trb, 0, sizeof(struct dwc3_trb));
-               dwc3_gadget_del_and_unmap_request(dep, req, ret);
+               struct dwc3_request *tmp;
+
+               if (ret == -EAGAIN)
+                       return ret;
+
+               dwc3_stop_active_transfer(dep, true, true);
+
+               list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+                       dwc3_gadget_move_cancelled_request(req);
+
+               /* If ep isn't started, then there's no end transfer pending */
+               if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING))
+                       dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+
                return ret;
        }
 
+       if (dep->stream_capable && req->request.is_last)
+               dep->flags |= DWC3_EP_WAIT_TRANSFER_COMPLETE;
+
        return 0;
 }
 
@@ -1402,17 +1467,15 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
        int ret;
        int i;
 
-       if (list_empty(&dep->pending_list)) {
+       if (list_empty(&dep->pending_list) &&
+           list_empty(&dep->started_list)) {
                dep->flags |= DWC3_EP_PENDING_REQUEST;
                return -EAGAIN;
        }
 
-       if (!dwc->dis_start_transfer_quirk && dwc3_is_usb31(dwc) &&
-           (dwc->revision <= DWC3_USB31_REVISION_160A ||
-            (dwc->revision == DWC3_USB31_REVISION_170A &&
-             dwc->version_type >= DWC31_VERSIONTYPE_EA01 &&
-             dwc->version_type <= DWC31_VERSIONTYPE_EA06))) {
-
+       if (!dwc->dis_start_transfer_quirk &&
+           (DWC3_VER_IS_PRIOR(DWC31, 170A) ||
+            DWC3_VER_TYPE_IS_WITHIN(DWC31, 170A, EA01, EA06))) {
                if (dwc->gadget.speed <= USB_SPEED_HIGH && dep->direction)
                        return dwc3_gadget_start_isoc_quirk(dep);
        }
@@ -1425,6 +1488,27 @@ static int __dwc3_gadget_start_isoc(struct dwc3_ep *dep)
                        break;
        }
 
+       /*
+        * After a number of unsuccessful start attempts due to bus-expiry
+        * status, issue END_TRANSFER command and retry on the next XferNotReady
+        * event.
+        */
+       if (ret == -EAGAIN) {
+               struct dwc3_gadget_ep_cmd_params params;
+               u32 cmd;
+
+               cmd = DWC3_DEPCMD_ENDTRANSFER |
+                       DWC3_DEPCMD_CMDIOC |
+                       DWC3_DEPCMD_PARAM(dep->resource_index);
+
+               dep->resource_index = 0;
+               memset(&params, 0, sizeof(params));
+
+               ret = dwc3_send_gadget_ep_cmd(dep, cmd, &params);
+               if (!ret)
+                       dep->flags |= DWC3_EP_END_TRANSFER_PENDING;
+       }
+
        return ret;
 }
 
@@ -1457,6 +1541,9 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
        list_add_tail(&req->list, &dep->pending_list);
        req->status = DWC3_REQUEST_STATUS_QUEUED;
 
+       if (dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE)
+               return 0;
+
        /* Start the transfer only after the END_TRANSFER is completed */
        if (dep->flags & DWC3_EP_END_TRANSFER_PENDING) {
                dep->flags |= DWC3_EP_DELAY_START;
@@ -1508,6 +1595,10 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
 {
        int i;
 
+       /* If req->trb is not set, then the request has not started */
+       if (!req->trb)
+               return;
+
        /*
         * If request was already started, this means we had to
         * stop the transfer. With that we also need to ignore
@@ -1556,39 +1647,40 @@ static int dwc3_gadget_ep_dequeue(struct usb_ep *ep,
 
        spin_lock_irqsave(&dwc->lock, flags);
 
-       list_for_each_entry(r, &dep->pending_list, list) {
+       list_for_each_entry(r, &dep->cancelled_list, list) {
                if (r == req)
-                       break;
+                       goto out;
        }
 
-       if (r != req) {
-               list_for_each_entry(r, &dep->started_list, list) {
-                       if (r == req)
-                               break;
+       list_for_each_entry(r, &dep->pending_list, list) {
+               if (r == req) {
+                       dwc3_gadget_giveback(dep, req, -ECONNRESET);
+                       goto out;
                }
+       }
+
+       list_for_each_entry(r, &dep->started_list, list) {
                if (r == req) {
+                       struct dwc3_request *t;
+
                        /* wait until it is processed */
                        dwc3_stop_active_transfer(dep, true, true);
 
-                       if (!r->trb)
-                               goto out0;
+                       /*
+                        * Remove any started request if the transfer is
+                        * cancelled.
+                        */
+                       list_for_each_entry_safe(r, t, &dep->started_list, list)
+                               dwc3_gadget_move_cancelled_request(r);
 
-                       dwc3_gadget_move_cancelled_request(req);
-                       if (dep->flags & DWC3_EP_TRANSFER_STARTED)
-                               goto out0;
-                       else
-                               goto out1;
+                       goto out;
                }
-               dev_err(dwc->dev, "request %pK was not queued to %s\n",
-                               request, ep->name);
-               ret = -EINVAL;
-               goto out0;
        }
 
-out1:
-       dwc3_gadget_giveback(dep, req, -ECONNRESET);
-
-out0:
+       dev_err(dwc->dev, "request %pK was not queued to %s\n",
+               request, ep->name);
+       ret = -EINVAL;
+out:
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        return ret;
@@ -1598,6 +1690,8 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
 {
        struct dwc3_gadget_ep_cmd_params        params;
        struct dwc3                             *dwc = dep->dwc;
+       struct dwc3_request                     *req;
+       struct dwc3_request                     *tmp;
        int                                     ret;
 
        if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
@@ -1634,13 +1728,37 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol)
                else
                        dep->flags |= DWC3_EP_STALL;
        } else {
+               /*
+                * Don't issue CLEAR_STALL command to control endpoints. The
+                * controller automatically clears the STALL when it receives
+                * the SETUP token.
+                */
+               if (dep->number <= 1) {
+                       dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+                       return 0;
+               }
 
                ret = dwc3_send_clear_stall_ep_cmd(dep);
-               if (ret)
+               if (ret) {
                        dev_err(dwc->dev, "failed to clear STALL on %s\n",
                                        dep->name);
-               else
-                       dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+                       return ret;
+               }
+
+               dep->flags &= ~(DWC3_EP_STALL | DWC3_EP_WEDGE);
+
+               dwc3_stop_active_transfer(dep, true, true);
+
+               list_for_each_entry_safe(req, tmp, &dep->started_list, list)
+                       dwc3_gadget_move_cancelled_request(req);
+
+               list_for_each_entry_safe(req, tmp, &dep->pending_list, list)
+                       dwc3_gadget_move_cancelled_request(req);
+
+               if (!(dep->flags & DWC3_EP_END_TRANSFER_PENDING)) {
+                       dep->flags &= ~DWC3_EP_DELAY_START;
+                       dwc3_gadget_ep_cleanup_cancelled_requests(dep);
+               }
        }
 
        return ret;
@@ -1756,7 +1874,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
        }
 
        /* Recent versions do this automatically */
-       if (dwc->revision < DWC3_REVISION_194A) {
+       if (DWC3_VER_IS_PRIOR(DWC3, 194A)) {
                /* write zeroes to Link Change Request */
                reg = dwc3_readl(dwc->regs, DWC3_DCTL);
                reg &= ~DWC3_DCTL_ULSTCHNGREQ_MASK;
@@ -1818,12 +1936,12 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 
        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
        if (is_on) {
-               if (dwc->revision <= DWC3_REVISION_187A) {
+               if (DWC3_VER_IS_WITHIN(DWC3, ANY, 187A)) {
                        reg &= ~DWC3_DCTL_TRGTULST_MASK;
                        reg |= DWC3_DCTL_TRGTULST_RX_DET;
                }
 
-               if (dwc->revision >= DWC3_REVISION_194A)
+               if (!DWC3_VER_IS_PRIOR(DWC3, 194A))
                        reg &= ~DWC3_DCTL_KEEP_CONNECT;
                reg |= DWC3_DCTL_RUN_STOP;
 
@@ -1897,7 +2015,7 @@ static void dwc3_gadget_enable_irq(struct dwc3 *dwc)
                        DWC3_DEVTEN_USBRSTEN |
                        DWC3_DEVTEN_DISCONNEVTEN);
 
-       if (dwc->revision < DWC3_REVISION_250A)
+       if (DWC3_VER_IS_PRIOR(DWC3, 250A))
                reg |= DWC3_DEVTEN_ULSTCNGEN;
 
        dwc3_writel(dwc->regs, DWC3_DEVTEN, reg);
@@ -1942,6 +2060,8 @@ static void dwc3_gadget_setup_nump(struct dwc3 *dwc)
 
        ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7);
        mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0);
+       if (DWC3_IP_IS(DWC32))
+               mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
 
        nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024;
        nump = min_t(u32, nump, 16);
@@ -1978,10 +2098,10 @@ static int __dwc3_gadget_start(struct dwc3 *dwc)
         * bursts of data without going through any sort of endpoint throttling.
         */
        reg = dwc3_readl(dwc->regs, DWC3_GRXTHRCFG);
-       if (dwc3_is_usb31(dwc))
-               reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
-       else
+       if (DWC3_IP_IS(DWC3))
                reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL;
+       else
+               reg &= ~DWC31_GRXTHRCFG_PKTCNTSEL;
 
        dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg);
 
@@ -2154,7 +2274,7 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
         * STAR#9000525659: Clock Domain Crossing on DCTL in
         * USB 2.0 Mode
         */
-       if (dwc->revision < DWC3_REVISION_220A &&
+       if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
            !dwc->dis_metastability_quirk) {
                reg |= DWC3_DCFG_SUPERSPEED;
        } else {
@@ -2172,18 +2292,18 @@ static void dwc3_gadget_set_speed(struct usb_gadget *g,
                        reg |= DWC3_DCFG_SUPERSPEED;
                        break;
                case USB_SPEED_SUPER_PLUS:
-                       if (dwc3_is_usb31(dwc))
-                               reg |= DWC3_DCFG_SUPERSPEED_PLUS;
-                       else
+                       if (DWC3_IP_IS(DWC3))
                                reg |= DWC3_DCFG_SUPERSPEED;
+                       else
+                               reg |= DWC3_DCFG_SUPERSPEED_PLUS;
                        break;
                default:
                        dev_err(dwc->dev, "invalid speed (%d)\n", speed);
 
-                       if (dwc->revision & DWC3_REVISION_IS_DWC31)
-                               reg |= DWC3_DCFG_SUPERSPEED_PLUS;
-                       else
+                       if (DWC3_IP_IS(DWC3))
                                reg |= DWC3_DCFG_SUPERSPEED;
+                       else
+                               reg |= DWC3_DCFG_SUPERSPEED_PLUS;
                }
        }
        dwc3_writel(dwc->regs, DWC3_DCFG, reg);
@@ -2226,14 +2346,17 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
        int size;
 
        mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+       if (DWC3_IP_IS(DWC32))
+               mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
+
        /* MDWIDTH is represented in bits, we need it in bytes */
        mdwidth /= 8;
 
        size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
-       if (dwc3_is_usb31(dwc))
-               size = DWC31_GTXFIFOSIZ_TXFDEP(size);
-       else
+       if (DWC3_IP_IS(DWC3))
                size = DWC3_GTXFIFOSIZ_TXFDEP(size);
+       else
+               size = DWC31_GTXFIFOSIZ_TXFDEP(size);
 
        /* FIFO Depth is in MDWDITH bytes. Multiply */
        size *= mdwidth;
@@ -2270,16 +2393,18 @@ static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
        int size;
 
        mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
+       if (DWC3_IP_IS(DWC32))
+               mdwidth += DWC3_GHWPARAMS6_MDWIDTH(dwc->hwparams.hwparams6);
 
        /* MDWIDTH is represented in bits, convert to bytes */
        mdwidth /= 8;
 
        /* All OUT endpoints share a single RxFIFO space */
        size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
-       if (dwc3_is_usb31(dwc))
-               size = DWC31_GRXFIFOSIZ_RXFDEP(size);
-       else
+       if (DWC3_IP_IS(DWC3))
                size = DWC3_GRXFIFOSIZ_RXFDEP(size);
+       else
+               size = DWC31_GRXFIFOSIZ_RXFDEP(size);
 
        /* FIFO depth is in MDWDITH bytes */
        size *= mdwidth;
@@ -2531,10 +2656,8 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
 
        req->request.actual = req->request.length - req->remaining;
 
-       if (!dwc3_gadget_ep_request_completed(req)) {
-               __dwc3_gadget_kick_transfer(dep);
+       if (!dwc3_gadget_ep_request_completed(req))
                goto out;
-       }
 
        dwc3_gadget_giveback(dep, req, status);
 
@@ -2558,41 +2681,53 @@ static void dwc3_gadget_ep_cleanup_completed_requests(struct dwc3_ep *dep,
        }
 }
 
+static bool dwc3_gadget_ep_should_continue(struct dwc3_ep *dep)
+{
+       struct dwc3_request     *req;
+
+       if (!list_empty(&dep->pending_list))
+               return true;
+
+       /*
+        * We only need to check the first entry of the started list. We can
+        * assume the completed requests are removed from the started list.
+        */
+       req = next_request(&dep->started_list);
+       if (!req)
+               return false;
+
+       return !dwc3_gadget_ep_request_completed(req);
+}
+
 static void dwc3_gadget_endpoint_frame_from_event(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event)
 {
        dep->frame_number = event->parameters;
 }
 
-static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
-               const struct dwc3_event_depevt *event)
+static bool dwc3_gadget_endpoint_trbs_complete(struct dwc3_ep *dep,
+               const struct dwc3_event_depevt *event, int status)
 {
        struct dwc3             *dwc = dep->dwc;
-       unsigned                status = 0;
-       bool                    stop = false;
-
-       dwc3_gadget_endpoint_frame_from_event(dep, event);
-
-       if (event->status & DEPEVT_STATUS_BUSERR)
-               status = -ECONNRESET;
-
-       if (event->status & DEPEVT_STATUS_MISSED_ISOC) {
-               status = -EXDEV;
-
-               if (list_empty(&dep->started_list))
-                       stop = true;
-       }
+       bool                    no_started_trb = true;
 
        dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
 
-       if (stop)
+       if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+               goto out;
+
+       if (status == -EXDEV && list_empty(&dep->started_list))
                dwc3_stop_active_transfer(dep, true, true);
+       else if (dwc3_gadget_ep_should_continue(dep))
+               if (__dwc3_gadget_kick_transfer(dep) == 0)
+                       no_started_trb = false;
 
+out:
        /*
         * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
         * See dwc3_gadget_linksts_change_interrupt() for 1st half.
         */
-       if (dwc->revision < DWC3_REVISION_183A) {
+       if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
                u32             reg;
                int             i;
 
@@ -2603,7 +2738,7 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
                                continue;
 
                        if (!list_empty(&dep->started_list))
-                               return;
+                               return no_started_trb;
                }
 
                reg = dwc3_readl(dwc->regs, DWC3_DCTL);
@@ -2612,15 +2747,124 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
 
                dwc->u1u2 = 0;
        }
+
+       return no_started_trb;
+}
+
+static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
+               const struct dwc3_event_depevt *event)
+{
+       int status = 0;
+
+       if (usb_endpoint_xfer_isoc(dep->endpoint.desc))
+               dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+       if (event->status & DEPEVT_STATUS_BUSERR)
+               status = -ECONNRESET;
+
+       if (event->status & DEPEVT_STATUS_MISSED_ISOC)
+               status = -EXDEV;
+
+       dwc3_gadget_endpoint_trbs_complete(dep, event, status);
+}
+
+static void dwc3_gadget_endpoint_transfer_complete(struct dwc3_ep *dep,
+               const struct dwc3_event_depevt *event)
+{
+       int status = 0;
+
+       dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
+
+       if (event->status & DEPEVT_STATUS_BUSERR)
+               status = -ECONNRESET;
+
+       if (dwc3_gadget_endpoint_trbs_complete(dep, event, status))
+               dep->flags &= ~DWC3_EP_WAIT_TRANSFER_COMPLETE;
 }
 
 static void dwc3_gadget_endpoint_transfer_not_ready(struct dwc3_ep *dep,
                const struct dwc3_event_depevt *event)
 {
        dwc3_gadget_endpoint_frame_from_event(dep, event);
+
+       /*
+        * The XferNotReady event is generated only once before the endpoint
+        * starts. It will be generated again when END_TRANSFER command is
+        * issued. For some controller versions, the XferNotReady event may be
+        * generated while the END_TRANSFER command is still in process. Ignore
+        * it and wait for the next XferNotReady event after the command is
+        * completed.
+        */
+       if (dep->flags & DWC3_EP_END_TRANSFER_PENDING)
+               return;
+
        (void) __dwc3_gadget_start_isoc(dep);
 }
 
+static void dwc3_gadget_endpoint_stream_event(struct dwc3_ep *dep,
+               const struct dwc3_event_depevt *event)
+{
+       struct dwc3 *dwc = dep->dwc;
+
+       if (event->status == DEPEVT_STREAMEVT_FOUND) {
+               dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+               goto out;
+       }
+
+       /* Note: NoStream rejection event param value is 0 and not 0xFFFF */
+       switch (event->parameters) {
+       case DEPEVT_STREAM_PRIME:
+               /*
+                * If the host can properly transition the endpoint state from
+                * idle to prime after a NoStream rejection, there's no need to
+                * force restarting the endpoint to reinitiate the stream. To
+                * simplify the check, assume the host follows the USB spec if
+                * it primed the endpoint more than once.
+                */
+               if (dep->flags & DWC3_EP_FORCE_RESTART_STREAM) {
+                       if (dep->flags & DWC3_EP_FIRST_STREAM_PRIMED)
+                               dep->flags &= ~DWC3_EP_FORCE_RESTART_STREAM;
+                       else
+                               dep->flags |= DWC3_EP_FIRST_STREAM_PRIMED;
+               }
+
+               break;
+       case DEPEVT_STREAM_NOSTREAM:
+               if ((dep->flags & DWC3_EP_IGNORE_NEXT_NOSTREAM) ||
+                   !(dep->flags & DWC3_EP_FORCE_RESTART_STREAM) ||
+                   !(dep->flags & DWC3_EP_WAIT_TRANSFER_COMPLETE))
+                       break;
+
+               /*
+                * If the host rejects a stream due to no active stream, by the
+                * USB and xHCI spec, the endpoint will be put back to idle
+                * state. When the host is ready (buffer added/updated), it will
+                * prime the endpoint to inform the usb device controller. This
+                * triggers the device controller to issue ERDY to restart the
+                * stream. However, some hosts don't follow this and keep the
+                * endpoint in the idle state. No prime will come despite host
+                * streams are updated, and the device controller will not be
+                * triggered to generate ERDY to move the next stream data. To
+                * workaround this and maintain compatibility with various
+                * hosts, force to reinitate the stream until the host is ready
+                * instead of waiting for the host to prime the endpoint.
+                */
+               if (DWC3_VER_IS_WITHIN(DWC32, 100A, ANY)) {
+                       unsigned int cmd = DWC3_DGCMD_SET_ENDPOINT_PRIME;
+
+                       dwc3_send_gadget_generic_command(dwc, cmd, dep->number);
+               } else {
+                       dep->flags |= DWC3_EP_DELAY_START;
+                       dwc3_stop_active_transfer(dep, true, true);
+                       return;
+               }
+               break;
+       }
+
+out:
+       dep->flags &= ~DWC3_EP_IGNORE_NEXT_NOSTREAM;
+}
+
 static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                const struct dwc3_event_depevt *event)
 {
@@ -2665,8 +2909,12 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                        dep->flags &= ~DWC3_EP_DELAY_START;
                }
                break;
-       case DWC3_DEPEVT_STREAMEVT:
        case DWC3_DEPEVT_XFERCOMPLETE:
+               dwc3_gadget_endpoint_transfer_complete(dep, event);
+               break;
+       case DWC3_DEPEVT_STREAMEVT:
+               dwc3_gadget_endpoint_stream_event(dep, event);
+               break;
        case DWC3_DEPEVT_RXTXFIFOEVT:
                break;
        }
@@ -2758,6 +3006,14 @@ static void dwc3_stop_active_transfer(struct dwc3_ep *dep, bool force,
        WARN_ON_ONCE(ret);
        dep->resource_index = 0;
 
+       /*
+        * The END_TRANSFER command will cause the controller to generate a
+        * NoStream Event, and it's not due to the host DP NoStream rejection.
+        * Ignore the next NoStream event.
+        */
+       if (dep->stream_capable)
+               dep->flags |= DWC3_EP_IGNORE_NEXT_NOSTREAM;
+
        if (!interrupt)
                dep->flags &= ~DWC3_EP_TRANSFER_STARTED;
        else
@@ -2838,7 +3094,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
         * STAR#9000466709: RTL: Device : Disconnect event not
         * generated if setup packet pending in FIFO
         */
-       if (dwc->revision < DWC3_REVISION_188A) {
+       if (DWC3_VER_IS_PRIOR(DWC3, 188A)) {
                if (dwc->setup_packet_pending)
                        dwc3_gadget_disconnect_interrupt(dwc);
        }
@@ -2897,7 +3153,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
                 * STAR#9000483510: RTL: SS : USB3 reset event may
                 * not be generated always when the link enters poll
                 */
-               if (dwc->revision < DWC3_REVISION_190A)
+               if (DWC3_VER_IS_PRIOR(DWC3, 190A))
                        dwc3_gadget_reset_interrupt(dwc);
 
                dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512);
@@ -2925,7 +3181,7 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
 
        /* Enable USB2 LPM Capability */
 
-       if ((dwc->revision > DWC3_REVISION_194A) &&
+       if (!DWC3_VER_IS_WITHIN(DWC3, ANY, 194A) &&
            (speed != DWC3_DSTS_SUPERSPEED) &&
            (speed != DWC3_DSTS_SUPERSPEED_PLUS)) {
                reg = dwc3_readl(dwc->regs, DWC3_DCFG);
@@ -2944,11 +3200,10 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc)
                 * BESL value in the LPM token is less than or equal to LPM
                 * NYET threshold.
                 */
-               WARN_ONCE(dwc->revision < DWC3_REVISION_240A
-                               && dwc->has_lpm_erratum,
+               WARN_ONCE(DWC3_VER_IS_PRIOR(DWC3, 240A) && dwc->has_lpm_erratum,
                                "LPM Erratum not available on dwc3 revisions < 2.40a\n");
 
-               if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A)
+               if (dwc->has_lpm_erratum && !DWC3_VER_IS_PRIOR(DWC3, 240A))
                        reg |= DWC3_DCTL_NYET_THRES(dwc->lpm_nyet_threshold);
 
                dwc3_gadget_dctl_write_safe(dwc, reg);
@@ -3019,7 +3274,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
         * operational mode
         */
        pwropt = DWC3_GHWPARAMS1_EN_PWROPT(dwc->hwparams.hwparams1);
-       if ((dwc->revision < DWC3_REVISION_250A) &&
+       if (DWC3_VER_IS_PRIOR(DWC3, 250A) &&
                        (pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
                if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
                                (next == DWC3_LINK_STATE_RESUME)) {
@@ -3045,7 +3300,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
         * STAR#9000446952: RTL: Device SS : if U1/U2 ->U0 takes >128us
         * core send LGO_Ux entering U0
         */
-       if (dwc->revision < DWC3_REVISION_183A) {
+       if (DWC3_VER_IS_PRIOR(DWC3, 183A)) {
                if (next == DWC3_LINK_STATE_U0) {
                        u32     u1u2;
                        u32     reg;
@@ -3156,7 +3411,7 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
                break;
        case DWC3_DEVICE_EVENT_EOPF:
                /* It changed to be suspend event for version 2.30a and above */
-               if (dwc->revision >= DWC3_REVISION_230A) {
+               if (!DWC3_VER_IS_PRIOR(DWC3, 230A)) {
                        /*
                         * Ignore suspend event until the gadget enters into
                         * USB_STATE_CONFIGURED state.
@@ -3401,7 +3656,7 @@ int dwc3_gadget_init(struct dwc3 *dwc)
         * is less than super speed because we don't have means, yet, to tell
         * composite.c that we are USB 2.0 + LPM ECN.
         */
-       if (dwc->revision < DWC3_REVISION_220A &&
+       if (DWC3_VER_IS_PRIOR(DWC3, 220A) &&
            !dwc->dis_metastability_quirk)
                dev_info(dwc->dev, "changing max_speed on rev %08x\n",
                                dwc->revision);
index fbc7d80..24dca38 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * gadget.h - DesignWare USB3 DRD Gadget Header
  *
index 86dbd01..bef1c1a 100644 (file)
@@ -104,7 +104,7 @@ int dwc3_host_init(struct dwc3 *dwc)
         *
         * This following flag tells XHCI to do just that.
         */
-       if (dwc->revision <= DWC3_REVISION_300A)
+       if (DWC3_VER_IS_WITHIN(DWC3, ANY, 300A))
                props[prop_idx++] = PROPERTY_ENTRY_BOOL("quirk-broken-port-ped");
 
        if (prop_idx) {
index 70acdf9..9bbe5d4 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /**
  * io.h - DesignWare USB3 DRD IO Header
  *
index 3054b89..4c4fc6c 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /**
  * trace.h - DesignWare USB3 DRD Controller Trace Support
  *
index 171280c..04ba11f 100644 (file)
@@ -18,7 +18,6 @@
 #include <asm/fixmap.h>
 #include <linux/bcd.h>
 #include <linux/export.h>
-#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/delay.h>
 #include <linux/kthread.h>
index 6e2b726..8b4d71d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * xhci-dbc.h - xHCI debug capability early driver
  *
index cb4950c..5c1eb96 100644 (file)
@@ -96,40 +96,43 @@ function_descriptors(struct usb_function *f,
 }
 
 /**
- * next_ep_desc() - advance to the next EP descriptor
+ * next_desc() - advance to the next desc_type descriptor
  * @t: currect pointer within descriptor array
+ * @desc_type: descriptor type
  *
- * Return: next EP descriptor or NULL
+ * Return: next desc_type descriptor or NULL
  *
- * Iterate over @t until either EP descriptor found or
+ * Iterate over @t until either desc_type descriptor found or
  * NULL (that indicates end of list) encountered
  */
 static struct usb_descriptor_header**
-next_ep_desc(struct usb_descriptor_header **t)
+next_desc(struct usb_descriptor_header **t, u8 desc_type)
 {
        for (; *t; t++) {
-               if ((*t)->bDescriptorType == USB_DT_ENDPOINT)
+               if ((*t)->bDescriptorType == desc_type)
                        return t;
        }
        return NULL;
 }
 
 /*
- * for_each_ep_desc()- iterate over endpoint descriptors in the
- *             descriptors list
- * @start:     pointer within descriptor array.
- * @ep_desc:   endpoint descriptor to use as the loop cursor
+ * for_each_desc() - iterate over desc_type descriptors in the
+ * descriptors list
+ * @start: pointer within descriptor array.
+ * @iter_desc: desc_type descriptor to use as the loop cursor
+ * @desc_type: wanted descriptr type
  */
-#define for_each_ep_desc(start, ep_desc) \
-       for (ep_desc = next_ep_desc(start); \
-             ep_desc; ep_desc = next_ep_desc(ep_desc+1))
+#define for_each_desc(start, iter_desc, desc_type) \
+       for (iter_desc = next_desc(start, desc_type); \
+            iter_desc; iter_desc = next_desc(iter_desc + 1, desc_type))
 
 /**
- * config_ep_by_speed() - configures the given endpoint
+ * config_ep_by_speed_and_alt() - configures the given endpoint
  * according to gadget speed.
  * @g: pointer to the gadget
  * @f: usb function
  * @_ep: the endpoint to configure
+ * @alt: alternate setting number
  *
  * Return: error code, 0 on success
  *
@@ -142,11 +145,13 @@ next_ep_desc(struct usb_descriptor_header **t)
  * Note: the supplied function should hold all the descriptors
  * for supported speeds
  */
-int config_ep_by_speed(struct usb_gadget *g,
-                       struct usb_function *f,
-                       struct usb_ep *_ep)
+int config_ep_by_speed_and_alt(struct usb_gadget *g,
+                               struct usb_function *f,
+                               struct usb_ep *_ep,
+                               u8 alt)
 {
        struct usb_endpoint_descriptor *chosen_desc = NULL;
+       struct usb_interface_descriptor *int_desc = NULL;
        struct usb_descriptor_header **speed_desc = NULL;
 
        struct usb_ss_ep_comp_descriptor *comp_desc = NULL;
@@ -182,8 +187,21 @@ int config_ep_by_speed(struct usb_gadget *g,
        default:
                speed_desc = f->fs_descriptors;
        }
+
+       /* find correct alternate setting descriptor */
+       for_each_desc(speed_desc, d_spd, USB_DT_INTERFACE) {
+               int_desc = (struct usb_interface_descriptor *)*d_spd;
+
+               if (int_desc->bAlternateSetting == alt) {
+                       speed_desc = d_spd;
+                       goto intf_found;
+               }
+       }
+       return -EIO;
+
+intf_found:
        /* find descriptors */
-       for_each_ep_desc(speed_desc, d_spd) {
+       for_each_desc(speed_desc, d_spd, USB_DT_ENDPOINT) {
                chosen_desc = (struct usb_endpoint_descriptor *)*d_spd;
                if (chosen_desc->bEndpointAddress == _ep->address)
                        goto ep_found;
@@ -237,6 +255,32 @@ ep_found:
        }
        return 0;
 }
+EXPORT_SYMBOL_GPL(config_ep_by_speed_and_alt);
+
+/**
+ * config_ep_by_speed() - configures the given endpoint
+ * according to gadget speed.
+ * @g: pointer to the gadget
+ * @f: usb function
+ * @_ep: the endpoint to configure
+ *
+ * Return: error code, 0 on success
+ *
+ * This function chooses the right descriptors for a given
+ * endpoint according to gadget speed and saves it in the
+ * endpoint desc field. If the endpoint already has a descriptor
+ * assigned to it - overwrites it with currently corresponding
+ * descriptor. The endpoint maxpacket field is updated according
+ * to the chosen descriptor.
+ * Note: the supplied function should hold all the descriptors
+ * for supported speeds
+ */
+int config_ep_by_speed(struct usb_gadget *g,
+                       struct usb_function *f,
+                       struct usb_ep *_ep)
+{
+       return config_ep_by_speed_and_alt(g, f, _ep, 0);
+}
 EXPORT_SYMBOL_GPL(config_ep_by_speed);
 
 /**
index 6a9aa44..9dc06a4 100644 (file)
@@ -13,8 +13,6 @@
 int check_user_usb_string(const char *name,
                struct usb_gadget_strings *stringtab_dev)
 {
-       unsigned primary_lang;
-       unsigned sub_lang;
        u16 num;
        int ret;
 
@@ -22,17 +20,7 @@ int check_user_usb_string(const char *name,
        if (ret)
                return ret;
 
-       primary_lang = num & 0x3ff;
-       sub_lang = num >> 10;
-
-       /* simple sanity check for valid langid */
-       switch (primary_lang) {
-       case 0:
-       case 0x62 ... 0xfe:
-       case 0x100 ... 0x3ff:
-               return -EINVAL;
-       }
-       if (!sub_lang)
+       if (!usb_validate_langid(num))
                return -EINVAL;
 
        stringtab_dev->language = num;
index 7c152c2..200596e 100644 (file)
@@ -723,6 +723,20 @@ static void acm_free_func(struct usb_function *f)
        kfree(acm);
 }
 
+static void acm_resume(struct usb_function *f)
+{
+       struct f_acm *acm = func_to_acm(f);
+
+       gserial_resume(&acm->port);
+}
+
+static void acm_suspend(struct usb_function *f)
+{
+       struct f_acm *acm = func_to_acm(f);
+
+       gserial_suspend(&acm->port);
+}
+
 static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
 {
        struct f_serial_opts *opts;
@@ -750,6 +764,8 @@ static struct usb_function *acm_alloc_func(struct usb_function_instance *fi)
        acm->port_num = opts->port_num;
        acm->port.func.unbind = acm_unbind;
        acm->port.func.free_func = acm_free_func;
+       acm->port.func.resume = acm_resume;
+       acm->port.func.suspend = acm_suspend;
 
        return &acm->port.func;
 }
index b81a91d..cfcc4e8 100644 (file)
@@ -291,8 +291,6 @@ static int eem_bind(struct usb_configuration *c, struct usb_function *f)
                goto fail;
        eem->port.out_ep = ep;
 
-       status = -ENOMEM;
-
        /* support all relevant hardware speeds... we expect that when
         * hardware is dual speed, all bulk-capable endpoints work at
         * both speeds
index 10f01f9..494f853 100644 (file)
@@ -2508,7 +2508,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
                os_descs_count = get_unaligned_le32(data);
                data += 4;
                len -= 4;
-       };
+       }
 
        /* Read descriptors */
        raw_descs = data;
index 1406255..e627138 100644 (file)
@@ -348,6 +348,20 @@ static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
        usb_free_all_descriptors(f);
 }
 
+static void gser_resume(struct usb_function *f)
+{
+       struct f_gser *gser = func_to_gser(f);
+
+       gserial_resume(&gser->port);
+}
+
+static void gser_suspend(struct usb_function *f)
+{
+       struct f_gser *gser = func_to_gser(f);
+
+       gserial_suspend(&gser->port);
+}
+
 static struct usb_function *gser_alloc(struct usb_function_instance *fi)
 {
        struct f_gser   *gser;
@@ -369,6 +383,8 @@ static struct usb_function *gser_alloc(struct usb_function_instance *fi)
        gser->port.func.set_alt = gser_set_alt;
        gser->port.func.disable = gser_disable;
        gser->port.func.free_func = gser_free;
+       gser->port.func.resume = gser_resume;
+       gser->port.func.suspend = gser_suspend;
 
        return &gser->port.func;
 }
index 3650493..2979cbe 100644 (file)
@@ -531,6 +531,7 @@ static int uasp_prepare_r_request(struct usbg_cmd *cmd)
                stream->req_in->sg = se_cmd->t_data_sg;
        }
 
+       stream->req_in->is_last = 1;
        stream->req_in->complete = uasp_status_data_cmpl;
        stream->req_in->length = se_cmd->data_length;
        stream->req_in->context = cmd;
@@ -554,6 +555,7 @@ static void uasp_prepare_status(struct usbg_cmd *cmd)
         */
        iu->len = cpu_to_be16(se_cmd->scsi_sense_length);
        iu->status = se_cmd->scsi_status;
+       stream->req_status->is_last = 1;
        stream->req_status->context = cmd;
        stream->req_status->length = se_cmd->scsi_sense_length + 16;
        stream->req_status->buf = iu;
@@ -991,6 +993,7 @@ static int usbg_prepare_w_request(struct usbg_cmd *cmd, struct usb_request *req)
                req->sg = se_cmd->t_data_sg;
        }
 
+       req->is_last = 1;
        req->complete = usbg_data_write_cmpl;
        req->length = se_cmd->data_length;
        req->context = cmd;
index a81a177..1db972d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  *     f_uvc.h  --  USB Video Class Gadget driver
  *
index c7e3a70..f6167f7 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * RNDIS       Definitions for Remote NDIS
  *
index 81d3d4e..5ea6b86 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * u_audio.h -- interface to USB gadget "ALSA sound card" utilities
  *
index 098ece5..77cfb89 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_ecm.h
  *
index 921386a..3bd85df 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_eem.h
  *
index 332307d..10dd640 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * u_ether.h -- interface to USB gadget "ethernet link" utilities
  *
index d8b9248..bd92b57 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_ether_configfs.h
  *
index f9b0cf6..f102ec2 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_fs.h
  *
index ce4f076..2f7a373 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_gether.h
  *
index 1594bfa..84e6da3 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_hid.h
  *
index 29bf006..f6e14af 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_midi.h
  *
index 70da320..5408854 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_ncm.h
  *
index 12fb613..c53233b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * u_phonet.h - interface to Phonet
  *
index 7879776..318205f 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_printer.h
  *
index 1e148b7..a8c409b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_rndis.h
  *
index 8167d37..3cfc6e2 100644 (file)
@@ -120,6 +120,8 @@ struct gs_port {
        wait_queue_head_t       drain_wait;     /* wait while writes drain */
        bool                    write_busy;
        wait_queue_head_t       close_wait;
+       bool                    suspended;      /* port suspended */
+       bool                    start_delayed;  /* delay start when suspended */
 
        /* REVISIT this state ... */
        struct usb_cdc_line_coding port_line_coding;    /* 8-N-1 etc */
@@ -630,13 +632,19 @@ static int gs_open(struct tty_struct *tty, struct file *file)
 
        /* if connected, start the I/O stream */
        if (port->port_usb) {
-               struct gserial  *gser = port->port_usb;
-
-               pr_debug("gs_open: start ttyGS%d\n", port->port_num);
-               gs_start_io(port);
-
-               if (gser->connect)
-                       gser->connect(gser);
+               /* if port is suspended, wait resume to start I/0 stream */
+               if (!port->suspended) {
+                       struct gserial  *gser = port->port_usb;
+
+                       pr_debug("gs_open: start ttyGS%d\n", port->port_num);
+                       gs_start_io(port);
+
+                       if (gser->connect)
+                               gser->connect(gser);
+               } else {
+                       pr_debug("delay start of ttyGS%d\n", port->port_num);
+                       port->start_delayed = true;
+               }
        }
 
        pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file);
@@ -680,7 +688,7 @@ raced_with_open:
        pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file);
 
        gser = port->port_usb;
-       if (gser && gser->disconnect)
+       if (gser && !port->suspended && gser->disconnect)
                gser->disconnect(gser);
 
        /* wait for circular write buffer to drain, disconnect, or at
@@ -708,6 +716,7 @@ raced_with_open:
        else
                kfifo_reset(&port->port_write_buf);
 
+       port->start_delayed = false;
        port->port.count = 0;
        port->port.tty = NULL;
 
@@ -1403,6 +1412,38 @@ void gserial_disconnect(struct gserial *gser)
 }
 EXPORT_SYMBOL_GPL(gserial_disconnect);
 
+void gserial_suspend(struct gserial *gser)
+{
+       struct gs_port  *port = gser->ioport;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       port->suspended = true;
+       spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_suspend);
+
+void gserial_resume(struct gserial *gser)
+{
+       struct gs_port *port = gser->ioport;
+       unsigned long   flags;
+
+       spin_lock_irqsave(&port->port_lock, flags);
+       port->suspended = false;
+       if (!port->start_delayed) {
+               spin_unlock_irqrestore(&port->port_lock, flags);
+               return;
+       }
+
+       pr_debug("delayed start ttyGS%d\n", port->port_num);
+       gs_start_io(port);
+       if (gser->connect)
+               gser->connect(gser);
+       port->start_delayed = false;
+       spin_unlock_irqrestore(&port->port_lock, flags);
+}
+EXPORT_SYMBOL_GPL(gserial_resume);
+
 static int userial_init(void)
 {
        unsigned                        i;
index e5b08ab..cadb76e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * u_serial.h - interface to USB gadget "serial port"/TTY utilities
  *
@@ -68,6 +68,8 @@ ssize_t gserial_get_console(unsigned char port_num, char *page);
 /* connect/disconnect is handled by individual functions */
 int gserial_connect(struct gserial *, u8 port_num);
 void gserial_disconnect(struct gserial *);
+void gserial_suspend(struct gserial *p);
+void gserial_resume(struct gserial *p);
 
 /* functions are bound to configurations by a config or gadget driver */
 int gser_bind_config(struct usb_configuration *c, u8 port_num);
index 3f7ccec..2cd15d9 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_tcm.h
  *
index 6f1a9d7..39c0e29 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_uac1.h - Utility definitions for UAC1 function
  *
index 5c1bdf4..b5df9bc 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * u_uac1.h -- interface to USB gadget "ALSA AUDIO" utilities
  *
index 8204879..b503571 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_uac2.h
  *
index 16da49a..9a01a7d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * u_uvc.h
  *
index 1473d25..23ee253 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  *     uvc_gadget.h  --  USB Video Class Gadget driver
  *
@@ -77,6 +77,8 @@ struct uvc_video {
        struct uvc_device *uvc;
        struct usb_ep *ep;
 
+       struct work_struct pump;
+
        /* Frame parameters */
        u8 bpp;
        u32 fcc;
index 341391d..7e1d7ca 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * uvc_configfs.h
  *
index 495f0ec..4ca89ea 100644 (file)
@@ -169,7 +169,9 @@ uvc_v4l2_qbuf(struct file *file, void *fh, struct v4l2_buffer *b)
        if (ret < 0)
                return ret;
 
-       return uvcg_video_pump(video);
+       schedule_work(&video->pump);
+
+       return ret;
 }
 
 static int
index 452d710..1576005 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  *     uvc_v4l2.h  --  USB Video Class Gadget driver
  *
index 5c042f3..633e23d 100644 (file)
@@ -142,44 +142,12 @@ static int uvcg_video_ep_queue(struct uvc_video *video, struct usb_request *req)
        return ret;
 }
 
-/*
- * I somehow feel that synchronisation won't be easy to achieve here. We have
- * three events that control USB requests submission:
- *
- * - USB request completion: the completion handler will resubmit the request
- *   if a video buffer is available.
- *
- * - USB interface setting selection: in response to a SET_INTERFACE request,
- *   the handler will start streaming if a video buffer is available and if
- *   video is not currently streaming.
- *
- * - V4L2 buffer queueing: the driver will start streaming if video is not
- *   currently streaming.
- *
- * Race conditions between those 3 events might lead to deadlocks or other
- * nasty side effects.
- *
- * The "video currently streaming" condition can't be detected by the irqqueue
- * being empty, as a request can still be in flight. A separate "queue paused"
- * flag is thus needed.
- *
- * The paused flag will be set when we try to retrieve the irqqueue head if the
- * queue is empty, and cleared when we queue a buffer.
- *
- * The USB request completion handler will get the buffer at the irqqueue head
- * under protection of the queue spinlock. If the queue is empty, the streaming
- * paused flag will be set. Right after releasing the spinlock a userspace
- * application can queue a buffer. The flag will then cleared, and the ioctl
- * handler will restart the video stream.
- */
 static void
 uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
 {
        struct uvc_video *video = req->context;
        struct uvc_video_queue *queue = &video->queue;
-       struct uvc_buffer *buf;
        unsigned long flags;
-       int ret;
 
        switch (req->status) {
        case 0:
@@ -188,39 +156,20 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req)
        case -ESHUTDOWN:        /* disconnect from host. */
                uvcg_dbg(&video->uvc->func, "VS request cancelled.\n");
                uvcg_queue_cancel(queue, 1);
-               goto requeue;
+               break;
 
        default:
                uvcg_info(&video->uvc->func,
                          "VS request completed with status %d.\n",
                          req->status);
                uvcg_queue_cancel(queue, 0);
-               goto requeue;
        }
 
-       spin_lock_irqsave(&video->queue.irqlock, flags);
-       buf = uvcg_queue_head(&video->queue);
-       if (buf == NULL) {
-               spin_unlock_irqrestore(&video->queue.irqlock, flags);
-               goto requeue;
-       }
-
-       video->encode(req, video, buf);
-
-       ret = uvcg_video_ep_queue(video, req);
-       spin_unlock_irqrestore(&video->queue.irqlock, flags);
-
-       if (ret < 0) {
-               uvcg_queue_cancel(queue, 0);
-               goto requeue;
-       }
-
-       return;
-
-requeue:
        spin_lock_irqsave(&video->req_lock, flags);
        list_add_tail(&req->list, &video->req_free);
        spin_unlock_irqrestore(&video->req_lock, flags);
+
+       schedule_work(&video->pump);
 }
 
 static int
@@ -294,18 +243,15 @@ error:
  * This function fills the available USB requests (listed in req_free) with
  * video data from the queued buffers.
  */
-int uvcg_video_pump(struct uvc_video *video)
+static void uvcg_video_pump(struct work_struct *work)
 {
+       struct uvc_video *video = container_of(work, struct uvc_video, pump);
        struct uvc_video_queue *queue = &video->queue;
        struct usb_request *req;
        struct uvc_buffer *buf;
        unsigned long flags;
        int ret;
 
-       /* FIXME TODO Race between uvcg_video_pump and requests completion
-        * handler ???
-        */
-
        while (1) {
                /* Retrieve the first available USB request, protected by the
                 * request lock.
@@ -313,7 +259,7 @@ int uvcg_video_pump(struct uvc_video *video)
                spin_lock_irqsave(&video->req_lock, flags);
                if (list_empty(&video->req_free)) {
                        spin_unlock_irqrestore(&video->req_lock, flags);
-                       return 0;
+                       return;
                }
                req = list_first_entry(&video->req_free, struct usb_request,
                                        list);
@@ -345,7 +291,7 @@ int uvcg_video_pump(struct uvc_video *video)
        spin_lock_irqsave(&video->req_lock, flags);
        list_add_tail(&req->list, &video->req_free);
        spin_unlock_irqrestore(&video->req_lock, flags);
-       return 0;
+       return;
 }
 
 /*
@@ -363,6 +309,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
        }
 
        if (!enable) {
+               cancel_work_sync(&video->pump);
+               uvcg_queue_cancel(&video->queue, 0);
+
                for (i = 0; i < UVC_NUM_REQUESTS; ++i)
                        if (video->req[i])
                                usb_ep_dequeue(video->ep, video->req[i]);
@@ -384,7 +333,9 @@ int uvcg_video_enable(struct uvc_video *video, int enable)
        } else
                video->encode = uvc_video_encode_isoc;
 
-       return uvcg_video_pump(video);
+       schedule_work(&video->pump);
+
+       return ret;
 }
 
 /*
@@ -394,6 +345,7 @@ int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc)
 {
        INIT_LIST_HEAD(&video->req_free);
        spin_lock_init(&video->req_lock);
+       INIT_WORK(&video->pump, uvcg_video_pump);
 
        video->uvc = uvc;
        video->fcc = V4L2_PIX_FMT_YUYV;
index dff1210..03adeef 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  *     uvc_video.h  --  USB Video Class Gadget driver
  *
@@ -14,8 +14,6 @@
 
 struct uvc_video;
 
-int uvcg_video_pump(struct uvc_video *video);
-
 int uvcg_video_enable(struct uvc_video *video, int enable);
 
 int uvcg_video_init(struct uvc_video *video, struct uvc_device *uvc);
index f18f775..9ed22c5 100644 (file)
@@ -229,18 +229,8 @@ static struct usb_composite_driver msg_driver = {
        .unbind         = msg_unbind,
 };
 
+module_usb_composite_driver(msg_driver);
+
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR("Michal Nazarewicz");
 MODULE_LICENSE("GPL");
-
-static int __init msg_init(void)
-{
-       return usb_composite_probe(&msg_driver);
-}
-module_init(msg_init);
-
-static void __exit msg_cleanup(void)
-{
-       usb_composite_unregister(&msg_driver);
-}
-module_exit(msg_cleanup);
index f8d35dd..cdf9691 100644 (file)
@@ -134,11 +134,15 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
        }
 
        /* Handle device interrupts */
-       for (i = 0; i < vhub->max_ports; i++) {
-               u32 dev_mask = VHUB_IRQ_DEVICE1 << i;
+       if (istat & vhub->port_irq_mask) {
+               unsigned long bitmap = istat;
+               int offset = VHUB_IRQ_DEV1_BIT;
+               int size = VHUB_IRQ_DEV1_BIT + vhub->max_ports;
 
-               if (istat & dev_mask)
+               for_each_set_bit_from(offset, &bitmap, size) {
+                       i = offset - VHUB_IRQ_DEV1_BIT;
                        ast_vhub_dev_irq(&vhub->ports[i].dev);
+               }
        }
 
        /* Handle top-level vHub EP0 interrupts */
@@ -332,6 +336,8 @@ static int ast_vhub_probe(struct platform_device *pdev)
 
        spin_lock_init(&vhub->lock);
        vhub->pdev = 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);
@@ -402,7 +408,9 @@ static int ast_vhub_probe(struct platform_device *pdev)
                goto err;
 
        /* Init hub emulation */
-       ast_vhub_init_hub(vhub);
+       rc = ast_vhub_init_hub(vhub);
+       if (rc)
+               goto err;
 
        /* Initialize HW */
        ast_vhub_init_hw(vhub);
index 6e565c3..6497185 100644 (file)
@@ -50,6 +50,7 @@
 #define KERNEL_VER     bin2bcd(((LINUX_VERSION_CODE >> 8) & 0x0ff))
 
 enum {
+       AST_VHUB_STR_INDEX_MAX = 4,
        AST_VHUB_STR_MANUF = 3,
        AST_VHUB_STR_PRODUCT = 2,
        AST_VHUB_STR_SERIAL = 1,
@@ -72,13 +73,6 @@ static const struct usb_device_descriptor ast_vhub_dev_desc = {
        .bNumConfigurations     = 1,
 };
 
-/* Patches to the above when forcing USB1 mode */
-static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
-{
-       desc->bcdUSB = cpu_to_le16(0x0100);
-       desc->bDeviceProtocol = 0;
-}
-
 /*
  * Configuration descriptor: same comments as above
  * regarding handling USB1 mode.
@@ -302,31 +296,81 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
        if (len > dsize)
                len = dsize;
 
-       /* Patch it if forcing USB1 */
-       if (desc_type == USB_DT_DEVICE && ep->vhub->force_usb1)
-               ast_vhub_patch_dev_desc_usb1(ep->buf);
-
        /* Shoot it from the EP buffer */
        return ast_vhub_reply(ep, NULL, len);
 }
 
+static struct usb_gadget_strings*
+ast_vhub_str_of_container(struct usb_gadget_string_container *container)
+{
+       return (struct usb_gadget_strings *)container->stash;
+}
+
+static int ast_vhub_collect_languages(struct ast_vhub *vhub, void *buf,
+                                     size_t size)
+{
+       int rc, hdr_len, nlangs, max_langs;
+       struct usb_gadget_strings *lang_str;
+       struct usb_gadget_string_container *container;
+       struct usb_string_descriptor *sdesc = buf;
+
+       nlangs = 0;
+       hdr_len = sizeof(struct usb_descriptor_header);
+       max_langs = (size - hdr_len) / sizeof(sdesc->wData[0]);
+       list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+               if (nlangs >= max_langs)
+                       break;
+
+               lang_str = ast_vhub_str_of_container(container);
+               sdesc->wData[nlangs++] = cpu_to_le16(lang_str->language);
+       }
+
+       rc = hdr_len + nlangs * sizeof(sdesc->wData[0]);
+       sdesc->bLength = rc;
+       sdesc->bDescriptorType = USB_DT_STRING;
+
+       return rc;
+}
+
+static struct usb_gadget_strings *ast_vhub_lookup_string(struct ast_vhub *vhub,
+                                                        u16 lang_id)
+{
+       struct usb_gadget_strings *lang_str;
+       struct usb_gadget_string_container *container;
+
+       list_for_each_entry(container, &vhub->vhub_str_desc, list) {
+               lang_str = ast_vhub_str_of_container(container);
+               if (lang_str->language == lang_id)
+                       return lang_str;
+       }
+
+       return NULL;
+}
+
 static int ast_vhub_rep_string(struct ast_vhub_ep *ep,
                               u8 string_id, u16 lang_id,
                               u16 len)
 {
-       int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
-                                       string_id, ep->buf);
+       int rc;
+       u8 buf[256];
+       struct ast_vhub *vhub = ep->vhub;
+       struct usb_gadget_strings *lang_str;
 
-       /*
-        * This should never happen unless we put too big strings in
-        * the array above
-        */
-       BUG_ON(rc >= AST_VHUB_EP0_MAX_PACKET);
+       if (string_id == 0) {
+               rc = ast_vhub_collect_languages(vhub, buf, sizeof(buf));
+       } else {
+               lang_str = ast_vhub_lookup_string(vhub, lang_id);
+               if (!lang_str)
+                       return std_req_stall;
 
-       if (rc < 0)
+               rc = usb_gadget_get_string(lang_str, string_id, buf);
+       }
+
+       if (rc < 0 || rc >= AST_VHUB_EP0_MAX_PACKET)
                return std_req_stall;
 
        /* Shoot it from the EP buffer */
+       memcpy(ep->buf, buf, rc);
        return ast_vhub_reply(ep, NULL, min_t(u16, rc, len));
 }
 
@@ -832,11 +876,148 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
        writel(0, vhub->regs + AST_VHUB_EP1_STS_CHG);
 }
 
-static void ast_vhub_init_desc(struct ast_vhub *vhub)
+static void ast_vhub_of_parse_dev_desc(struct ast_vhub *vhub,
+                                      const struct device_node *vhub_np)
+{
+       u16 id;
+       u32 data;
+
+       if (!of_property_read_u32(vhub_np, "vhub-vendor-id", &data)) {
+               id = (u16)data;
+               vhub->vhub_dev_desc.idVendor = cpu_to_le16(id);
+       }
+       if (!of_property_read_u32(vhub_np, "vhub-product-id", &data)) {
+               id = (u16)data;
+               vhub->vhub_dev_desc.idProduct = cpu_to_le16(id);
+       }
+       if (!of_property_read_u32(vhub_np, "vhub-device-revision", &data)) {
+               id = (u16)data;
+               vhub->vhub_dev_desc.bcdDevice = cpu_to_le16(id);
+       }
+}
+
+static void ast_vhub_fixup_usb1_dev_desc(struct ast_vhub *vhub)
+{
+       vhub->vhub_dev_desc.bcdUSB = cpu_to_le16(0x0100);
+       vhub->vhub_dev_desc.bDeviceProtocol = 0;
+}
+
+static struct usb_gadget_string_container*
+ast_vhub_str_container_alloc(struct ast_vhub *vhub)
+{
+       unsigned int size;
+       struct usb_string *str_array;
+       struct usb_gadget_strings *lang_str;
+       struct usb_gadget_string_container *container;
+
+       size = sizeof(*container);
+       size += sizeof(struct usb_gadget_strings);
+       size += sizeof(struct usb_string) * AST_VHUB_STR_INDEX_MAX;
+       container = devm_kzalloc(&vhub->pdev->dev, size, GFP_KERNEL);
+       if (!container)
+               return ERR_PTR(-ENOMEM);
+
+       lang_str = ast_vhub_str_of_container(container);
+       str_array = (struct usb_string *)(lang_str + 1);
+       lang_str->strings = str_array;
+       return container;
+}
+
+static void ast_vhub_str_deep_copy(struct usb_gadget_strings *dest,
+                                  const struct usb_gadget_strings *src)
 {
+       struct usb_string *src_array = src->strings;
+       struct usb_string *dest_array = dest->strings;
+
+       dest->language = src->language;
+       if (src_array && dest_array) {
+               do {
+                       *dest_array = *src_array;
+                       dest_array++;
+                       src_array++;
+               } while (src_array->s);
+       }
+}
+
+static int ast_vhub_str_alloc_add(struct ast_vhub *vhub,
+                                 const struct usb_gadget_strings *src_str)
+{
+       struct usb_gadget_strings *dest_str;
+       struct usb_gadget_string_container *container;
+
+       container = ast_vhub_str_container_alloc(vhub);
+       if (IS_ERR(container))
+               return PTR_ERR(container);
+
+       dest_str = ast_vhub_str_of_container(container);
+       ast_vhub_str_deep_copy(dest_str, src_str);
+       list_add_tail(&container->list, &vhub->vhub_str_desc);
+
+       return 0;
+}
+
+static const struct {
+       const char *name;
+       u8 id;
+} str_id_map[] = {
+       {"manufacturer",        AST_VHUB_STR_MANUF},
+       {"product",             AST_VHUB_STR_PRODUCT},
+       {"serial-number",       AST_VHUB_STR_SERIAL},
+       {},
+};
+
+static int ast_vhub_of_parse_str_desc(struct ast_vhub *vhub,
+                                     const struct device_node *desc_np)
+{
+       u32 langid;
+       int ret = 0;
+       int i, offset;
+       const char *str;
+       struct device_node *child;
+       struct usb_string str_array[AST_VHUB_STR_INDEX_MAX];
+       struct usb_gadget_strings lang_str = {
+               .strings = (struct usb_string *)str_array,
+       };
+
+       for_each_child_of_node(desc_np, child) {
+               if (of_property_read_u32(child, "reg", &langid))
+                       continue; /* no language identifier specified */
+
+               if (!usb_validate_langid(langid))
+                       continue; /* invalid language identifier */
+
+               lang_str.language = langid;
+               for (i = offset = 0; str_id_map[i].name; i++) {
+                       str = of_get_property(child, str_id_map[i].name, NULL);
+                       if (str) {
+                               str_array[offset].s = str;
+                               str_array[offset].id = str_id_map[i].id;
+                               offset++;
+                       }
+               }
+               str_array[offset].id = 0;
+               str_array[offset].s = NULL;
+
+               ret = ast_vhub_str_alloc_add(vhub, &lang_str);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+static int ast_vhub_init_desc(struct ast_vhub *vhub)
+{
+       int ret;
+       struct device_node *desc_np;
+       const struct device_node *vhub_np = vhub->pdev->dev.of_node;
+
        /* Initialize vhub Device Descriptor. */
        memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
                sizeof(vhub->vhub_dev_desc));
+       ast_vhub_of_parse_dev_desc(vhub, vhub_np);
+       if (vhub->force_usb1)
+               ast_vhub_fixup_usb1_dev_desc(vhub);
 
        /* Initialize vhub Configuration Descriptor. */
        memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
@@ -848,15 +1029,20 @@ static void ast_vhub_init_desc(struct ast_vhub *vhub)
        vhub->vhub_hub_desc.bNbrPorts = vhub->max_ports;
 
        /* Initialize vhub String Descriptors. */
-       memcpy(&vhub->vhub_str_desc, &ast_vhub_strings,
-               sizeof(vhub->vhub_str_desc));
+       INIT_LIST_HEAD(&vhub->vhub_str_desc);
+       desc_np = of_get_child_by_name(vhub_np, "vhub-strings");
+       if (desc_np)
+               ret = ast_vhub_of_parse_str_desc(vhub, desc_np);
+       else
+               ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings);
+
+       return ret;
 }
 
-void ast_vhub_init_hub(struct ast_vhub *vhub)
+int ast_vhub_init_hub(struct ast_vhub *vhub)
 {
        vhub->speed = USB_SPEED_UNKNOWN;
        INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
 
-       ast_vhub_init_desc(vhub);
+       return ast_vhub_init_desc(vhub);
 }
-
index fac79ef..2e5a1ef 100644 (file)
 #define VHUB_CTRL_UPSTREAM_CONNECT             (1 << 0)
 
 /* IER & ISR */
+#define VHUB_IRQ_DEV1_BIT                      9
 #define VHUB_IRQ_USB_CMD_DEADLOCK              (1 << 18)
 #define VHUB_IRQ_EP_POOL_NAK                   (1 << 17)
 #define VHUB_IRQ_EP_POOL_ACK_STALL             (1 << 16)
-#define VHUB_IRQ_DEVICE5                       (1 << 13)
-#define VHUB_IRQ_DEVICE4                       (1 << 12)
-#define VHUB_IRQ_DEVICE3                       (1 << 11)
-#define VHUB_IRQ_DEVICE2                       (1 << 10)
-#define VHUB_IRQ_DEVICE1                       (1 << 9)
+#define VHUB_IRQ_DEVICE1                       (1 << (VHUB_IRQ_DEV1_BIT))
 #define VHUB_IRQ_BUS_RESUME                    (1 << 8)
 #define VHUB_IRQ_BUS_SUSPEND                   (1 << 7)
 #define VHUB_IRQ_BUS_RESET                     (1 << 6)
@@ -402,6 +399,7 @@ struct ast_vhub {
        /* Per-port info */
        struct ast_vhub_port            *ports;
        u32                             max_ports;
+       u32                             port_irq_mask;
 
        /* Generic EP data structures */
        struct ast_vhub_ep              *epns;
@@ -423,7 +421,7 @@ struct ast_vhub {
        struct usb_device_descriptor    vhub_dev_desc;
        struct ast_vhub_full_cdesc      vhub_conf_desc;
        struct usb_hub_descriptor       vhub_hub_desc;
-       struct usb_gadget_strings       vhub_str_desc;
+       struct list_head                vhub_str_desc;
 };
 
 /* Standard request handlers result codes */
@@ -533,7 +531,7 @@ int __ast_vhub_simple_reply(struct ast_vhub_ep *ep, int len, ...);
                               __VA_ARGS__)
 
 /* hub.c */
-void ast_vhub_init_hub(struct ast_vhub *vhub);
+int ast_vhub_init_hub(struct ast_vhub *vhub);
 enum std_req_rc ast_vhub_std_hub_request(struct ast_vhub_ep *ep,
                                         struct usb_ctrlrequest *crq);
 enum std_req_rc ast_vhub_class_hub_request(struct ast_vhub_ep *ep,
index b771a85..d69f61f 100644 (file)
@@ -2043,10 +2043,56 @@ static const struct usba_udc_errata at91sam9g45_errata = {
        .pulse_bias = at91sam9g45_pulse_bias,
 };
 
+static const struct usba_ep_config ep_config_sam9[] __initconst = {
+       { .nr_banks = 1 },                              /* ep 0 */
+       { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
+       { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
+       { .nr_banks = 3, .can_dma = 1 },                /* ep 3 */
+       { .nr_banks = 3, .can_dma = 1 },                /* ep 4 */
+       { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
+       { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
+};
+
+static const struct usba_ep_config ep_config_sama5[] __initconst = {
+       { .nr_banks = 1 },                              /* ep 0 */
+       { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */
+       { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */
+       { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 3 */
+       { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 4 */
+       { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 5 */
+       { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */
+       { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 7 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 8 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 9 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 10 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 11 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 12 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 13 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 14 */
+       { .nr_banks = 2, .can_isoc = 1 },               /* ep 15 */
+};
+
+static const struct usba_udc_config udc_at91sam9rl_cfg = {
+       .errata = &at91sam9rl_errata,
+       .config = ep_config_sam9,
+       .num_ep = ARRAY_SIZE(ep_config_sam9),
+};
+
+static const struct usba_udc_config udc_at91sam9g45_cfg = {
+       .errata = &at91sam9g45_errata,
+       .config = ep_config_sam9,
+       .num_ep = ARRAY_SIZE(ep_config_sam9),
+};
+
+static const struct usba_udc_config udc_sama5d3_cfg = {
+       .config = ep_config_sama5,
+       .num_ep = ARRAY_SIZE(ep_config_sama5),
+};
+
 static const struct of_device_id atmel_udc_dt_ids[] = {
-       { .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
-       { .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
-       { .compatible = "atmel,sama5d3-udc" },
+       { .compatible = "atmel,at91sam9rl-udc", .data = &udc_at91sam9rl_cfg },
+       { .compatible = "atmel,at91sam9g45-udc", .data = &udc_at91sam9g45_cfg },
+       { .compatible = "atmel,sama5d3-udc", .data = &udc_sama5d3_cfg },
        { /* sentinel */ }
 };
 
@@ -2055,18 +2101,19 @@ MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
 static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
                                                    struct usba_udc *udc)
 {
-       u32 val;
        struct device_node *np = pdev->dev.of_node;
        const struct of_device_id *match;
        struct device_node *pp;
        int i, ret;
        struct usba_ep *eps, *ep;
+       const struct usba_udc_config *udc_config;
 
        match = of_match_node(atmel_udc_dt_ids, np);
        if (!match)
                return ERR_PTR(-EINVAL);
 
-       udc->errata = match->data;
+       udc_config = match->data;
+       udc->errata = udc_config->errata;
        udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc");
        if (IS_ERR(udc->pmc))
                udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9rl-pmc");
@@ -2082,8 +2129,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
 
        if (fifo_mode == 0) {
                pp = NULL;
-               while ((pp = of_get_next_child(np, pp)))
-                       udc->num_ep++;
+               udc->num_ep = udc_config->num_ep;
                udc->configured_ep = 1;
        } else {
                udc->num_ep = usba_config_fifo_table(udc);
@@ -2100,52 +2146,38 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
 
        pp = NULL;
        i = 0;
-       while ((pp = of_get_next_child(np, pp)) && i < udc->num_ep) {
+       while (i < udc->num_ep) {
+               const struct usba_ep_config *ep_cfg = &udc_config->config[i];
+
                ep = &eps[i];
 
-               ret = of_property_read_u32(pp, "reg", &val);
-               if (ret) {
-                       dev_err(&pdev->dev, "of_probe: reg error(%d)\n", ret);
-                       goto err;
-               }
-               ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : val;
+               ep->index = fifo_mode ? udc->fifo_cfg[i].hw_ep_num : i;
+
+               /* Only the first EP is 64 bytes */
+               if (ep->index == 0)
+                       ep->fifo_size = 64;
+               else
+                       ep->fifo_size = 1024;
 
-               ret = of_property_read_u32(pp, "atmel,fifo-size", &val);
-               if (ret) {
-                       dev_err(&pdev->dev, "of_probe: fifo-size error(%d)\n", ret);
-                       goto err;
-               }
                if (fifo_mode) {
-                       if (val < udc->fifo_cfg[i].fifo_size) {
+                       if (ep->fifo_size < udc->fifo_cfg[i].fifo_size)
                                dev_warn(&pdev->dev,
-                                        "Using max fifo-size value from DT\n");
-                               ep->fifo_size = val;
-                       } else {
+                                        "Using default max fifo-size value\n");
+                       else
                                ep->fifo_size = udc->fifo_cfg[i].fifo_size;
-                       }
-               } else {
-                       ep->fifo_size = val;
                }
 
-               ret = of_property_read_u32(pp, "atmel,nb-banks", &val);
-               if (ret) {
-                       dev_err(&pdev->dev, "of_probe: nb-banks error(%d)\n", ret);
-                       goto err;
-               }
+               ep->nr_banks = ep_cfg->nr_banks;
                if (fifo_mode) {
-                       if (val < udc->fifo_cfg[i].nr_banks) {
+                       if (ep->nr_banks < udc->fifo_cfg[i].nr_banks)
                                dev_warn(&pdev->dev,
-                                        "Using max nb-banks value from DT\n");
-                               ep->nr_banks = val;
-                       } else {
+                                        "Using default max nb-banks value\n");
+                       else
                                ep->nr_banks = udc->fifo_cfg[i].nr_banks;
-                       }
-               } else {
-                       ep->nr_banks = val;
                }
 
-               ep->can_dma = of_property_read_bool(pp, "atmel,can-dma");
-               ep->can_isoc = of_property_read_bool(pp, "atmel,can-isoc");
+               ep->can_dma = ep_cfg->can_dma;
+               ep->can_isoc = ep_cfg->can_isoc;
 
                sprintf(ep->name, "ep%d", ep->index);
                ep->ep.name = ep->name;
index a0225e4..48e3324 100644 (file)
@@ -290,6 +290,12 @@ struct usba_ep {
 #endif
 };
 
+struct usba_ep_config {
+       u8                                      nr_banks;
+       unsigned int                            can_dma:1;
+       unsigned int                            can_isoc:1;
+};
+
 struct usba_request {
        struct usb_request                      req;
        struct list_head                        queue;
@@ -307,6 +313,12 @@ struct usba_udc_errata {
        void (*pulse_bias)(struct usba_udc *udc);
 };
 
+struct usba_udc_config {
+       const struct usba_udc_errata *errata;
+       const struct usba_ep_config *config;
+       const int num_ep;
+};
+
 struct usba_udc {
        /* Protect hw registers from concurrent modifications */
        spinlock_t lock;
index 9b11046..2e28dde 100644 (file)
@@ -1297,6 +1297,8 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
        kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
 
        usb_gadget_disconnect(udc->gadget);
+       if (udc->gadget->irq)
+               synchronize_irq(udc->gadget->irq);
        udc->driver->unbind(udc->gadget);
        usb_gadget_udc_stop(udc);
 
index 6e3e3eb..0eeaead 100644 (file)
@@ -187,31 +187,31 @@ static const struct {
                USB_EP_CAPS(USB_EP_CAPS_TYPE_BULK, USB_EP_CAPS_DIR_IN)),
 
        /* and now some generic EPs so we have enough in multi config */
-       EP_INFO("ep3out",
+       EP_INFO("ep-aout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
-       EP_INFO("ep4in",
+       EP_INFO("ep-bin",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
-       EP_INFO("ep5out",
+       EP_INFO("ep-cout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
-       EP_INFO("ep6out",
+       EP_INFO("ep-dout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
-       EP_INFO("ep7in",
+       EP_INFO("ep-ein",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
-       EP_INFO("ep8out",
+       EP_INFO("ep-fout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
-       EP_INFO("ep9in",
+       EP_INFO("ep-gin",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
-       EP_INFO("ep10out",
+       EP_INFO("ep-hout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
-       EP_INFO("ep11out",
+       EP_INFO("ep-iout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
-       EP_INFO("ep12in",
+       EP_INFO("ep-jin",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
-       EP_INFO("ep13out",
+       EP_INFO("ep-kout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
-       EP_INFO("ep14in",
+       EP_INFO("ep-lin",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_IN)),
-       EP_INFO("ep15out",
+       EP_INFO("ep-mout",
                USB_EP_CAPS(TYPE_BULK_OR_INT, USB_EP_CAPS_DIR_OUT)),
 
 #undef EP_INFO
@@ -427,6 +427,7 @@ static void set_link_state_by_speed(struct dummy_hcd *dum_hcd)
 
 /* caller must hold lock */
 static void set_link_state(struct dummy_hcd *dum_hcd)
+       __must_hold(&dum->lock)
 {
        struct dummy *dum = dum_hcd->dum;
        unsigned int power_bit;
index febabde..b2638e8 100644 (file)
@@ -2440,8 +2440,8 @@ static int fsl_udc_probe(struct platform_device *pdev)
        udc_controller->max_ep = (dccparams & DCCPARAMS_DEN_MASK) * 2;
 
        udc_controller->irq = platform_get_irq(pdev, 0);
-       if (!udc_controller->irq) {
-               ret = -ENODEV;
+       if (udc_controller->irq <= 0) {
+               ret = udc_controller->irq ? : -ENODEV;
                goto err_iounmap;
        }
 
index aaf975c..7164ad9 100644 (file)
@@ -48,7 +48,6 @@
 #define        DRIVER_DESC     "Aeroflex Gaisler GRUSBDC USB Peripheral Controller"
 
 static const char driver_name[] = DRIVER_NAME;
-static const char driver_desc[] = DRIVER_DESC;
 
 #define gr_read32(x) (ioread32be((x)))
 #define gr_write32(x, v) (iowrite32be((v), (x)))
index cb997b8..465d0b7 100644 (file)
@@ -1614,17 +1614,17 @@ static int lpc32xx_ep_enable(struct usb_ep *_ep,
                             const struct usb_endpoint_descriptor *desc)
 {
        struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
-       struct lpc32xx_udc *udc = ep->udc;
+       struct lpc32xx_udc *udc;
        u16 maxpacket;
        u32 tmp;
        unsigned long flags;
 
        /* Verify EP data */
        if ((!_ep) || (!ep) || (!desc) ||
-           (desc->bDescriptorType != USB_DT_ENDPOINT)) {
-               dev_dbg(udc->dev, "bad ep or descriptor\n");
+           (desc->bDescriptorType != USB_DT_ENDPOINT))
                return -EINVAL;
-       }
+
+       udc = ep->udc;
        maxpacket = usb_endpoint_maxp(desc);
        if ((maxpacket == 0) || (maxpacket > ep->maxpacket)) {
                dev_dbg(udc->dev, "bad ep descriptor's packet size\n");
@@ -1872,7 +1872,7 @@ static int lpc32xx_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
 static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
 {
        struct lpc32xx_ep *ep = container_of(_ep, struct lpc32xx_ep, ep);
-       struct lpc32xx_udc *udc = ep->udc;
+       struct lpc32xx_udc *udc;
        unsigned long flags;
 
        if ((!ep) || (ep->hwep_num <= 1))
@@ -1882,6 +1882,7 @@ static int lpc32xx_ep_set_halt(struct usb_ep *_ep, int value)
        if (ep->is_in)
                return -EAGAIN;
 
+       udc = ep->udc;
        spin_lock_irqsave(&udc->lock, flags);
 
        if (value == 1) {
index 75d16a8..931e636 100644 (file)
@@ -1667,7 +1667,7 @@ static int m66592_probe(struct platform_device *pdev)
 
 err_add_udc:
        m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
-
+       m66592->ep0_req = NULL;
 clean_up3:
        if (m66592->pdata->on_chip) {
                clk_disable(m66592->clk);
index 8fbc083..23f3394 100644 (file)
@@ -901,7 +901,7 @@ loop:
        }
 
        set_current_state(TASK_RUNNING);
-       dev_info(udc->dev, "SPI thread exiting");
+       dev_info(udc->dev, "SPI thread exiting\n");
        return 0;
 }
 
index 35e02a8..5bb0568 100644 (file)
@@ -1548,7 +1548,7 @@ static void mv_u3d_handle_setup_packet(struct mv_u3d *u3d, u8 ep_num,
                delegate = true;
 
        /* delegate USB standard requests to the gadget driver */
-       if (delegate == true) {
+       if (delegate) {
                /* USB requests handled by gadget */
                if (setup->wLength) {
                        /* DATA phase from gadget, STATUS phase from u3d */
index 5af0fe9..928057b 100644 (file)
@@ -54,7 +54,7 @@ static const char * const ep_name[] = {
  *
  * If use_dma is disabled, pio will be used instead.
  */
-static bool use_dma = 0;
+static bool use_dma = false;
 module_param(use_dma, bool, 0644);
 
 /*
index bf87c6c..4139da8 100644 (file)
@@ -2576,7 +2576,7 @@ omap_ep_setup(char *name, u8 addr, u8 type,
        case USB_ENDPOINT_XFER_INT:
                ep->ep.caps.type_int = true;
                break;
-       };
+       }
 
        if (addr & USB_DIR_IN)
                ep->ep.caps.dir_in = true;
index 0507a2c..80002d9 100644 (file)
@@ -251,10 +251,6 @@ static void s3c2410_udc_done(struct s3c2410_ep *ep,
 static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
                struct s3c2410_ep *ep, int status)
 {
-       /* Sanity check */
-       if (&ep->queue == NULL)
-               return;
-
        while (!list_empty(&ep->queue)) {
                struct s3c2410_request *req;
                req = list_entry(ep->queue.next, struct s3c2410_request,
index dfabc54..bbe1a04 100644 (file)
 #define  SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK GENMASK(7, 0)
 #define  SSPX_CORE_CNT32_POLL_TBURST_MAX(x) ((x) & \
                                        SSPX_CORE_CNT32_POLL_TBURST_MAX_MASK)
+#define SSPX_CORE_CNT56 0x6fc
+#define  SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK GENMASK(19, 0)
+#define  SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(x) ((x) & \
+                               SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK)
+#define SSPX_CORE_CNT57 0x700
+#define  SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK GENMASK(19, 0)
+#define  SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(x) ((x) & \
+                               SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK)
+#define SSPX_CORE_CNT65 0x720
+#define  SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK GENMASK(19, 0)
+#define  SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(x) ((x) & \
+                               SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK)
+#define SSPX_CORE_CNT66 0x724
+#define  SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK GENMASK(19, 0)
+#define  SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(x) ((x) & \
+                               SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK)
+#define SSPX_CORE_CNT67 0x728
+#define  SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK GENMASK(19, 0)
+#define  SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(x) ((x) & \
+                               SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK)
+#define SSPX_CORE_CNT72 0x73c
+#define  SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK GENMASK(19, 0)
+#define  SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(x) ((x) & \
+                               SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK)
 #define SSPX_CORE_PADCTL4 0x750
 #define  SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK GENMASK(19, 0)
 #define  SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3(x) ((x) & \
@@ -492,6 +516,7 @@ struct tegra_xudc {
        bool powergated;
 
        struct usb_phy **usbphy;
+       struct usb_phy *curr_usbphy;
        struct notifier_block vbus_nb;
 
        struct completion disconnect_complete;
@@ -530,6 +555,7 @@ struct tegra_xudc_soc {
        bool invalid_seq_num;
        bool pls_quirk;
        bool port_reset_quirk;
+       bool port_speed_quirk;
        bool has_ipfs;
 };
 
@@ -599,6 +625,78 @@ static inline void dump_trb(struct tegra_xudc *xudc, const char *type,
                trb->control);
 }
 
+static void tegra_xudc_limit_port_speed(struct tegra_xudc *xudc)
+{
+       u32 val;
+
+       /* limit port speed to gen 1 */
+       val = xudc_readl(xudc, SSPX_CORE_CNT56);
+       val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
+       val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x260);
+       xudc_writel(xudc, val, SSPX_CORE_CNT56);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT57);
+       val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
+       val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x6D6);
+       xudc_writel(xudc, val, SSPX_CORE_CNT57);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT65);
+       val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
+       val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0x4B0);
+       xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT66);
+       val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
+       val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x4B0);
+       xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT67);
+       val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
+       val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x4B0);
+       xudc_writel(xudc, val, SSPX_CORE_CNT67);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT72);
+       val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
+       val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x10);
+       xudc_writel(xudc, val, SSPX_CORE_CNT72);
+}
+
+static void tegra_xudc_restore_port_speed(struct tegra_xudc *xudc)
+{
+       u32 val;
+
+       /* restore port speed to gen2 */
+       val = xudc_readl(xudc, SSPX_CORE_CNT56);
+       val &= ~(SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX_MASK);
+       val |= SSPX_CORE_CNT56_SCD_BIT0_TRPT_MAX(0x438);
+       xudc_writel(xudc, val, SSPX_CORE_CNT56);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT57);
+       val &= ~(SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX_MASK);
+       val |= SSPX_CORE_CNT57_SCD_BIT1_TRPT_MAX(0x528);
+       xudc_writel(xudc, val, SSPX_CORE_CNT57);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT65);
+       val &= ~(SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID_MASK);
+       val |= SSPX_CORE_CNT65_TX_SCD_END_TRPT_MID(0xE10);
+       xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT66);
+       val &= ~(SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID_MASK);
+       val |= SSPX_CORE_CNT66_TX_SCD_BIT0_TRPT_MID(0x348);
+       xudc_writel(xudc, val, SSPX_CORE_CNT66);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT67);
+       val &= ~(SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID_MASK);
+       val |= SSPX_CORE_CNT67_TX_SCD_BIT1_TRPT_MID(0x5a0);
+       xudc_writel(xudc, val, SSPX_CORE_CNT67);
+
+       val = xudc_readl(xudc, SSPX_CORE_CNT72);
+       val &= ~(SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT_MASK);
+       val |= SSPX_CORE_CNT72_SCD_LFPS_TIMEOUT(0x1c21);
+       xudc_writel(xudc, val, SSPX_CORE_CNT72);
+}
+
 static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
 {
        int err;
@@ -631,6 +729,9 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
 
        reinit_completion(&xudc->disconnect_complete);
 
+       if (xudc->soc->port_speed_quirk)
+               tegra_xudc_restore_port_speed(xudc);
+
        phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
 
        pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
@@ -719,6 +820,7 @@ static int tegra_xudc_vbus_notify(struct notifier_block *nb,
        if (!xudc->suspended && phy_index != -1) {
                xudc->curr_utmi_phy = xudc->utmi_phy[phy_index];
                xudc->curr_usb3_phy = xudc->usb3_phy[phy_index];
+               xudc->curr_usbphy = usbphy;
                schedule_work(&xudc->usb_role_sw_work);
        }
 
@@ -2042,6 +2144,20 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
        return 0;
 }
 
+static int tegra_xudc_gadget_vbus_draw(struct usb_gadget *gadget,
+                                               unsigned int m_a)
+{
+       int ret = 0;
+       struct tegra_xudc *xudc = to_xudc(gadget);
+
+       dev_dbg(xudc->dev, "%s: %u mA\n", __func__, m_a);
+
+       if (xudc->curr_usbphy->chg_type == SDP_TYPE)
+               ret = usb_phy_set_power(xudc->curr_usbphy, m_a);
+
+       return ret;
+}
+
 static int tegra_xudc_set_selfpowered(struct usb_gadget *gadget, int is_on)
 {
        struct tegra_xudc *xudc = to_xudc(gadget);
@@ -2058,6 +2174,7 @@ static struct usb_gadget_ops tegra_xudc_gadget_ops = {
        .pullup = tegra_xudc_gadget_pullup,
        .udc_start = tegra_xudc_gadget_start,
        .udc_stop = tegra_xudc_gadget_stop,
+       .vbus_draw = tegra_xudc_gadget_vbus_draw,
        .set_selfpowered = tegra_xudc_set_selfpowered,
 };
 
@@ -3274,6 +3391,9 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
                xudc_writel(xudc, val, BLCG);
        }
 
+       if (xudc->soc->port_speed_quirk)
+               tegra_xudc_limit_port_speed(xudc);
+
        /* Set a reasonable U3 exit timer value. */
        val = xudc_readl(xudc, SSPX_CORE_PADCTL4);
        val &= ~(SSPX_CORE_PADCTL4_RXDAT_VLD_TIMEOUT_U3_MASK);
@@ -3506,6 +3626,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
        .invalid_seq_num = true,
        .pls_quirk = true,
        .port_reset_quirk = true,
+       .port_speed_quirk = false,
        .has_ipfs = true,
 };
 
@@ -3519,6 +3640,21 @@ static struct tegra_xudc_soc tegra186_xudc_soc_data = {
        .invalid_seq_num = false,
        .pls_quirk = false,
        .port_reset_quirk = false,
+       .port_speed_quirk = false,
+       .has_ipfs = false,
+};
+
+static struct tegra_xudc_soc tegra194_xudc_soc_data = {
+       .clock_names = tegra186_xudc_clock_names,
+       .num_clks = ARRAY_SIZE(tegra186_xudc_clock_names),
+       .num_phys = 4,
+       .u1_enable = true,
+       .u2_enable = true,
+       .lpm_enable = true,
+       .invalid_seq_num = false,
+       .pls_quirk = false,
+       .port_reset_quirk = false,
+       .port_speed_quirk = true,
        .has_ipfs = false,
 };
 
@@ -3531,6 +3667,10 @@ static const struct of_device_id tegra_xudc_of_match[] = {
                .compatible = "nvidia,tegra186-xudc",
                .data = &tegra186_xudc_soc_data
        },
+       {
+               .compatible = "nvidia,tegra194-xudc",
+               .data = &tegra194_xudc_soc_data
+       },
        { }
 };
 MODULE_DEVICE_TABLE(of, tegra_xudc_of_match);
index b1cfc82..709553b 100644 (file)
@@ -1732,6 +1732,7 @@ static void xudc_set_clear_feature(struct xusb_udc *udc)
  * Process setup packet and delegate to gadget layer.
  */
 static void xudc_handle_setup(struct xusb_udc *udc)
+       __must_hold(&udc->lock)
 {
        struct xusb_ep *ep0 = &udc->ep[0];
        struct usb_ctrlrequest setup;
index 7c24d1c..58a4d33 100644 (file)
@@ -65,3 +65,27 @@ usb_gadget_get_string (const struct usb_gadget_strings *table, int id, u8 *buf)
        return buf [0];
 }
 EXPORT_SYMBOL_GPL(usb_gadget_get_string);
+
+/**
+ * usb_validate_langid - validate usb language identifiers
+ * @lang: usb language identifier
+ *
+ * Returns true for valid language identifier, otherwise false.
+ */
+bool usb_validate_langid(u16 langid)
+{
+       u16 primary_lang = langid & 0x3ff;      /* bit [9:0] */
+       u16 sub_lang = langid >> 10;            /* bit [15:10] */
+
+       switch (primary_lang) {
+       case 0:
+       case 0x62 ... 0xfe:
+       case 0x100 ... 0x3ff:
+               return false;
+       }
+       if (!sub_lang)
+               return false;
+
+       return true;
+}
+EXPORT_SYMBOL_GPL(usb_validate_langid);
index 55bdfdf..62c3480 100644 (file)
@@ -40,8 +40,17 @@ config USB_XHCI_DBGCAP
 config USB_XHCI_PCI
        tristate
        depends on USB_PCI
+       depends on USB_XHCI_PCI_RENESAS || !USB_XHCI_PCI_RENESAS
        default y
 
+config USB_XHCI_PCI_RENESAS
+       tristate "Support for additional Renesas xHCI controller with firwmare"
+       ---help---
+         Say 'Y' to enable the support for the Renesas xHCI controller with
+         firwmare. Make sure you have the firwmare for the device and
+         installed on your system for this device to work.
+         If unsure, say 'N'.
+
 config USB_XHCI_PLATFORM
        tristate "Generic xHCI driver for a platform device"
        select USB_XHCI_RCAR if ARCH_RENESAS
@@ -97,6 +106,26 @@ config USB_XHCI_TEGRA
 
 endif # USB_XHCI_HCD
 
+config USB_EHCI_BRCMSTB
+       tristate
+
+config USB_BRCMSTB
+       tristate "Broadcom STB USB support"
+       depends on (ARCH_BRCMSTB && PHY_BRCM_USB) || COMPILE_TEST
+       select USB_OHCI_HCD_PLATFORM if USB_OHCI_HCD
+       select USB_EHCI_BRCMSTB if USB_EHCI_HCD
+       select USB_XHCI_PLATFORM if USB_XHCI_HCD
+       help
+         Enables support for XHCI, EHCI and OHCI host controllers
+         found in Broadcom STB SoC's.
+
+         To compile these drivers as modules, choose M here: the
+         modules will be called ohci-platform.ko, ehci-brcm.ko and
+         xhci-plat-hcd.ko
+
+         Disabling this will keep the controllers and corresponding
+         PHYs powered down.
+
 config USB_EHCI_HCD
        tristate "EHCI HCD (USB 2.0) support"
        depends on HAS_DMA && HAS_IOMEM
index b191361..bc73133 100644 (file)
@@ -49,6 +49,7 @@ obj-$(CONFIG_USB_EHCI_HCD_STI)        += ehci-st.o
 obj-$(CONFIG_USB_EHCI_EXYNOS)  += ehci-exynos.o
 obj-$(CONFIG_USB_EHCI_HCD_AT91) += ehci-atmel.o
 obj-$(CONFIG_USB_EHCI_TEGRA)   += ehci-tegra.o
+obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o
 
 obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o
 obj-$(CONFIG_USB_ISP116X_HCD)  += isp116x-hcd.o
@@ -71,6 +72,7 @@ obj-$(CONFIG_USB_UHCI_HCD)    += uhci-hcd.o
 obj-$(CONFIG_USB_FHCI_HCD)     += fhci.o
 obj-$(CONFIG_USB_XHCI_HCD)     += xhci-hcd.o
 obj-$(CONFIG_USB_XHCI_PCI)     += xhci-pci.o
+obj-$(CONFIG_USB_XHCI_PCI_RENESAS)     += xhci-pci-renesas.o
 obj-$(CONFIG_USB_XHCI_PLATFORM) += xhci-plat-hcd.o
 obj-$(CONFIG_USB_XHCI_HISTB)   += xhci-histb.o
 obj-$(CONFIG_USB_XHCI_MTK)     += xhci-mtk.o
diff --git a/drivers/usb/host/ehci-brcm.c b/drivers/usb/host/ehci-brcm.c
new file mode 100644 (file)
index 0000000..3e0ebe8
--- /dev/null
@@ -0,0 +1,280 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2020, Broadcom */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/iopoll.h>
+
+#include "ehci.h"
+
+#define hcd_to_ehci_priv(h) ((struct brcm_priv *)hcd_to_ehci(h)->priv)
+
+struct brcm_priv {
+       struct clk *clk;
+};
+
+/*
+ * ehci_brcm_wait_for_sof
+ * Wait for start of next microframe, then wait extra delay microseconds
+ */
+static inline void ehci_brcm_wait_for_sof(struct ehci_hcd *ehci, u32 delay)
+{
+       u32 frame_idx = ehci_readl(ehci, &ehci->regs->frame_index);
+       u32 val;
+       int res;
+
+       /* Wait for next microframe (every 125 usecs) */
+       res = readl_relaxed_poll_timeout(&ehci->regs->frame_index, val,
+                                        val != frame_idx, 1, 130);
+       if (res)
+               ehci_err(ehci, "Error waiting for SOF\n");
+       udelay(delay);
+}
+
+/*
+ * ehci_brcm_hub_control
+ * The EHCI controller has a bug where it can violate the SOF
+ * interval between the first two SOF's transmitted after resume
+ * if the resume occurs near the end of the microframe. This causees
+ * the controller to detect babble on the suspended port and
+ * will eventually cause the controller to reset the port.
+ * The fix is to Intercept the echi-hcd request to complete RESUME and
+ * align it to the start of the next microframe.
+ * See SWLINUX-1909 for more details
+ */
+static int ehci_brcm_hub_control(
+       struct usb_hcd  *hcd,
+       u16             typeReq,
+       u16             wValue,
+       u16             wIndex,
+       char            *buf,
+       u16             wLength)
+{
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       int             ports = HCS_N_PORTS(ehci->hcs_params);
+       u32 __iomem     *status_reg;
+       unsigned long flags;
+       int retval, irq_disabled = 0;
+
+       status_reg = &ehci->regs->port_status[(wIndex & 0xff) - 1];
+
+       /*
+        * RESUME is cleared when GetPortStatus() is called 20ms after start
+        * of RESUME
+        */
+       if ((typeReq == GetPortStatus) &&
+           (wIndex && wIndex <= ports) &&
+           ehci->reset_done[wIndex-1] &&
+           time_after_eq(jiffies, ehci->reset_done[wIndex-1]) &&
+           (ehci_readl(ehci, status_reg) & PORT_RESUME)) {
+
+               /*
+                * to make sure we are not interrupted until RESUME bit
+                * is cleared, disable interrupts on current CPU
+                */
+               ehci_dbg(ehci, "SOF alignment workaround\n");
+               irq_disabled = 1;
+               local_irq_save(flags);
+               ehci_brcm_wait_for_sof(ehci, 5);
+       }
+       retval = ehci_hub_control(hcd, typeReq, wValue, wIndex, buf, wLength);
+       if (irq_disabled)
+               local_irq_restore(flags);
+       return retval;
+}
+
+static int ehci_brcm_reset(struct usb_hcd *hcd)
+{
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       int len;
+
+       ehci->big_endian_mmio = 1;
+
+       ehci->caps = (void __iomem *)hcd->regs;
+       len = HC_LENGTH(ehci, ehci_readl(ehci, &ehci->caps->hc_capbase));
+       ehci->regs = (void __iomem *)(hcd->regs + len);
+
+       /* This fixes the lockup during reboot due to prior interrupts */
+       ehci_writel(ehci, CMD_RESET, &ehci->regs->command);
+       mdelay(10);
+
+       /*
+        * SWLINUX-1705: Avoid OUT packet underflows during high memory
+        *   bus usage
+        * port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00 @ 0x90
+        */
+       ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]);
+       ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]);
+
+       return ehci_setup(hcd);
+}
+
+static struct hc_driver __read_mostly ehci_brcm_hc_driver;
+
+static const struct ehci_driver_overrides brcm_overrides __initconst = {
+       .reset = ehci_brcm_reset,
+       .extra_priv_size = sizeof(struct brcm_priv),
+};
+
+static int ehci_brcm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res_mem;
+       struct brcm_priv *priv;
+       struct usb_hcd *hcd;
+       int irq;
+       int err;
+
+       err = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+       if (err)
+               return err;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq <= 0)
+               return irq ? irq : -EINVAL;
+
+       /* Hook the hub control routine to work around a bug */
+       ehci_brcm_hc_driver.hub_control = ehci_brcm_hub_control;
+
+       /* initialize hcd */
+       hcd = usb_create_hcd(&ehci_brcm_hc_driver, dev, dev_name(dev));
+       if (!hcd)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, hcd);
+       priv = hcd_to_ehci_priv(hcd);
+
+       priv->clk = devm_clk_get_optional(dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               err = PTR_ERR(priv->clk);
+               goto err_hcd;
+       }
+
+       err = clk_prepare_enable(priv->clk);
+       if (err)
+               goto err_hcd;
+
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res_mem);
+       if (IS_ERR(hcd->regs)) {
+               err = PTR_ERR(hcd->regs);
+               goto err_clk;
+       }
+       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_clk;
+
+       device_wakeup_enable(hcd->self.controller);
+       device_enable_async_suspend(hcd->self.controller);
+
+       return 0;
+
+err_clk:
+       clk_disable_unprepare(priv->clk);
+err_hcd:
+       usb_put_hcd(hcd);
+
+       return err;
+}
+
+static int ehci_brcm_remove(struct platform_device *dev)
+{
+       struct usb_hcd *hcd = platform_get_drvdata(dev);
+       struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
+
+       usb_remove_hcd(hcd);
+       clk_disable_unprepare(priv->clk);
+       usb_put_hcd(hcd);
+       return 0;
+}
+
+static int __maybe_unused ehci_brcm_suspend(struct device *dev)
+{
+       int ret;
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
+       bool do_wakeup = device_may_wakeup(dev);
+
+       ret = ehci_suspend(hcd, do_wakeup);
+       if (ret)
+               return ret;
+       clk_disable_unprepare(priv->clk);
+       return 0;
+}
+
+static int __maybe_unused ehci_brcm_resume(struct device *dev)
+{
+       struct usb_hcd *hcd = dev_get_drvdata(dev);
+       struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+       struct brcm_priv *priv = hcd_to_ehci_priv(hcd);
+       int err;
+
+       err = clk_prepare_enable(priv->clk);
+       if (err)
+               return err;
+       /*
+        * SWLINUX-1705: Avoid OUT packet underflows during high memory
+        *   bus usage
+        * port_status[0x0f] = Broadcom-proprietary USB_EHCI_INSNREG00
+        * @ 0x90
+        */
+       ehci_writel(ehci, 0x00800040, &ehci->regs->port_status[0x10]);
+       ehci_writel(ehci, 0x00000001, &ehci->regs->port_status[0x12]);
+
+       ehci_resume(hcd, false);
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(ehci_brcm_pm_ops, ehci_brcm_suspend,
+               ehci_brcm_resume);
+
+static const struct of_device_id brcm_ehci_of_match[] = {
+       { .compatible = "brcm,ehci-brcm-v2", },
+       { .compatible = "brcm,bcm7445-ehci", },
+       {}
+};
+
+static struct platform_driver ehci_brcm_driver = {
+       .probe          = ehci_brcm_probe,
+       .remove         = ehci_brcm_remove,
+       .shutdown       = usb_hcd_platform_shutdown,
+       .driver         = {
+               .name   = "ehci-brcm",
+               .pm     = &ehci_brcm_pm_ops,
+               .of_match_table = brcm_ehci_of_match,
+       }
+};
+
+static int __init ehci_brcm_init(void)
+{
+       if (usb_disabled())
+               return -ENODEV;
+
+       ehci_init_driver(&ehci_brcm_hc_driver, &brcm_overrides);
+       return platform_driver_register(&ehci_brcm_driver);
+}
+module_init(ehci_brcm_init);
+
+static void __exit ehci_brcm_exit(void)
+{
+       platform_driver_unregister(&ehci_brcm_driver);
+}
+module_exit(ehci_brcm_exit);
+
+MODULE_ALIAS("platform:ehci-brcm");
+MODULE_DESCRIPTION("EHCI Broadcom STB driver");
+MODULE_AUTHOR("Al Cooper");
+MODULE_LICENSE("GPL");
index 9d18c6e..c95341d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /* Copyright (C) 2005-2010,2012 Freescale Semiconductor, Inc.
  * Copyright (c) 2005 MontaVista Software
  */
index 1300c45..cffdc8d 100644 (file)
@@ -108,7 +108,7 @@ static int mv_ehci_probe(struct platform_device *pdev)
        struct ehci_hcd *ehci;
        struct ehci_hcd_mv *ehci_mv;
        struct resource *r;
-       int retval = -ENODEV;
+       int retval;
        u32 offset;
        u32 status;
 
@@ -143,8 +143,6 @@ 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);
        if (IS_ERR(ehci_mv->base)) {
@@ -168,12 +166,10 @@ static int mv_ehci_probe(struct platform_device *pdev)
        hcd->rsrc_len = resource_size(r);
        hcd->regs = ehci_mv->op_regs;
 
-       hcd->irq = platform_get_irq(pdev, 0);
-       if (!hcd->irq) {
-               dev_err(&pdev->dev, "Cannot get irq.");
-               retval = -ENODEV;
+       retval = platform_get_irq(pdev, 0);
+       if (retval < 0)
                goto err_disable_clk;
-       }
+       hcd->irq = retval;
 
        ehci = hcd_to_ehci(hcd);
        ehci->caps = (struct ehci_caps __iomem *) ehci_mv->cap_regs;
index c9f91e6..dc26763 100644 (file)
@@ -36,12 +36,12 @@ static const struct ehci_driver_overrides ehci_mxc_overrides __initconst = {
 
 static int ehci_mxc_drv_probe(struct platform_device *pdev)
 {
-       struct mxc_usbh_platform_data *pdata = dev_get_platdata(&pdev->dev);
+       struct device *dev = &pdev->dev;
+       struct mxc_usbh_platform_data *pdata = dev_get_platdata(dev);
        struct usb_hcd *hcd;
        struct resource *res;
        int irq, ret;
        struct ehci_mxc_priv *priv;
-       struct device *dev = &pdev->dev;
        struct ehci_hcd *ehci;
 
        if (!pdata) {
@@ -50,13 +50,15 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        }
 
        irq = platform_get_irq(pdev, 0);
+       if (irq < 0)
+               return irq;
 
        hcd = usb_create_hcd(&ehci_mxc_hc_driver, dev, dev_name(dev));
        if (!hcd)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_ioremap_resource(dev, res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto err_alloc;
@@ -69,14 +71,14 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        priv = (struct ehci_mxc_priv *) ehci->priv;
 
        /* enable clocks */
-       priv->usbclk = devm_clk_get(&pdev->dev, "ipg");
+       priv->usbclk = devm_clk_get(dev, "ipg");
        if (IS_ERR(priv->usbclk)) {
                ret = PTR_ERR(priv->usbclk);
                goto err_alloc;
        }
        clk_prepare_enable(priv->usbclk);
 
-       priv->ahbclk = devm_clk_get(&pdev->dev, "ahb");
+       priv->ahbclk = devm_clk_get(dev, "ahb");
        if (IS_ERR(priv->ahbclk)) {
                ret = PTR_ERR(priv->ahbclk);
                goto err_clk_ahb;
@@ -84,13 +86,12 @@ static int ehci_mxc_drv_probe(struct platform_device *pdev)
        clk_prepare_enable(priv->ahbclk);
 
        /* "dr" device has its own clock on i.MX51 */
-       priv->phyclk = devm_clk_get(&pdev->dev, "phy");
+       priv->phyclk = devm_clk_get(dev, "phy");
        if (IS_ERR(priv->phyclk))
                priv->phyclk = NULL;
        if (priv->phyclk)
                clk_prepare_enable(priv->phyclk);
 
-
        /* call platform specific init function */
        if (pdata->init) {
                ret = pdata->init(pdev);
index 1a48ab1..3c3820a 100644 (file)
@@ -360,23 +360,21 @@ static int ehci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
        if (is_bypassed_id(pdev))
                return -ENODEV;
-       return usb_hcd_pci_probe(pdev, id);
+       return usb_hcd_pci_probe(pdev, id, &ehci_pci_hc_driver);
 }
 
 static void ehci_pci_remove(struct pci_dev *pdev)
 {
        pci_clear_mwi(pdev);
-       usb_hcd_pci_remove(pdev);       
+       usb_hcd_pci_remove(pdev);
 }
 
 /* PCI driver selection metadata; PCI hotplugging uses this */
 static const struct pci_device_id pci_ids [] = { {
        /* handle any USB 2.0 EHCI controller */
        PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_EHCI, ~0),
-       .driver_data =  (unsigned long) &ehci_pci_hc_driver,
        }, {
        PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_HOST),
-       .driver_data = (unsigned long) &ehci_pci_hc_driver,
        },
        { /* end: all zeroes */ }
 };
index e4fc3f6..e9a4900 100644 (file)
@@ -455,6 +455,10 @@ static int ehci_platform_resume(struct device *dev)
 
        ehci_resume(hcd, priv->reset_on_resume);
 
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
        if (priv->quirk_poll)
                quirk_poll_init(priv);
 
index 10d51da..e077b2c 100644 (file)
@@ -480,7 +480,6 @@ static int tegra_ehci_probe(struct platform_device *pdev)
 
        irq = platform_get_irq(pdev, 0);
        if (!irq) {
-               dev_err(&pdev->dev, "Failed to get IRQ\n");
                err = -ENODEV;
                goto cleanup_phy;
        }
index 229b3de..eabf22a 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Copyright (c) 2001-2002 by David Brownell
  */
index 2ce5031..81fbc01 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Freescale QUICC Engine USB Host Controller Driver
  *
index 7b9cf0a..96d1675 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Macros and prototypes for i.MX21
  *
index 22117a6..585222a 100644 (file)
@@ -277,21 +277,24 @@ static const struct ohci_driver_overrides pci_overrides __initconst = {
 static const struct pci_device_id pci_ids[] = { {
        /* handle any USB OHCI controller */
        PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_OHCI, ~0),
-       .driver_data =  (unsigned long) &ohci_pci_hc_driver,
        }, {
        /* The device in the ConneXT I/O hub has no class reg */
        PCI_VDEVICE(STMICRO, PCI_DEVICE_ID_STMICRO_USB_OHCI),
-       .driver_data =  (unsigned long) &ohci_pci_hc_driver,
        }, { /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE (pci, pci_ids);
 
+static int ohci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       return usb_hcd_pci_probe(dev, id, &ohci_pci_hc_driver);
+}
+
 /* pci driver glue; this is a "new style" PCI driver module */
 static struct pci_driver ohci_pci_driver = {
        .name =         hcd_name,
        .id_table =     pci_ids,
 
-       .probe =        usb_hcd_pci_probe,
+       .probe =        ohci_pci_probe,
        .remove =       usb_hcd_pci_remove,
        .shutdown =     usb_hcd_pci_shutdown,
 
index 7addfc2..4a8456f 100644 (file)
@@ -299,6 +299,11 @@ static int ohci_platform_resume(struct device *dev)
        }
 
        ohci_resume(hcd, false);
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
        return 0;
 }
 #endif /* CONFIG_PM_SLEEP */
index c158cda..cff9652 100644 (file)
@@ -157,9 +157,10 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev)
         * the call to usb_hcd_setup_local_mem() below does just that.
         */
 
-       if (usb_hcd_setup_local_mem(hcd, mem->start,
-                                   mem->start - mem->parent->start,
-                                   resource_size(mem)) < 0)
+       retval = usb_hcd_setup_local_mem(hcd, mem->start,
+                                        mem->start - mem->parent->start,
+                                        resource_size(mem));
+       if (retval < 0)
                goto err5;
        retval = usb_add_hcd(hcd, irq, IRQF_SHARED);
        if (retval)
index 27c26ca..b85a395 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
 /*
  * OHCI HCD (Host Controller Driver) for USB.
  *
index 0dc3466..0b949ac 100644 (file)
@@ -208,7 +208,7 @@ static void usb_amd_find_chipset_info(void)
 {
        unsigned long flags;
        struct amd_chipset_info info;
-       info.need_pll_quirk = 0;
+       info.need_pll_quirk = false;
 
        spin_lock_irqsave(&amd_lock, flags);
 
@@ -232,10 +232,10 @@ static void usb_amd_find_chipset_info(void)
        case AMD_CHIPSET_SB800:
        case AMD_CHIPSET_HUDSON2:
        case AMD_CHIPSET_BOLTON:
-               info.need_pll_quirk = 1;
+               info.need_pll_quirk = true;
                break;
        default:
-               info.need_pll_quirk = 0;
+               info.need_pll_quirk = false;
                break;
        }
 
@@ -532,7 +532,7 @@ void usb_amd_dev_put(void)
        amd_chipset.nb_type = 0;
        memset(&amd_chipset.sb_type, 0, sizeof(amd_chipset.sb_type));
        amd_chipset.isoc_reqs = 0;
-       amd_chipset.need_pll_quirk = 0;
+       amd_chipset.need_pll_quirk = false;
 
        spin_unlock_irqrestore(&amd_lock, flags);
 
index 51973a9..ab08147 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * R8A66597 HCD (Host Controller Driver)
  *
index e9209e3..995bc52 100644 (file)
@@ -81,7 +81,6 @@ static DECLARE_WAIT_QUEUE_HEAD(u132_hcd_wait);
 static struct mutex u132_module_lock;
 static int u132_exiting;
 static int u132_instances;
-static struct list_head u132_static_list;
 /*
 * end of the global variables protected by u132_module_lock
 */
@@ -177,7 +176,6 @@ struct u132_ring {
 };
 struct u132 {
        struct kref kref;
-       struct list_head u132_list;
        struct mutex sw_lock;
        struct mutex scheduler_lock;
        struct u132_platform_data *board;
@@ -254,7 +252,6 @@ static void u132_hcd_delete(struct kref *kref)
        struct usb_hcd *hcd = u132_to_hcd(u132);
        u132->going += 1;
        mutex_lock(&u132_module_lock);
-       list_del_init(&u132->u132_list);
        u132_instances -= 1;
        mutex_unlock(&u132_module_lock);
        dev_warn(&u132->platform_dev->dev, "FREEING the hcd=%p and thus the u13"
@@ -3089,7 +3086,6 @@ static int u132_probe(struct platform_device *pdev)
                retval = 0;
                hcd->rsrc_start = 0;
                mutex_lock(&u132_module_lock);
-               list_add_tail(&u132->u132_list, &u132_static_list);
                u132->sequence_num = ++u132_instances;
                mutex_unlock(&u132_module_lock);
                u132_u132_init_kref(u132);
@@ -3192,7 +3188,6 @@ static struct platform_driver u132_platform_driver = {
 static int __init u132_hcd_init(void)
 {
        int retval;
-       INIT_LIST_HEAD(&u132_static_list);
        u132_instances = 0;
        u132_exiting = 0;
        mutex_init(&u132_module_lock);
@@ -3213,14 +3208,9 @@ static int __init u132_hcd_init(void)
 module_init(u132_hcd_init);
 static void __exit u132_hcd_exit(void)
 {
-       struct u132 *u132;
-       struct u132 *temp;
        mutex_lock(&u132_module_lock);
        u132_exiting += 1;
        mutex_unlock(&u132_module_lock);
-       list_for_each_entry_safe(u132, temp, &u132_static_list, u132_list) {
-               platform_device_unregister(u132->platform_dev);
-       }
        platform_driver_unregister(&u132_platform_driver);
        printk(KERN_INFO "u132-hcd driver deregistered\n");
        wait_event(u132_hcd_wait, u132_instances == 0);
index 957c87e..9b88745 100644 (file)
@@ -287,17 +287,21 @@ static const struct hc_driver uhci_driver = {
 static const struct pci_device_id uhci_pci_ids[] = { {
        /* handle any USB UHCI controller */
        PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_UHCI, ~0),
-       .driver_data =  (unsigned long) &uhci_driver,
        }, { /* end: all zeroes */ }
 };
 
 MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
 
+static int uhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
+{
+       return usb_hcd_pci_probe(dev, id, &uhci_driver);
+}
+
 static struct pci_driver uhci_pci_driver = {
        .name =         hcd_name,
        .id_table =     uhci_pci_ids,
 
-       .probe =        usb_hcd_pci_probe,
+       .probe =        uhci_pci_probe,
        .remove =       usb_hcd_pci_remove,
        .shutdown =     uhci_shutdown,
 
index f7a4e24..56db635 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * xhci-debugfs.h - xHCI debugfs interface
  *
index 268328c..fa59b24 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * xHCI host controller driver
  *
index acd5651..a93cfe8 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (c) 2015 MediaTek Inc.
  * Author:
index ca0a3a5..3be0217 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (C) 2014 Marvell
  *
diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c
new file mode 100644 (file)
index 0000000..59b1965
--- /dev/null
@@ -0,0 +1,645 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (C) 2019-2020 Linaro Limited */
+
+#include <linux/acpi.h>
+#include <linux/firmware.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
+
+#include "xhci.h"
+#include "xhci-trace.h"
+#include "xhci-pci.h"
+
+#define RENESAS_FW_VERSION                             0x6C
+#define RENESAS_ROM_CONFIG                             0xF0
+#define RENESAS_FW_STATUS                              0xF4
+#define RENESAS_FW_STATUS_MSB                          0xF5
+#define RENESAS_ROM_STATUS                             0xF6
+#define RENESAS_ROM_STATUS_MSB                         0xF7
+#define RENESAS_DATA0                                  0xF8
+#define RENESAS_DATA1                                  0xFC
+
+#define RENESAS_FW_VERSION_FIELD                       GENMASK(23, 7)
+#define RENESAS_FW_VERSION_OFFSET                      8
+
+#define RENESAS_FW_STATUS_DOWNLOAD_ENABLE              BIT(0)
+#define RENESAS_FW_STATUS_LOCK                         BIT(1)
+#define RENESAS_FW_STATUS_RESULT                       GENMASK(6, 4)
+  #define RENESAS_FW_STATUS_INVALID                    0
+  #define RENESAS_FW_STATUS_SUCCESS                    BIT(4)
+  #define RENESAS_FW_STATUS_ERROR                      BIT(5)
+#define RENESAS_FW_STATUS_SET_DATA0                    BIT(8)
+#define RENESAS_FW_STATUS_SET_DATA1                    BIT(9)
+
+#define RENESAS_ROM_STATUS_ACCESS                      BIT(0)
+#define RENESAS_ROM_STATUS_ERASE                       BIT(1)
+#define RENESAS_ROM_STATUS_RELOAD                      BIT(2)
+#define RENESAS_ROM_STATUS_RESULT                      GENMASK(6, 4)
+  #define RENESAS_ROM_STATUS_NO_RESULT                 0
+  #define RENESAS_ROM_STATUS_SUCCESS                   BIT(4)
+  #define RENESAS_ROM_STATUS_ERROR                     BIT(5)
+#define RENESAS_ROM_STATUS_SET_DATA0                   BIT(8)
+#define RENESAS_ROM_STATUS_SET_DATA1                   BIT(9)
+#define RENESAS_ROM_STATUS_ROM_EXISTS                  BIT(15)
+
+#define RENESAS_ROM_ERASE_MAGIC                                0x5A65726F
+#define RENESAS_ROM_WRITE_MAGIC                                0x53524F4D
+
+#define RENESAS_RETRY  10000
+#define RENESAS_DELAY  10
+
+#define ROM_VALID_01 0x2013
+#define ROM_VALID_02 0x2026
+
+static int renesas_verify_fw_version(struct pci_dev *pdev, u32 version)
+{
+       switch (version) {
+       case ROM_VALID_01:
+       case ROM_VALID_02:
+               return 0;
+       }
+       dev_err(&pdev->dev, "FW has invalid version :%d\n", version);
+       return -EINVAL;
+}
+
+static int renesas_fw_download_image(struct pci_dev *dev,
+                                    const u32 *fw, size_t step, bool rom)
+{
+       size_t i;
+       int err;
+       u8 fw_status;
+       bool data0_or_data1;
+       u32 status_reg;
+
+       if (rom)
+               status_reg = RENESAS_ROM_STATUS_MSB;
+       else
+               status_reg = RENESAS_FW_STATUS_MSB;
+
+       /*
+        * The hardware does alternate between two 32-bit pages.
+        * (This is because each row of the firmware is 8 bytes).
+        *
+        * for even steps we use DATA0, for odd steps DATA1.
+        */
+       data0_or_data1 = (step & 1) == 1;
+
+       /* step+1. Read "Set DATAX" and confirm it is cleared. */
+       for (i = 0; i < RENESAS_RETRY; i++) {
+               err = pci_read_config_byte(dev, status_reg, &fw_status);
+               if (err) {
+                       dev_err(&dev->dev, "Read Status failed: %d\n",
+                               pcibios_err_to_errno(err));
+                       return pcibios_err_to_errno(err);
+               }
+               if (!(fw_status & BIT(data0_or_data1)))
+                       break;
+
+               udelay(RENESAS_DELAY);
+       }
+       if (i == RENESAS_RETRY) {
+               dev_err(&dev->dev, "Timeout for Set DATAX step: %zd\n", step);
+               return -ETIMEDOUT;
+       }
+
+       /*
+        * step+2. Write FW data to "DATAX".
+        * "LSB is left" => force little endian
+        */
+       err = pci_write_config_dword(dev, data0_or_data1 ?
+                                    RENESAS_DATA1 : RENESAS_DATA0,
+                                    (__force u32)cpu_to_le32(fw[step]));
+       if (err) {
+               dev_err(&dev->dev, "Write to DATAX failed: %d\n",
+                       pcibios_err_to_errno(err));
+               return pcibios_err_to_errno(err);
+       }
+
+       udelay(100);
+
+       /* step+3. Set "Set DATAX". */
+       err = pci_write_config_byte(dev, status_reg, BIT(data0_or_data1));
+       if (err) {
+               dev_err(&dev->dev, "Write config for DATAX failed: %d\n",
+                       pcibios_err_to_errno(err));
+               return pcibios_err_to_errno(err);
+       }
+
+       return 0;
+}
+
+static int renesas_fw_verify(const void *fw_data,
+                            size_t length)
+{
+       u16 fw_version_pointer;
+       u16 fw_version;
+
+       /*
+        * The Firmware's Data Format is describe in
+        * "6.3 Data Format" R19UH0078EJ0500 Rev.5.00 page 124
+        */
+
+       /*
+        * The bootrom chips of the big brother have sizes up to 64k, let's
+        * assume that's the biggest the firmware can get.
+        */
+       if (length < 0x1000 || length >= 0x10000) {
+               pr_err("firmware is size %zd is not (4k - 64k).",
+                       length);
+               return -EINVAL;
+       }
+
+       /* The First 2 bytes are fixed value (55aa). "LSB on Left" */
+       if (get_unaligned_le16(fw_data) != 0x55aa) {
+               pr_err("no valid firmware header found.");
+               return -EINVAL;
+       }
+
+       /* verify the firmware version position and print it. */
+       fw_version_pointer = get_unaligned_le16(fw_data + 4);
+       if (fw_version_pointer + 2 >= length) {
+               pr_err("fw ver pointer is outside of the firmware image");
+               return -EINVAL;
+       }
+
+       fw_version = get_unaligned_le16(fw_data + fw_version_pointer);
+       pr_err("got firmware version: %02x.", fw_version);
+
+       return 0;
+}
+
+static bool renesas_check_rom(struct pci_dev *pdev)
+{
+       u16 rom_status;
+       int retval;
+
+       /* Check if external ROM exists */
+       retval = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_status);
+       if (retval)
+               return false;
+
+       rom_status &= RENESAS_ROM_STATUS_ROM_EXISTS;
+       if (rom_status) {
+               dev_dbg(&pdev->dev, "External ROM exists\n");
+               return true; /* External ROM exists */
+       }
+
+       return false;
+}
+
+static int renesas_check_rom_state(struct pci_dev *pdev)
+{
+       u16 rom_state;
+       u32 version;
+       int err;
+
+       /* check FW version */
+       err = pci_read_config_dword(pdev, RENESAS_FW_VERSION, &version);
+       if (err)
+               return pcibios_err_to_errno(err);
+
+       version &= RENESAS_FW_VERSION_FIELD;
+       version = version >> RENESAS_FW_VERSION_OFFSET;
+
+       err = renesas_verify_fw_version(pdev, version);
+       if (err)
+               return err;
+
+       /*
+        * Test if ROM is present and loaded, if so we can skip everything
+        */
+       err = pci_read_config_word(pdev, RENESAS_ROM_STATUS, &rom_state);
+       if (err)
+               return pcibios_err_to_errno(err);
+
+       if (rom_state & BIT(15)) {
+               /* ROM exists */
+               dev_dbg(&pdev->dev, "ROM exists\n");
+
+               /* Check the "Result Code" Bits (6:4) and act accordingly */
+               switch (rom_state & RENESAS_ROM_STATUS_RESULT) {
+               case RENESAS_ROM_STATUS_SUCCESS:
+                       return 0;
+
+               case RENESAS_ROM_STATUS_NO_RESULT: /* No result yet */
+                       return 0;
+
+               case RENESAS_ROM_STATUS_ERROR: /* Error State */
+               default: /* All other states are marked as "Reserved states" */
+                       dev_err(&pdev->dev, "Invalid ROM..");
+                       break;
+               }
+       }
+
+       return -EIO;
+}
+
+static int renesas_fw_check_running(struct pci_dev *pdev)
+{
+       u8 fw_state;
+       int err;
+
+       /* Check if device has ROM and loaded, if so skip everything */
+       err = renesas_check_rom(pdev);
+       if (err) { /* we have rom */
+               err = renesas_check_rom_state(pdev);
+               if (!err)
+                       return err;
+       }
+
+       /*
+        * Test if the device is actually needing the firmware. As most
+        * BIOSes will initialize the device for us. If the device is
+        * initialized.
+        */
+       err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_state);
+       if (err)
+               return pcibios_err_to_errno(err);
+
+       /*
+        * Check if "FW Download Lock" is locked. If it is and the FW is
+        * ready we can simply continue. If the FW is not ready, we have
+        * to give up.
+        */
+       if (fw_state & RENESAS_FW_STATUS_LOCK) {
+               dev_dbg(&pdev->dev, "FW Download Lock is engaged.");
+
+               if (fw_state & RENESAS_FW_STATUS_SUCCESS)
+                       return 0;
+
+               dev_err(&pdev->dev,
+                       "FW Download Lock is set and FW is not ready. Giving Up.");
+               return -EIO;
+       }
+
+       /*
+        * Check if "FW Download Enable" is set. If someone (us?) tampered
+        * with it and it can't be reset, we have to give up too... and
+        * ask for a forgiveness and a reboot.
+        */
+       if (fw_state & RENESAS_FW_STATUS_DOWNLOAD_ENABLE) {
+               dev_err(&pdev->dev,
+                       "FW Download Enable is stale. Giving Up (poweroff/reboot needed).");
+               return -EIO;
+       }
+
+       /* Otherwise, Check the "Result Code" Bits (6:4) and act accordingly */
+       switch (fw_state & RENESAS_FW_STATUS_RESULT) {
+       case 0: /* No result yet */
+               dev_dbg(&pdev->dev, "FW is not ready/loaded yet.");
+
+               /* tell the caller, that this device needs the firmware. */
+               return 1;
+
+       case RENESAS_FW_STATUS_SUCCESS: /* Success, device should be working. */
+               dev_dbg(&pdev->dev, "FW is ready.");
+               return 0;
+
+       case RENESAS_FW_STATUS_ERROR: /* Error State */
+               dev_err(&pdev->dev,
+                       "hardware is in an error state. Giving up (poweroff/reboot needed).");
+               return -ENODEV;
+
+       default: /* All other states are marked as "Reserved states" */
+               dev_err(&pdev->dev,
+                       "hardware is in an invalid state %lx. Giving up (poweroff/reboot needed).",
+                       (fw_state & RENESAS_FW_STATUS_RESULT) >> 4);
+               return -EINVAL;
+       }
+}
+
+static int renesas_fw_download(struct pci_dev *pdev,
+                              const struct firmware *fw)
+{
+       const u32 *fw_data = (const u32 *)fw->data;
+       size_t i;
+       int err;
+       u8 fw_status;
+
+       /*
+        * For more information and the big picture: please look at the
+        * "Firmware Download Sequence" in "7.1 FW Download Interface"
+        * of R19UH0078EJ0500 Rev.5.00 page 131
+        */
+
+       /*
+        * 0. Set "FW Download Enable" bit in the
+        * "FW Download Control & Status Register" at 0xF4
+        */
+       err = pci_write_config_byte(pdev, RENESAS_FW_STATUS,
+                                   RENESAS_FW_STATUS_DOWNLOAD_ENABLE);
+       if (err)
+               return pcibios_err_to_errno(err);
+
+       /* 1 - 10 follow one step after the other. */
+       for (i = 0; i < fw->size / 4; i++) {
+               err = renesas_fw_download_image(pdev, fw_data, i, false);
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "Firmware Download Step %zd failed at position %zd bytes with (%d).",
+                               i, i * 4, err);
+                       return err;
+               }
+       }
+
+       /*
+        * This sequence continues until the last data is written to
+        * "DATA0" or "DATA1". Naturally, we wait until "SET DATA0/1"
+        * is cleared by the hardware beforehand.
+        */
+       for (i = 0; i < RENESAS_RETRY; i++) {
+               err = pci_read_config_byte(pdev, RENESAS_FW_STATUS_MSB,
+                                          &fw_status);
+               if (err)
+                       return pcibios_err_to_errno(err);
+               if (!(fw_status & (BIT(0) | BIT(1))))
+                       break;
+
+               udelay(RENESAS_DELAY);
+       }
+       if (i == RENESAS_RETRY)
+               dev_warn(&pdev->dev, "Final Firmware Download step timed out.");
+
+       /*
+        * 11. After finishing writing the last data of FW, the
+        * System Software must clear "FW Download Enable"
+        */
+       err = pci_write_config_byte(pdev, RENESAS_FW_STATUS, 0);
+       if (err)
+               return pcibios_err_to_errno(err);
+
+       /* 12. Read "Result Code" and confirm it is good. */
+       for (i = 0; i < RENESAS_RETRY; i++) {
+               err = pci_read_config_byte(pdev, RENESAS_FW_STATUS, &fw_status);
+               if (err)
+                       return pcibios_err_to_errno(err);
+               if (fw_status & RENESAS_FW_STATUS_SUCCESS)
+                       break;
+
+               udelay(RENESAS_DELAY);
+       }
+       if (i == RENESAS_RETRY) {
+               /* Timed out / Error - let's see if we can fix this */
+               err = renesas_fw_check_running(pdev);
+               switch (err) {
+               case 0: /*
+                        * we shouldn't end up here.
+                        * maybe it took a little bit longer.
+                        * But all should be well?
+                        */
+                       break;
+
+               case 1: /* (No result yet! */
+                       dev_err(&pdev->dev, "FW Load timedout");
+                       return -ETIMEDOUT;
+
+               default:
+                       return err;
+               }
+       }
+
+       return 0;
+}
+
+static void renesas_rom_erase(struct pci_dev *pdev)
+{
+       int retval, i;
+       u8 status;
+
+       dev_dbg(&pdev->dev, "Performing ROM Erase...\n");
+       retval = pci_write_config_dword(pdev, RENESAS_DATA0,
+                                       RENESAS_ROM_ERASE_MAGIC);
+       if (retval) {
+               dev_err(&pdev->dev, "ROM erase, magic word write failed: %d\n",
+                       pcibios_err_to_errno(retval));
+               return;
+       }
+
+       retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+       if (retval) {
+               dev_err(&pdev->dev, "ROM status read failed: %d\n",
+                       pcibios_err_to_errno(retval));
+               return;
+       }
+       status |= RENESAS_ROM_STATUS_ERASE;
+       retval = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, status);
+       if (retval) {
+               dev_err(&pdev->dev, "ROM erase set word write failed\n");
+               return;
+       }
+
+       /* sleep a bit while ROM is erased */
+       msleep(20);
+
+       for (i = 0; i < RENESAS_RETRY; i++) {
+               retval = pci_read_config_byte(pdev, RENESAS_ROM_STATUS,
+                                             &status);
+               status &= RENESAS_ROM_STATUS_ERASE;
+               if (!status)
+                       break;
+
+               mdelay(RENESAS_DELAY);
+       }
+
+       if (i == RENESAS_RETRY)
+               dev_dbg(&pdev->dev, "Chip erase timedout: %x\n", status);
+
+       dev_dbg(&pdev->dev, "ROM Erase... Done success\n");
+}
+
+static bool renesas_setup_rom(struct pci_dev *pdev, const struct firmware *fw)
+{
+       const u32 *fw_data = (const u32 *)fw->data;
+       int err, i;
+       u8 status;
+
+       /* 2. Write magic word to Data0 */
+       err = pci_write_config_dword(pdev, RENESAS_DATA0,
+                                    RENESAS_ROM_WRITE_MAGIC);
+       if (err)
+               return false;
+
+       /* 3. Set External ROM access */
+       err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS,
+                                   RENESAS_ROM_STATUS_ACCESS);
+       if (err)
+               goto remove_bypass;
+
+       /* 4. Check the result */
+       err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+       if (err)
+               goto remove_bypass;
+       status &= GENMASK(6, 4);
+       if (status) {
+               dev_err(&pdev->dev,
+                       "setting external rom failed: %x\n", status);
+               goto remove_bypass;
+       }
+
+       /* 5 to 16 Write FW to DATA0/1 while checking SetData0/1 */
+       for (i = 0; i < fw->size / 4; i++) {
+               err = renesas_fw_download_image(pdev, fw_data, i, true);
+               if (err) {
+                       dev_err(&pdev->dev,
+                               "ROM Download Step %d failed at position %d bytes with (%d)\n",
+                                i, i * 4, err);
+                       goto remove_bypass;
+               }
+       }
+
+       /*
+        * wait till DATA0/1 is cleared
+        */
+       for (i = 0; i < RENESAS_RETRY; i++) {
+               err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS_MSB,
+                                          &status);
+               if (err)
+                       goto remove_bypass;
+               if (!(status & (BIT(0) | BIT(1))))
+                       break;
+
+               udelay(RENESAS_DELAY);
+       }
+       if (i == RENESAS_RETRY) {
+               dev_err(&pdev->dev, "Final Firmware ROM Download step timed out\n");
+               goto remove_bypass;
+       }
+
+       /* 17. Remove bypass */
+       err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0);
+       if (err)
+               return false;
+
+       udelay(10);
+
+       /* 18. check result */
+       for (i = 0; i < RENESAS_RETRY; i++) {
+               err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+               if (err) {
+                       dev_err(&pdev->dev, "Read ROM status failed:%d\n",
+                               pcibios_err_to_errno(err));
+                       return false;
+               }
+               status &= RENESAS_ROM_STATUS_RESULT;
+               if (status ==  RENESAS_ROM_STATUS_SUCCESS) {
+                       dev_dbg(&pdev->dev, "Download ROM success\n");
+                       break;
+               }
+               udelay(RENESAS_DELAY);
+       }
+       if (i == RENESAS_RETRY) { /* Timed out */
+               dev_err(&pdev->dev,
+                       "Download to external ROM TO: %x\n", status);
+               return false;
+       }
+
+       dev_dbg(&pdev->dev, "Download to external ROM succeeded\n");
+
+       /* Last step set Reload */
+       err = pci_write_config_byte(pdev, RENESAS_ROM_STATUS,
+                                   RENESAS_ROM_STATUS_RELOAD);
+       if (err) {
+               dev_err(&pdev->dev, "Set ROM execute failed: %d\n",
+                       pcibios_err_to_errno(err));
+               return false;
+       }
+
+       /*
+        * wait till Reload is cleared
+        */
+       for (i = 0; i < RENESAS_RETRY; i++) {
+               err = pci_read_config_byte(pdev, RENESAS_ROM_STATUS, &status);
+               if (err)
+                       return false;
+               if (!(status & RENESAS_ROM_STATUS_RELOAD))
+                       break;
+
+               udelay(RENESAS_DELAY);
+       }
+       if (i == RENESAS_RETRY) {
+               dev_err(&pdev->dev, "ROM Exec timed out: %x\n", status);
+               return false;
+       }
+
+       return true;
+
+remove_bypass:
+       pci_write_config_byte(pdev, RENESAS_ROM_STATUS, 0);
+       return false;
+}
+
+static int renesas_load_fw(struct pci_dev *pdev, const struct firmware *fw)
+{
+       int err = 0;
+       bool rom;
+
+       /* Check if the device has external ROM */
+       rom = renesas_check_rom(pdev);
+       if (rom) {
+               /* perform chip erase first */
+               renesas_rom_erase(pdev);
+
+               /* lets try loading fw on ROM first */
+               rom = renesas_setup_rom(pdev, fw);
+               if (!rom) {
+                       dev_dbg(&pdev->dev,
+                               "ROM load failed, falling back on FW load\n");
+               } else {
+                       dev_dbg(&pdev->dev,
+                               "ROM load success\n");
+                       goto exit;
+               }
+       }
+
+       err = renesas_fw_download(pdev, fw);
+
+exit:
+       if (err)
+               dev_err(&pdev->dev, "firmware failed to download (%d).", err);
+       return err;
+}
+
+int renesas_xhci_check_request_fw(struct pci_dev *pdev,
+                                 const struct pci_device_id *id)
+{
+       struct xhci_driver_data *driver_data =
+                       (struct xhci_driver_data *)id->driver_data;
+       const char *fw_name = driver_data->firmware;
+       const struct firmware *fw;
+       int err;
+
+       err = renesas_fw_check_running(pdev);
+       /* Continue ahead, if the firmware is already running. */
+       if (err == 0)
+               return 0;
+
+       if (err != 1)
+               return err;
+
+       pci_dev_get(pdev);
+       err = request_firmware(&fw, fw_name, &pdev->dev);
+       pci_dev_put(pdev);
+       if (err) {
+               dev_err(&pdev->dev, "request_firmware failed: %d\n", err);
+               return err;
+       }
+
+       err = renesas_fw_verify(fw->data, fw->size);
+       if (err)
+               goto exit;
+
+       err = renesas_load_fw(pdev, fw);
+exit:
+       release_firmware(fw);
+       return err;
+}
+EXPORT_SYMBOL_GPL(renesas_xhci_check_request_fw);
+
+void renesas_xhci_pci_exit(struct pci_dev *dev)
+{
+}
+EXPORT_SYMBOL_GPL(renesas_xhci_pci_exit);
+
+MODULE_LICENSE("GPL v2");
index 766b747..ef513c2 100644 (file)
@@ -15,6 +15,7 @@
 
 #include "xhci.h"
 #include "xhci-trace.h"
+#include "xhci-pci.h"
 
 #define SSIC_PORT_NUM          2
 #define SSIC_PORT_CFG2         0x880c
@@ -87,7 +88,16 @@ static int xhci_pci_reinit(struct xhci_hcd *xhci, struct pci_dev *pdev)
 
 static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
 {
-       struct pci_dev          *pdev = to_pci_dev(dev);
+       struct pci_dev                  *pdev = to_pci_dev(dev);
+       struct xhci_driver_data         *driver_data;
+       const struct pci_device_id      *id;
+
+       id = pci_match_id(pdev->driver->id_table, pdev);
+
+       if (id && id->driver_data) {
+               driver_data = (struct xhci_driver_data *)id->driver_data;
+               xhci->quirks |= driver_data->quirks;
+       }
 
        /* Look for vendor-specific quirks */
        if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
@@ -327,10 +337,15 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
        int retval;
        struct xhci_hcd *xhci;
-       struct hc_driver *driver;
        struct usb_hcd *hcd;
+       struct xhci_driver_data *driver_data;
 
-       driver = (struct hc_driver *)id->driver_data;
+       driver_data = (struct xhci_driver_data *)id->driver_data;
+       if (driver_data && driver_data->quirks & XHCI_RENESAS_FW_QUIRK) {
+               retval = renesas_xhci_check_request_fw(dev, id);
+               if (retval)
+                       return retval;
+       }
 
        /* Prevent runtime suspending between USB-2 and USB-3 initialization */
        pm_runtime_get_noresume(&dev->dev);
@@ -341,7 +356,7 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
         * to say USB 2.0, but I'm not sure what the implications would be in
         * the other parts of the HCD code.
         */
-       retval = usb_hcd_pci_probe(dev, id);
+       retval = usb_hcd_pci_probe(dev, id, &xhci_pci_hc_driver);
 
        if (retval)
                goto put_runtime_pm;
@@ -349,8 +364,8 @@ static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
        /* USB 2.0 roothub is stored in the PCI device now. */
        hcd = dev_get_drvdata(&dev->dev);
        xhci = hcd_to_xhci(hcd);
-       xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev,
-                               pci_name(dev), hcd);
+       xhci->shared_hcd = usb_create_shared_hcd(&xhci_pci_hc_driver, &dev->dev,
+                                                pci_name(dev), hcd);
        if (!xhci->shared_hcd) {
                retval = -ENOMEM;
                goto dealloc_usb2_hcd;
@@ -392,6 +407,9 @@ static void xhci_pci_remove(struct pci_dev *dev)
        struct xhci_hcd *xhci;
 
        xhci = hcd_to_xhci(pci_get_drvdata(dev));
+       if (xhci->quirks & XHCI_RENESAS_FW_QUIRK)
+               renesas_xhci_pci_exit(dev);
+
        xhci->xhc_state |= XHCI_STATE_REMOVING;
 
        if (xhci->quirks & XHCI_DEFAULT_PM_RUNTIME_ALLOW)
@@ -543,15 +561,26 @@ static void xhci_pci_shutdown(struct usb_hcd *hcd)
 
 /*-------------------------------------------------------------------------*/
 
+static const struct xhci_driver_data reneses_data = {
+       .quirks  = XHCI_RENESAS_FW_QUIRK,
+       .firmware = "renesas_usb_fw.mem",
+};
+
 /* PCI driver selection metadata; PCI hotplugging uses this */
-static const struct pci_device_id pci_ids[] = { {
+static const struct pci_device_id pci_ids[] = {
+       { PCI_DEVICE(0x1912, 0x0014),
+               .driver_data =  (unsigned long)&reneses_data,
+       },
+       { PCI_DEVICE(0x1912, 0x0015),
+               .driver_data =  (unsigned long)&reneses_data,
+       },
        /* handle any USB 3.0 xHCI controller */
-       PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
-       .driver_data =  (unsigned long) &xhci_pci_hc_driver,
+       { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0),
        },
        { /* end: all zeroes */ }
 };
 MODULE_DEVICE_TABLE(pci, pci_ids);
+MODULE_FIRMWARE("renesas_usb_fw.mem");
 
 /* pci driver glue; this is a "new style" PCI driver module */
 static struct pci_driver xhci_pci_driver = {
diff --git a/drivers/usb/host/xhci-pci.h b/drivers/usb/host/xhci-pci.h
new file mode 100644 (file)
index 0000000..acd7cf0
--- /dev/null
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/* Copyright (C) 2019-2020 Linaro Limited */
+
+#ifndef XHCI_PCI_H
+#define XHCI_PCI_H
+
+#if IS_ENABLED(CONFIG_USB_XHCI_PCI_RENESAS)
+int renesas_xhci_check_request_fw(struct pci_dev *dev,
+                                 const struct pci_device_id *id);
+void renesas_xhci_pci_exit(struct pci_dev *dev);
+
+#else
+static int renesas_xhci_check_request_fw(struct pci_dev *dev,
+                                        const struct pci_device_id *id)
+{
+       return 0;
+}
+
+static void renesas_xhci_pci_exit(struct pci_dev *dev) { };
+
+#endif
+
+struct xhci_driver_data {
+       u64 quirks;
+       const char *firmware;
+};
+
+#endif
index ea460b9..f6b4089 100644 (file)
@@ -112,6 +112,10 @@ static const struct xhci_plat_priv xhci_plat_renesas_rcar_gen3 = {
        SET_XHCI_PLAT_PRIV_FOR_RCAR(XHCI_RCAR_FIRMWARE_NAME_V3)
 };
 
+static const struct xhci_plat_priv xhci_plat_brcm = {
+       .quirks = XHCI_RESET_ON_RESUME,
+};
+
 static const struct of_device_id usb_xhci_of_match[] = {
        {
                .compatible = "generic-xhci",
@@ -147,6 +151,12 @@ static const struct of_device_id usb_xhci_of_match[] = {
        }, {
                .compatible = "renesas,rcar-gen3-xhci",
                .data = &xhci_plat_renesas_rcar_gen3,
+       }, {
+               .compatible = "brcm,xhci-brcm-v2",
+               .data = &xhci_plat_brcm,
+       }, {
+               .compatible = "brcm,bcm7445-xhci",
+               .data = &xhci_plat_brcm,
        },
        {},
 };
@@ -409,7 +419,15 @@ static int __maybe_unused xhci_plat_resume(struct device *dev)
        if (ret)
                return ret;
 
-       return xhci_resume(xhci, 0);
+       ret = xhci_resume(xhci, 0);
+       if (ret)
+               return ret;
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+
+       return 0;
 }
 
 static int __maybe_unused xhci_plat_runtime_suspend(struct device *dev)
index 5681723..b49f644 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * xhci-plat.h - xHCI host controller driver platform Bus Glue.
  *
index 012744a..048ad3b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * drivers/usb/host/xhci-rcar.h
  *
index b19582b..627abd2 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * xHCI host controller driver
  *
index 86cfefd..2c6c4f8 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 
 /*
  * xHCI host controller driver
@@ -1873,6 +1873,7 @@ struct xhci_hcd {
 #define XHCI_DEFAULT_PM_RUNTIME_ALLOW  BIT_ULL(33)
 #define XHCI_RESET_PLL_ON_DISCONNECT   BIT_ULL(34)
 #define XHCI_SNPS_BROKEN_SUSPEND    BIT_ULL(35)
+#define XHCI_RENESAS_FW_QUIRK  BIT_ULL(36)
 
        unsigned int            num_active_eps;
        unsigned int            limit_active_eps;
index 97cb4d7..d9a0a4c 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Driver for the NXP ISP1760 chip
  *
index 1f00c38..fedc4f5 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Driver for the NXP ISP1760 chip
  *
index 2d0b887..d2df650 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Driver for the NXP ISP1761 device controller
  *
index 8a5e6bb..c0fb9e1 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) */
 /*
  * sisusb - usb kernel driver for Net2280/SiS315 based USB2VGA dongles
  *
index ace0998..aa33bc8 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
 /* $XFree86$ */
 /* $XdotOrg$ */
 /*
index 706d770..3df64d2 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
+/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
 /*
  * General structure definitions for universal mode switching modules
  *
index 4bf7773..1584efb 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
 * Common Header File for the Elan Digital Systems U132 adapter
 * this file should be included by both the "ftdi-u132" and
index 6087be2..d49db92 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * mtu3.h - MediaTek USB3 DRD header
  *
index e96a692..fb6b282 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * mtu3_debug.h - debug header
  *
index 5e58c4d..760fe7d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * mtu3_dr.h - dual role switch and host glue layer header
  *
index 8382d06..bf34f78 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * mtu3_hw_regs.h - MediaTek USB3 DRD register and field definitions
  *
index 9cfde20..66e1c0a 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * mtu3_qmu.h - Queue Management Unit driver header
  *
index 050e30f..1b89763 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /**
  * mtu3_trace.h - trace support
  *
index e021485..c8e67d1 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (C) 2005-2006 by Texas Instruments
  */
index e64dd30..c4fe1f4 100644 (file)
@@ -30,11 +30,11 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
        irqreturn_t     retval = IRQ_NONE, retval_dma = IRQ_NONE;
        struct musb     *musb = __hci;
 
-       spin_lock_irqsave(&musb->lock, flags);
-
        if (IS_ENABLED(CONFIG_USB_INVENTRA_DMA) && musb->dma_controller)
                retval_dma = dma_controller_irq(irq, musb->dma_controller);
 
+       spin_lock_irqsave(&musb->lock, flags);
+
        musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
        musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
        musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
index 6196b0e..eebeadd 100644 (file)
@@ -208,6 +208,12 @@ static irqreturn_t generic_interrupt(int irq, void *__hci)
        musb->int_rx = musb_clearw(musb->mregs, MUSB_INTRRX);
        musb->int_tx = musb_clearw(musb->mregs, MUSB_INTRTX);
 
+       if ((musb->int_usb & MUSB_INTR_RESET) && !is_host_active(musb)) {
+               /* ep0 FADDR must be 0 when (re)entering peripheral mode */
+               musb_ep_select(musb->mregs, 0);
+               musb_writeb(musb->mregs, MUSB_FADDR, 0);
+       }
+
        if (musb->int_usb || musb->int_tx || musb->int_rx)
                retval = musb_interrupt(musb);
 
index d590110..384a803 100644 (file)
@@ -1795,7 +1795,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
 EXPORT_SYMBOL_GPL(musb_interrupt);
 
 #ifndef CONFIG_MUSB_PIO_ONLY
-static bool use_dma = 1;
+static bool use_dma = true;
 
 /* "modprobe ... use_dma=0" etc */
 module_param(use_dma, bool, 0644);
@@ -2877,6 +2877,13 @@ static int musb_resume(struct device *dev)
        musb_enable_interrupts(musb);
        musb_platform_enable(musb);
 
+       /* session might be disabled in suspend */
+       if (musb->port_mode == MUSB_HOST &&
+           !(musb->ops->quirks & MUSB_PRESERVE_SESSION)) {
+               devctl |= MUSB_DEVCTL_SESSION;
+               musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
+       }
+
        spin_lock_irqsave(&musb->lock, flags);
        error = musb_run_resume_work(musb);
        if (error)
index 290a2bc..dbe5623 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * MUSB OTG driver defines
  *
index c444a80..e5b3506 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * MUSB OTG driver debug defines
  *
index 7b6281a..30a89aa 100644 (file)
@@ -168,6 +168,11 @@ static ssize_t musb_test_mode_write(struct file *file,
        u8                      test;
        char                    buf[24];
 
+       memset(buf, 0x00, sizeof(buf));
+
+       if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+               return -EFAULT;
+
        pm_runtime_get_sync(musb->controller);
        test = musb_readb(musb->mregs, MUSB_TESTMODE);
        if (test) {
@@ -176,11 +181,6 @@ static ssize_t musb_test_mode_write(struct file *file,
                goto ret;
        }
 
-       memset(buf, 0x00, sizeof(buf));
-
-       if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
-               return -EFAULT;
-
        if (strstarts(buf, "force host full-speed"))
                test = MUSB_TEST_FORCE_HOST | MUSB_TEST_FORCE_FS;
 
index 4b4d8dc..7d67b69 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * MUSB OTG driver DMA controller abstraction
  *
index d026636..f49f25b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * MUSB OTG driver peripheral defines
  *
index 8736f42..8b7d22a 100644 (file)
@@ -1774,9 +1774,15 @@ void musb_host_rx(struct musb *musb, u8 epnum)
                status = -EPIPE;
 
        } else if (rx_csr & MUSB_RXCSR_H_ERROR) {
-               musb_dbg(musb, "end %d RX proto error", epnum);
+               dev_err(musb->controller, "ep%d RX three-strikes error", epnum);
 
-               status = -EPROTO;
+               /*
+                * The three-strikes error could only happen when the USB
+                * device is not accessible, for example detached or powered
+                * off. So return the fatal error -ESHUTDOWN so hopefully the
+                * USB device drivers won't immediately resubmit the same URB.
+                */
+               status = -ESHUTDOWN;
                musb_writeb(epio, MUSB_RXINTERVAL, 0);
 
                rx_csr &= ~MUSB_RXCSR_H_ERROR;
index 2999845..3233657 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * MUSB OTG driver host defines
  *
index f17aabd..12874d3 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * MUSB OTG driver register I/O
  *
index 5cd7264..5fa1109 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * MUSB OTG driver register defines
  *
index b193daf..380ebc7 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * musb_trace.h - MUSB Controller Trace Support
  *
index 859008f..939a036 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (C) 2005-2006 by Texas Instruments
  */
index fd8025b..8a25356 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Definitions for TUSB6010 USB 2.0 OTG Dual Role controller
  *
index 43d410f..fbcc28a 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. */
 
 #include <linux/usb/otg-fsm.h>
index 3ea1f5b..8f62dc2 100644 (file)
@@ -125,13 +125,13 @@ static int jz4770_phy_init(struct usb_phy *phy)
 
        err = regulator_enable(priv->vcc_supply);
        if (err) {
-               dev_err(priv->dev, "Unable to enable VCC: %d", err);
+               dev_err(priv->dev, "Unable to enable VCC: %d\n", err);
                return err;
        }
 
        err = clk_prepare_enable(priv->clk);
        if (err) {
-               dev_err(priv->dev, "Unable to start clock: %d", err);
+               dev_err(priv->dev, "Unable to start clock: %d\n", err);
                return err;
        }
 
@@ -191,7 +191,7 @@ static int jz4770_phy_probe(struct platform_device *pdev)
 
        priv->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(priv->base)) {
-               dev_err(dev, "Failed to map registers");
+               dev_err(dev, "Failed to map registers\n");
                return PTR_ERR(priv->base);
        }
 
@@ -199,7 +199,7 @@ static int jz4770_phy_probe(struct platform_device *pdev)
        if (IS_ERR(priv->clk)) {
                err = PTR_ERR(priv->clk);
                if (err != -EPROBE_DEFER)
-                       dev_err(dev, "Failed to get clock");
+                       dev_err(dev, "Failed to get clock\n");
                return err;
        }
 
@@ -207,14 +207,14 @@ static int jz4770_phy_probe(struct platform_device *pdev)
        if (IS_ERR(priv->vcc_supply)) {
                err = PTR_ERR(priv->vcc_supply);
                if (err != -EPROBE_DEFER)
-                       dev_err(dev, "failed to get regulator");
+                       dev_err(dev, "Failed to get regulator\n");
                return err;
        }
 
        err = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2);
        if (err) {
                if (err != -EPROBE_DEFER)
-                       dev_err(dev, "Unable to register PHY");
+                       dev_err(dev, "Unable to register PHY\n");
                return err;
        }
 
index 96701a1..5d5c0ab 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Copyright (C) 2011 Marvell International Ltd. All rights reserved.
  */
index ef1735d..eb34d76 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
 /*
  * Renesas USB driver
  *
index c3d3cc3..7d3700b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
 /*
  * Renesas USB driver
  *
index 65dc19c..56b7106 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
 /*
  * Renesas USB driver
  *
index 3b13052..a4ae9f9 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-1.0+
+/* SPDX-License-Identifier: GPL-1.0+ */
 /*
  * Renesas USB driver
  *
index 7d88732..046d07e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 #include "common.h"
 
 extern const struct renesas_usbhs_platform_info usbhs_rcar_gen2_plat_info;
index c7c5ec1..d13db30 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 #include "common.h"
 
 extern const struct renesas_usbhs_platform_info usbhs_rcar_gen3_plat_info;
index 1ca42a6..a29b75f 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 #include "common.h"
 
 extern const struct renesas_usbhs_platform_info usbhs_rza1_plat_info;
index 5b17709..27d92af 100644 (file)
@@ -49,8 +49,10 @@ int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role)
        mutex_lock(&sw->lock);
 
        ret = sw->set(sw, role);
-       if (!ret)
+       if (!ret) {
                sw->role = role;
+               kobject_uevent(&sw->dev.kobj, KOBJ_CHANGE);
+       }
 
        mutex_unlock(&sw->lock);
 
index a13a98d..89ec30c 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Definitions for Belkin USB Serial Adapter Driver
  *
index c5ecdcd..89675ee 100644 (file)
@@ -73,6 +73,8 @@
 #define CH341_LCR_CS6          0x01
 #define CH341_LCR_CS5          0x00
 
+#define CH341_QUIRK_LIMITED_PRESCALER  BIT(0)
+
 static const struct usb_device_id id_table[] = {
        { USB_DEVICE(0x4348, 0x5523) },
        { USB_DEVICE(0x1a86, 0x7523) },
@@ -87,6 +89,7 @@ struct ch341_private {
        u8 mcr;
        u8 msr;
        u8 lcr;
+       unsigned long quirks;
 };
 
 static void ch341_set_termios(struct tty_struct *tty,
@@ -159,9 +162,11 @@ static const speed_t ch341_min_rates[] = {
  *             2 <= div <= 256 if fact = 0, or
  *             9 <= div <= 256 if fact = 1
  */
-static int ch341_get_divisor(speed_t speed)
+static int ch341_get_divisor(struct ch341_private *priv)
 {
        unsigned int fact, div, clk_div;
+       speed_t speed = priv->baud_rate;
+       bool force_fact0 = false;
        int ps;
 
        /*
@@ -187,8 +192,12 @@ static int ch341_get_divisor(speed_t speed)
        clk_div = CH341_CLK_DIV(ps, fact);
        div = CH341_CLKRATE / (clk_div * speed);
 
+       /* Some devices require a lower base clock if ps < 3. */
+       if (ps < 3 && (priv->quirks & CH341_QUIRK_LIMITED_PRESCALER))
+               force_fact0 = true;
+
        /* Halve base clock (fact = 0) if required. */
-       if (div < 9 || div > 255) {
+       if (div < 9 || div > 255 || force_fact0) {
                div /= 2;
                clk_div *= 2;
                fact = 0;
@@ -227,7 +236,7 @@ static int ch341_set_baudrate_lcr(struct usb_device *dev,
        if (!priv->baud_rate)
                return -EINVAL;
 
-       val = ch341_get_divisor(priv->baud_rate);
+       val = ch341_get_divisor(priv);
        if (val < 0)
                return -EINVAL;
 
@@ -308,6 +317,54 @@ out:       kfree(buffer);
        return r;
 }
 
+static int ch341_detect_quirks(struct usb_serial_port *port)
+{
+       struct ch341_private *priv = usb_get_serial_port_data(port);
+       struct usb_device *udev = port->serial->dev;
+       const unsigned int size = 2;
+       unsigned long quirks = 0;
+       char *buffer;
+       int r;
+
+       buffer = kmalloc(size, GFP_KERNEL);
+       if (!buffer)
+               return -ENOMEM;
+
+       /*
+        * A subset of CH34x devices does not support all features. The
+        * prescaler is limited and there is no support for sending a RS232
+        * break condition. A read failure when trying to set up the latter is
+        * used to detect these devices.
+        */
+       r = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), CH341_REQ_READ_REG,
+                           USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+                           CH341_REG_BREAK, 0, buffer, size, DEFAULT_TIMEOUT);
+       if (r == -EPIPE) {
+               dev_dbg(&port->dev, "break control not supported\n");
+               quirks = CH341_QUIRK_LIMITED_PRESCALER;
+               r = 0;
+               goto out;
+       }
+
+       if (r != size) {
+               if (r >= 0)
+                       r = -EIO;
+               dev_err(&port->dev, "failed to read break control: %d\n", r);
+               goto out;
+       }
+
+       r = 0;
+out:
+       kfree(buffer);
+
+       if (quirks) {
+               dev_dbg(&port->dev, "enabling quirk flags: 0x%02lx\n", quirks);
+               priv->quirks |= quirks;
+       }
+
+       return r;
+}
+
 static int ch341_port_probe(struct usb_serial_port *port)
 {
        struct ch341_private *priv;
@@ -330,6 +387,11 @@ static int ch341_port_probe(struct usb_serial_port *port)
                goto error;
 
        usb_set_serial_port_data(port, priv);
+
+       r = ch341_detect_quirks(port);
+       if (r < 0)
+               goto error;
+
        return 0;
 
 error: kfree(priv);
index 4980f72..f18501f 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /************************************************************************
  *
  *     16654.H         Definitions for 16C654 UART used on EdgePorts
index 2e7fedb..43ba53a 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /************************************************************************
  *
  *     io_edgeport.h   Edgeport Linux Interface definitions
index 4b8e482..db4fce8 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /************************************************************************
  *
  *     IONSP.H         Definitions for I/O Networks Serial Protocol
index 9bbcee3..50b899d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*****************************************************************************
  *
  *     Copyright (C) 1997-2002 Inside Out Networks, Inc.
index 0d1a5bb..52cbc35 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /************************************************************************
  *
  *     USBVEND.H               Vendor-specific USB definitions
index b400b26..87992b2 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Infinity Unlimited USB Phoenix driver
  *
index 0084edf..e3d09a8 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Definitions for MCT (Magic Control Technology) USB-RS232 Converter Driver
  *
index 8bfffca..254a8bb 100644 (file)
@@ -1157,6 +1157,10 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_CC864_SINGLE) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_DE910_DUAL) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_UE910_V2) },
+       { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1031, 0xff),    /* Telit LE910C1-EUX */
+        .driver_info = NCTRL(0) | RSVD(3) },
+       { USB_DEVICE_INTERFACE_CLASS(TELIT_VENDOR_ID, 0x1033, 0xff),    /* Telit LE910C1-EUX (ECM) */
+        .driver_info = NCTRL(0) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG0),
          .driver_info = RSVD(0) | RSVD(1) | NCTRL(2) | RSVD(3) },
        { USB_DEVICE(TELIT_VENDOR_ID, TELIT_PRODUCT_LE922_USBCFG1),
index 1226bf2..5c25836 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Ours Technology Inc. OTi-6858 USB to serial adapter driver.
  */
index 52db551..7d3090e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Prolific PL2303 USB to serial adaptor driver header file
  */
index ce0401d..d147fea 100644 (file)
@@ -173,6 +173,7 @@ static const struct usb_device_id id_table[] = {
        {DEVICE_SWI(0x413c, 0x81b3)},   /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
        {DEVICE_SWI(0x413c, 0x81b5)},   /* Dell Wireless 5811e QDL */
        {DEVICE_SWI(0x413c, 0x81b6)},   /* Dell Wireless 5811e QDL */
+       {DEVICE_SWI(0x413c, 0x81cb)},   /* Dell Wireless 5816e QDL */
        {DEVICE_SWI(0x413c, 0x81cc)},   /* Dell Wireless 5816e */
        {DEVICE_SWI(0x413c, 0x81cf)},   /* Dell Wireless 5819 */
        {DEVICE_SWI(0x413c, 0x81d0)},   /* Dell Wireless 5819 */
index 13be21a..4b98458 100644 (file)
@@ -270,6 +270,10 @@ static void usb_wwan_indat_callback(struct urb *urb)
        if (status) {
                dev_dbg(dev, "%s: nonzero status: %d on endpoint %02x.\n",
                        __func__, status, endpoint);
+
+               /* don't resubmit on fatal errors */
+               if (status == -ESHUTDOWN || status == -ENOENT)
+                       return;
        } else {
                if (urb->actual_length) {
                        tty_insert_flip_string(&port->port, data,
index 4bd69d0..622d639 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * USB HandSpring Visor driver
  *
index 269e727..7e63074 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * USB ConnectTech WhiteHEAT driver
  *
index 16ce060..a6505ce 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for USB Mass Storage compliant devices
  * Debugging Functions Header File
index 2dbf9c7..dcd7b7e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Header file for Special Initializers for certain USB Mass Storage devices
  *
index 072f1ff..1d10246 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for USB Mass Storage compliant devices
  * Protocol Functions Header File
index 2bc5ea0..2a79c3e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for USB Mass Storage compliant devices
  * SCSI Connecting Glue Header File
index e605cbc..b9f78ef 100644 (file)
@@ -129,15 +129,11 @@ int sierra_ms_init(struct us_data *us)
        int result, retries;
        struct swoc_info *swocInfo;
        struct usb_device *udev;
-       struct Scsi_Host *sh;
 
        retries = 3;
        result = 0;
        udev = us->pusb_dev;
 
-       sh = us_to_host(us);
-       scsi_get_host_dev(sh);
-
        /* Force Modem mode */
        if (swi_tru_install == TRU_FORCE_MODEM) {
                usb_stor_dbg(us, "SWIMS: Forcing Modem Mode\n");
index fb3bb4e..74ffd0d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for USB Mass Storage compliant devices
  * Transport Functions Header File
index 0ec8c99..13f61ec 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for the Alauda-based card readers
  */
index fb99e52..0547daf 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for devices based on the Cypress USB/ATA bridge
  *     with support for ATACB
index fdab5e7..5335b5d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for the Datafab USB Compact Flash reader
  */
index f6c3681..b6a9a74 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for USB Mass Storage compliant devices
  * Unusual Devices File
index 9134b91..a3b32ab 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 #if defined(CONFIG_USB_STORAGE_ENE_UB6250) || \
                defined(CONFIG_USB_STORAGE_ENE_UB6250_MODULE)
 
index 949231c..9ca6863 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for the Freecom USB/IDE adaptor
  */
index d03a02c..f248190 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for In-System Design, Inc. ISD200 ASIC
  */
index c323338..44878f8 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for the Lexar "Jumpshot" Compact Flash reader
  */
index 8f1eebd..9fbed4c 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for the Rio Karma
  */
index c76d4e9..cdfee8f 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for the Maxtor OneTouch USB hard drive's button
  */
index 7e14c2d..945dcb1 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for Realtek RTS51xx USB card reader
  *
index 650cf28..bfb6509 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for SanDisk SDDR-09 SmartMedia reader
  */
index e89df2c..6d6f76e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for SanDisk SDDR-55 SmartMedia reader
  */
index 37157ed..162b09d 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for USB Attached SCSI devices - Unusual Devices File
  *
index 05abf68..f9d3e5e 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Unusual Devices File for SCM Microsystems (a.k.a. Shuttle) USB-ATAPI cable
  */
index 5850d62..0451fac 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Driver for USB Mass Storage compliant devices
  * Main Header File
index b4f2aac..559dd06 100644 (file)
@@ -64,7 +64,8 @@ config TYPEC_HD3SS3220
 config TYPEC_TPS6598X
        tristate "TI TPS6598x USB Power Delivery controller driver"
        depends on I2C
-       select REGMAP_I2C
+       depends on REGMAP_I2C
+       depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH
        help
          Say Y or M here if your system has TI TPS65982 or TPS65983 USB Power
          Delivery controller.
index 8d894bd..c923474 100644 (file)
@@ -917,6 +917,12 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable);
 /* ------------------------------------------------------------------------- */
 /* USB Type-C ports */
 
+static const char * const typec_orientations[] = {
+       [TYPEC_ORIENTATION_NONE]        = "unknown",
+       [TYPEC_ORIENTATION_NORMAL]      = "normal",
+       [TYPEC_ORIENTATION_REVERSE]     = "reverse",
+};
+
 static const char * const typec_roles[] = {
        [TYPEC_SINK]    = "sink",
        [TYPEC_SOURCE]  = "source",
@@ -1248,18 +1254,9 @@ static ssize_t orientation_show(struct device *dev,
                                   struct device_attribute *attr,
                                   char *buf)
 {
-       struct typec_port *p = to_typec_port(dev);
-       enum typec_orientation orientation = typec_get_orientation(p);
-
-       switch (orientation) {
-       case TYPEC_ORIENTATION_NORMAL:
-               return sprintf(buf, "%s\n", "normal");
-       case TYPEC_ORIENTATION_REVERSE:
-               return sprintf(buf, "%s\n", "reverse");
-       case TYPEC_ORIENTATION_NONE:
-       default:
-               return sprintf(buf, "%s\n", "unknown");
-       }
+       struct typec_port *port = to_typec_port(dev);
+
+       return sprintf(buf, "%s\n", typec_orientations[port->orientation]);
 }
 static DEVICE_ATTR_RO(orientation);
 
@@ -1452,6 +1449,21 @@ void typec_set_pwr_opmode(struct typec_port *port,
 EXPORT_SYMBOL_GPL(typec_set_pwr_opmode);
 
 /**
+ * typec_find_orientation - Convert orientation string to enum typec_orientation
+ * @name: Orientation string
+ *
+ * This routine is used to find the typec_orientation by its string name @name.
+ *
+ * Returns the orientation value on success, otherwise negative error code.
+ */
+int typec_find_orientation(const char *name)
+{
+       return match_string(typec_orientations, ARRAY_SIZE(typec_orientations),
+                           name);
+}
+EXPORT_SYMBOL_GPL(typec_find_orientation);
+
+/**
  * typec_find_port_power_role - Get the typec port power capability
  * @name: port power capability string
  *
index 1ac0a3e..962bc69 100644 (file)
@@ -92,6 +92,9 @@ struct pmc_usb_port {
 
        u8 usb2_port;
        u8 usb3_port;
+
+       enum typec_orientation sbu_orientation;
+       enum typec_orientation hsl_orientation;
 };
 
 struct pmc_usb {
@@ -101,6 +104,22 @@ struct pmc_usb {
        struct pmc_usb_port *port;
 };
 
+static int sbu_orientation(struct pmc_usb_port *port)
+{
+       if (port->sbu_orientation)
+               return port->sbu_orientation - 1;
+
+       return port->orientation - 1;
+}
+
+static int hsl_orientation(struct pmc_usb_port *port)
+{
+       if (port->hsl_orientation)
+               return port->hsl_orientation - 1;
+
+       return port->orientation - 1;
+}
+
 static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
 {
        u8 response[4];
@@ -152,8 +171,9 @@ pmc_usb_mux_dp(struct pmc_usb_port *port, struct typec_mux_state *state)
 
        req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT;
        req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT;
-       req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT;
-       req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT;
+
+       req.mode_data |= sbu_orientation(port) << PMC_USB_ALTMODE_ORI_AUX_SHIFT;
+       req.mode_data |= hsl_orientation(port) << PMC_USB_ALTMODE_ORI_HSL_SHIFT;
 
        req.mode_data |= (state->mode - TYPEC_STATE_MODAL) <<
                         PMC_USB_ALTMODE_DP_MODE_SHIFT;
@@ -177,8 +197,9 @@ pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state)
 
        req.mode_data = (port->orientation - 1) << PMC_USB_ALTMODE_ORI_SHIFT;
        req.mode_data |= (port->role - 1) << PMC_USB_ALTMODE_UFP_SHIFT;
-       req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_AUX_SHIFT;
-       req.mode_data |= (port->orientation - 1) << PMC_USB_ALTMODE_ORI_HSL_SHIFT;
+
+       req.mode_data |= sbu_orientation(port) << PMC_USB_ALTMODE_ORI_AUX_SHIFT;
+       req.mode_data |= hsl_orientation(port) << PMC_USB_ALTMODE_ORI_HSL_SHIFT;
 
        if (TBT_ADAPTER(data->device_mode) == TBT_ADAPTER_TBT3)
                req.mode_data |= PMC_USB_ALTMODE_TBT_TYPE;
@@ -215,8 +236,8 @@ static int pmc_usb_connect(struct pmc_usb_port *port)
        msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
 
        msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT;
-       msg[1] |= (port->orientation - 1) << PMC_USB_MSG_ORI_HSL_SHIFT;
-       msg[1] |= (port->orientation - 1) << PMC_USB_MSG_ORI_AUX_SHIFT;
+       msg[1] |= hsl_orientation(port) << PMC_USB_MSG_ORI_HSL_SHIFT;
+       msg[1] |= sbu_orientation(port) << PMC_USB_MSG_ORI_AUX_SHIFT;
 
        return pmc_usb_command(port, msg, sizeof(msg));
 }
@@ -300,6 +321,7 @@ static int pmc_usb_register_port(struct pmc_usb *pmc, int index,
        struct usb_role_switch_desc desc = { };
        struct typec_switch_desc sw_desc = { };
        struct typec_mux_desc mux_desc = { };
+       const char *str;
        int ret;
 
        ret = fwnode_property_read_u8(fwnode, "usb2-port-number", &port->usb2_port);
@@ -310,6 +332,14 @@ static int pmc_usb_register_port(struct pmc_usb *pmc, int index,
        if (ret)
                return ret;
 
+       ret = fwnode_property_read_string(fwnode, "sbu-orientation", &str);
+       if (!ret)
+               port->sbu_orientation = typec_find_orientation(str);
+
+       ret = fwnode_property_read_string(fwnode, "hsl-orientation", &str);
+       if (!ret)
+               port->hsl_orientation = typec_find_orientation(str);
+
        port->num = index;
        port->pmc = pmc;
 
index b498960..b28face 100644 (file)
@@ -9,14 +9,13 @@
 #include <linux/delay.h>
 #include <linux/errno.h>
 #include <linux/extcon.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/i2c.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of_device.h>
-#include <linux/of_gpio.h>
 #include <linux/pinctrl/consumer.h>
 #include <linux/proc_fs.h>
 #include <linux/regulator/consumer.h>
@@ -83,7 +82,7 @@ struct fusb302_chip {
        struct work_struct irq_work;
        bool irq_suspended;
        bool irq_while_suspended;
-       int gpio_int_n;
+       struct gpio_desc *gpio_int_n;
        int gpio_int_n_irq;
        struct extcon_dev *extcon;
 
@@ -1618,30 +1617,17 @@ done:
 
 static int init_gpio(struct fusb302_chip *chip)
 {
-       struct device_node *node;
+       struct device *dev = chip->dev;
        int ret = 0;
 
-       node = chip->dev->of_node;
-       chip->gpio_int_n = of_get_named_gpio(node, "fcs,int_n", 0);
-       if (!gpio_is_valid(chip->gpio_int_n)) {
-               ret = chip->gpio_int_n;
-               dev_err(chip->dev, "cannot get named GPIO Int_N, ret=%d", ret);
-               return ret;
-       }
-       ret = devm_gpio_request(chip->dev, chip->gpio_int_n, "fcs,int_n");
-       if (ret < 0) {
-               dev_err(chip->dev, "cannot request GPIO Int_N, ret=%d", ret);
-               return ret;
-       }
-       ret = gpio_direction_input(chip->gpio_int_n);
-       if (ret < 0) {
-               dev_err(chip->dev,
-                       "cannot set GPIO Int_N to input, ret=%d", ret);
-               return ret;
+       chip->gpio_int_n = devm_gpiod_get(dev, "fcs,int_n", GPIOD_IN);
+       if (IS_ERR(chip->gpio_int_n)) {
+               dev_err(dev, "failed to request gpio_int_n\n");
+               return PTR_ERR(chip->gpio_int_n);
        }
-       ret = gpio_to_irq(chip->gpio_int_n);
+       ret = gpiod_to_irq(chip->gpio_int_n);
        if (ret < 0) {
-               dev_err(chip->dev,
+               dev_err(dev,
                        "cannot request IRQ for GPIO Int_N, ret=%d", ret);
                return ret;
        }
index 00b39d3..edc0e4b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Copyright 2016-2017 Google, Inc
  *
index 0698add..b7c9fe5 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/regmap.h>
 #include <linux/interrupt.h>
 #include <linux/usb/typec.h>
+#include <linux/usb/role.h>
 
 /* Register offsets */
 #define TPS_REG_VID                    0x00
@@ -94,6 +95,7 @@ struct tps6598x {
        struct typec_port *port;
        struct typec_partner *partner;
        struct usb_pd_identity partner_identity;
+       struct usb_role_switch *role_sw;
 };
 
 /*
@@ -190,6 +192,23 @@ static int tps6598x_read_partner_identity(struct tps6598x *tps)
        return 0;
 }
 
+static void tps6598x_set_data_role(struct tps6598x *tps,
+                                  enum typec_data_role role, bool connected)
+{
+       enum usb_role role_val;
+
+       if (role == TYPEC_HOST)
+               role_val = USB_ROLE_HOST;
+       else
+               role_val = USB_ROLE_DEVICE;
+
+       if (!connected)
+               role_val = USB_ROLE_NONE;
+
+       usb_role_switch_set_role(tps->role_sw, role_val);
+       typec_set_data_role(tps->port, role);
+}
+
 static int tps6598x_connect(struct tps6598x *tps, u32 status)
 {
        struct typec_partner_desc desc;
@@ -220,7 +239,7 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status)
        typec_set_pwr_opmode(tps->port, mode);
        typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status));
        typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status));
-       typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status));
+       tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), true);
 
        tps->partner = typec_register_partner(tps->port, &desc);
        if (IS_ERR(tps->partner))
@@ -240,7 +259,7 @@ static void tps6598x_disconnect(struct tps6598x *tps, u32 status)
        typec_set_pwr_opmode(tps->port, TYPEC_PWR_MODE_USB);
        typec_set_pwr_role(tps->port, TPS_STATUS_PORTROLE(status));
        typec_set_vconn_role(tps->port, TPS_STATUS_VCONN(status));
-       typec_set_data_role(tps->port, TPS_STATUS_DATAROLE(status));
+       tps6598x_set_data_role(tps, TPS_STATUS_DATAROLE(status), false);
 }
 
 static int tps6598x_exec_cmd(struct tps6598x *tps, const char *cmd,
@@ -328,7 +347,7 @@ static int tps6598x_dr_set(struct typec_port *port, enum typec_data_role role)
                goto out_unlock;
        }
 
-       typec_set_data_role(tps->port, role);
+       tps6598x_set_data_role(tps, role, true);
 
 out_unlock:
        mutex_unlock(&tps->lock);
@@ -452,6 +471,7 @@ static int tps6598x_probe(struct i2c_client *client)
 {
        struct typec_capability typec_cap = { };
        struct tps6598x *tps;
+       struct fwnode_handle *fwnode;
        u32 status;
        u32 conf;
        u32 vid;
@@ -495,11 +515,22 @@ static int tps6598x_probe(struct i2c_client *client)
        if (ret < 0)
                return ret;
 
+       fwnode = device_get_named_child_node(&client->dev, "connector");
+       if (IS_ERR(fwnode))
+               return PTR_ERR(fwnode);
+
+       tps->role_sw = fwnode_usb_role_switch_get(fwnode);
+       if (IS_ERR(tps->role_sw)) {
+               ret = PTR_ERR(tps->role_sw);
+               goto err_fwnode_put;
+       }
+
        typec_cap.revision = USB_TYPEC_REV_1_2;
        typec_cap.pd_revision = 0x200;
        typec_cap.prefer_role = TYPEC_NO_PREFERRED_ROLE;
        typec_cap.driver_data = tps;
        typec_cap.ops = &tps6598x_ops;
+       typec_cap.fwnode = fwnode;
 
        switch (TPS_SYSCONF_PORTINFO(conf)) {
        case TPS_PORTINFO_SINK_ACCESSORY:
@@ -525,12 +556,16 @@ static int tps6598x_probe(struct i2c_client *client)
                typec_cap.data = TYPEC_PORT_DFP;
                break;
        default:
-               return -ENODEV;
+               ret = -ENODEV;
+               goto err_role_put;
        }
 
        tps->port = typec_register_port(&client->dev, &typec_cap);
-       if (IS_ERR(tps->port))
-               return PTR_ERR(tps->port);
+       if (IS_ERR(tps->port)) {
+               ret = PTR_ERR(tps->port);
+               goto err_role_put;
+       }
+       fwnode_handle_put(fwnode);
 
        if (status & TPS_STATUS_PLUG_PRESENT) {
                ret = tps6598x_connect(tps, status);
@@ -545,12 +580,19 @@ static int tps6598x_probe(struct i2c_client *client)
        if (ret) {
                tps6598x_disconnect(tps, 0);
                typec_unregister_port(tps->port);
-               return ret;
+               goto err_role_put;
        }
 
        i2c_set_clientdata(client, tps);
 
        return 0;
+
+err_role_put:
+       usb_role_switch_put(tps->role_sw);
+err_fwnode_put:
+       fwnode_handle_put(fwnode);
+
+       return ret;
 }
 
 static int tps6598x_remove(struct i2c_client *client)
@@ -559,10 +601,17 @@ static int tps6598x_remove(struct i2c_client *client)
 
        tps6598x_disconnect(tps, 0);
        typec_unregister_port(tps->port);
+       usb_role_switch_put(tps->role_sw);
 
        return 0;
 }
 
+static const struct of_device_id tps6598x_of_match[] = {
+       { .compatible = "ti,tps6598x", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, tps6598x_of_match);
+
 static const struct i2c_device_id tps6598x_id[] = {
        { "tps6598x" },
        { }
@@ -572,6 +621,7 @@ MODULE_DEVICE_TABLE(i2c, tps6598x_id);
 static struct i2c_driver tps6598x_i2c_driver = {
        .driver = {
                .name = "tps6598x",
+               .of_match_table = tps6598x_of_match,
        },
        .probe_new = tps6598x_probe,
        .remove = tps6598x_remove,
index b35e15a..8a8eb5c 100644 (file)
@@ -7,6 +7,10 @@ typec_ucsi-y                           := ucsi.o
 
 typec_ucsi-$(CONFIG_TRACING)           += trace.o
 
+ifneq ($(CONFIG_POWER_SUPPLY),)
+       typec_ucsi-y                    += psy.o
+endif
+
 ifneq ($(CONFIG_TYPEC_DP_ALTMODE),)
        typec_ucsi-y                    += displayport.o
 endif
diff --git a/drivers/usb/typec/ucsi/psy.c b/drivers/usb/typec/ucsi/psy.c
new file mode 100644 (file)
index 0000000..26ed0b5
--- /dev/null
@@ -0,0 +1,241 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Power Supply for UCSI
+ *
+ * Copyright (C) 2020, Intel Corporation
+ * Author: K V, Abhilash <abhilash.k.v@intel.com>
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/property.h>
+#include <linux/usb/pd.h>
+
+#include "ucsi.h"
+
+/* Power Supply access to expose source power information */
+enum ucsi_psy_online_states {
+       UCSI_PSY_OFFLINE = 0,
+       UCSI_PSY_FIXED_ONLINE,
+       UCSI_PSY_PROG_ONLINE,
+};
+
+static enum power_supply_property ucsi_psy_props[] = {
+       POWER_SUPPLY_PROP_USB_TYPE,
+       POWER_SUPPLY_PROP_ONLINE,
+       POWER_SUPPLY_PROP_VOLTAGE_MIN,
+       POWER_SUPPLY_PROP_VOLTAGE_MAX,
+       POWER_SUPPLY_PROP_VOLTAGE_NOW,
+       POWER_SUPPLY_PROP_CURRENT_MAX,
+       POWER_SUPPLY_PROP_CURRENT_NOW,
+};
+
+static int ucsi_psy_get_online(struct ucsi_connector *con,
+                              union power_supply_propval *val)
+{
+       val->intval = UCSI_PSY_OFFLINE;
+       if (con->status.flags & UCSI_CONSTAT_CONNECTED &&
+           (con->status.flags & UCSI_CONSTAT_PWR_DIR) == TYPEC_SINK)
+               val->intval = UCSI_PSY_FIXED_ONLINE;
+       return 0;
+}
+
+static int ucsi_psy_get_voltage_min(struct ucsi_connector *con,
+                                   union power_supply_propval *val)
+{
+       u32 pdo;
+
+       switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+       case UCSI_CONSTAT_PWR_OPMODE_PD:
+               pdo = con->src_pdos[0];
+               val->intval = pdo_fixed_voltage(pdo) * 1000;
+               break;
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+       case UCSI_CONSTAT_PWR_OPMODE_BC:
+       case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+               val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+               break;
+       default:
+               val->intval = 0;
+               break;
+       }
+       return 0;
+}
+
+static int ucsi_psy_get_voltage_max(struct ucsi_connector *con,
+                                   union power_supply_propval *val)
+{
+       u32 pdo;
+
+       switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+       case UCSI_CONSTAT_PWR_OPMODE_PD:
+               if (con->num_pdos > 0) {
+                       pdo = con->src_pdos[con->num_pdos - 1];
+                       val->intval = pdo_fixed_voltage(pdo) * 1000;
+               } else {
+                       val->intval = 0;
+               }
+               break;
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+       case UCSI_CONSTAT_PWR_OPMODE_BC:
+       case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+               val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+               break;
+       default:
+               val->intval = 0;
+               break;
+       }
+       return 0;
+}
+
+static int ucsi_psy_get_voltage_now(struct ucsi_connector *con,
+                                   union power_supply_propval *val)
+{
+       int index;
+       u32 pdo;
+
+       switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+       case UCSI_CONSTAT_PWR_OPMODE_PD:
+               index = rdo_index(con->rdo);
+               if (index > 0) {
+                       pdo = con->src_pdos[index - 1];
+                       val->intval = pdo_fixed_voltage(pdo) * 1000;
+               } else {
+                       val->intval = 0;
+               }
+               break;
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+       case UCSI_CONSTAT_PWR_OPMODE_BC:
+       case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+               val->intval = UCSI_TYPEC_VSAFE5V * 1000;
+               break;
+       default:
+               val->intval = 0;
+               break;
+       }
+       return 0;
+}
+
+static int ucsi_psy_get_current_max(struct ucsi_connector *con,
+                                   union power_supply_propval *val)
+{
+       u32 pdo;
+
+       switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
+       case UCSI_CONSTAT_PWR_OPMODE_PD:
+               if (con->num_pdos > 0) {
+                       pdo = con->src_pdos[con->num_pdos - 1];
+                       val->intval = pdo_max_current(pdo) * 1000;
+               } else {
+                       val->intval = 0;
+               }
+               break;
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+               val->intval = UCSI_TYPEC_1_5_CURRENT * 1000;
+               break;
+       case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+               val->intval = UCSI_TYPEC_3_0_CURRENT * 1000;
+               break;
+       case UCSI_CONSTAT_PWR_OPMODE_BC:
+       case UCSI_CONSTAT_PWR_OPMODE_DEFAULT:
+       /* UCSI can't tell b/w DCP/CDP or USB2/3x1/3x2 SDP chargers */
+       default:
+               val->intval = 0;
+               break;
+       }
+       return 0;
+}
+
+static int ucsi_psy_get_current_now(struct ucsi_connector *con,
+                                   union power_supply_propval *val)
+{
+       u16 flags = con->status.flags;
+
+       if (UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
+               val->intval = rdo_op_current(con->rdo) * 1000;
+       else
+               val->intval = 0;
+       return 0;
+}
+
+static int ucsi_psy_get_usb_type(struct ucsi_connector *con,
+                                union power_supply_propval *val)
+{
+       u16 flags = con->status.flags;
+
+       val->intval = POWER_SUPPLY_USB_TYPE_C;
+       if (flags & UCSI_CONSTAT_CONNECTED &&
+           UCSI_CONSTAT_PWR_OPMODE(flags) == UCSI_CONSTAT_PWR_OPMODE_PD)
+               val->intval = POWER_SUPPLY_USB_TYPE_PD;
+
+       return 0;
+}
+
+static int ucsi_psy_get_prop(struct power_supply *psy,
+                            enum power_supply_property psp,
+                            union power_supply_propval *val)
+{
+       struct ucsi_connector *con = power_supply_get_drvdata(psy);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_USB_TYPE:
+               return ucsi_psy_get_usb_type(con, val);
+       case POWER_SUPPLY_PROP_ONLINE:
+               return ucsi_psy_get_online(con, val);
+       case POWER_SUPPLY_PROP_VOLTAGE_MIN:
+               return ucsi_psy_get_voltage_min(con, val);
+       case POWER_SUPPLY_PROP_VOLTAGE_MAX:
+               return ucsi_psy_get_voltage_max(con, val);
+       case POWER_SUPPLY_PROP_VOLTAGE_NOW:
+               return ucsi_psy_get_voltage_now(con, val);
+       case POWER_SUPPLY_PROP_CURRENT_MAX:
+               return ucsi_psy_get_current_max(con, val);
+       case POWER_SUPPLY_PROP_CURRENT_NOW:
+               return ucsi_psy_get_current_now(con, val);
+       default:
+               return -EINVAL;
+       }
+}
+
+static enum power_supply_usb_type ucsi_psy_usb_types[] = {
+       POWER_SUPPLY_USB_TYPE_C,
+       POWER_SUPPLY_USB_TYPE_PD,
+       POWER_SUPPLY_USB_TYPE_PD_PPS,
+};
+
+int ucsi_register_port_psy(struct ucsi_connector *con)
+{
+       struct power_supply_config psy_cfg = {};
+       struct device *dev = con->ucsi->dev;
+       char *psy_name;
+
+       psy_cfg.drv_data = con;
+       psy_cfg.fwnode = dev_fwnode(dev);
+
+       psy_name = devm_kasprintf(dev, GFP_KERNEL, "ucsi-source-psy-%s%d",
+                                 dev_name(dev), con->num);
+       if (!psy_name)
+               return -ENOMEM;
+
+       con->psy_desc.name = psy_name;
+       con->psy_desc.type = POWER_SUPPLY_TYPE_USB,
+       con->psy_desc.usb_types = ucsi_psy_usb_types;
+       con->psy_desc.num_usb_types = ARRAY_SIZE(ucsi_psy_usb_types);
+       con->psy_desc.properties = ucsi_psy_props,
+       con->psy_desc.num_properties = ARRAY_SIZE(ucsi_psy_props),
+       con->psy_desc.get_property = ucsi_psy_get_prop;
+
+       con->psy = power_supply_register(dev, &con->psy_desc, &psy_cfg);
+
+       return PTR_ERR_OR_ZERO(con->psy);
+}
+
+void ucsi_unregister_port_psy(struct ucsi_connector *con)
+{
+       if (IS_ERR_OR_NULL(con->psy))
+               return;
+
+       power_supply_unregister(con->psy);
+}
index 48ad1dc..cb62ad8 100644 (file)
@@ -35,16 +35,16 @@ const char *ucsi_cmd_str(u64 raw_cmd)
 
 const char *ucsi_cci_str(u32 cci)
 {
-       if (cci & GENMASK(7, 0)) {
-               if (cci & BIT(29))
+       if (UCSI_CCI_CONNECTOR(cci)) {
+               if (cci & UCSI_CCI_ACK_COMPLETE)
                        return "Event pending (ACK completed)";
-               if (cci & BIT(31))
+               if (cci & UCSI_CCI_COMMAND_COMPLETE)
                        return "Event pending (command completed)";
                return "Connector Change";
        }
-       if (cci & BIT(29))
+       if (cci & UCSI_CCI_ACK_COMPLETE)
                return "ACK completed";
-       if (cci & BIT(31))
+       if (cci & UCSI_CCI_COMMAND_COMPLETE)
                return "Command completed";
 
        return "";
index ddf2ad3..d0c63af 100644 (file)
@@ -492,19 +492,45 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
        }
 }
 
+static void ucsi_get_pdos(struct ucsi_connector *con, int is_partner)
+{
+       struct ucsi *ucsi = con->ucsi;
+       u64 command;
+       int ret;
+
+       command = UCSI_COMMAND(UCSI_GET_PDOS) | UCSI_CONNECTOR_NUMBER(con->num);
+       command |= UCSI_GET_PDOS_PARTNER_PDO(is_partner);
+       command |= UCSI_GET_PDOS_NUM_PDOS(UCSI_MAX_PDOS - 1);
+       command |= UCSI_GET_PDOS_SRC_PDOS;
+       ret = ucsi_run_command(ucsi, command, con->src_pdos,
+                              sizeof(con->src_pdos));
+       if (ret < 0) {
+               dev_err(ucsi->dev, "UCSI_GET_PDOS failed (%d)\n", ret);
+               return;
+       }
+       con->num_pdos = ret / sizeof(u32); /* number of bytes to 32-bit PDOs */
+       if (ret == 0)
+               dev_warn(ucsi->dev, "UCSI_GET_PDOS returned 0 bytes\n");
+}
+
 static void ucsi_pwr_opmode_change(struct ucsi_connector *con)
 {
        switch (UCSI_CONSTAT_PWR_OPMODE(con->status.flags)) {
        case UCSI_CONSTAT_PWR_OPMODE_PD:
+               con->rdo = con->status.request_data_obj;
                typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_PD);
+               ucsi_get_pdos(con, 1);
                break;
        case UCSI_CONSTAT_PWR_OPMODE_TYPEC1_5:
+               con->rdo = 0;
                typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_1_5A);
                break;
        case UCSI_CONSTAT_PWR_OPMODE_TYPEC3_0:
+               con->rdo = 0;
                typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_3_0A);
                break;
        default:
+               con->rdo = 0;
                typec_set_pwr_opmode(con->port, TYPEC_PWR_MODE_USB);
                break;
        }
@@ -566,6 +592,8 @@ static void ucsi_partner_change(struct ucsi_connector *con)
 
        switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
        case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+       case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+       case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
                typec_set_data_role(con->port, TYPEC_HOST);
                break;
        case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -611,7 +639,8 @@ static void ucsi_handle_connector_change(struct work_struct *work)
 
        role = !!(con->status.flags & UCSI_CONSTAT_PWR_DIR);
 
-       if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE)
+       if (con->status.change & UCSI_CONSTAT_POWER_OPMODE_CHANGE ||
+           con->status.change & UCSI_CONSTAT_POWER_LEVEL_CHANGE)
                ucsi_pwr_opmode_change(con);
 
        if (con->status.change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
@@ -627,6 +656,8 @@ static void ucsi_handle_connector_change(struct work_struct *work)
 
                switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
                case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+               case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+               case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
                        typec_set_data_role(con->port, TYPEC_HOST);
                        break;
                case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -905,6 +936,10 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
        cap->driver_data = con;
        cap->ops = &ucsi_ops;
 
+       ret = ucsi_register_port_psy(con);
+       if (ret)
+               return ret;
+
        /* Register the connector */
        con->port = typec_register_port(ucsi->dev, cap);
        if (IS_ERR(con->port))
@@ -927,6 +962,8 @@ static int ucsi_register_port(struct ucsi *ucsi, int index)
 
        switch (UCSI_CONSTAT_PARTNER_TYPE(con->status.flags)) {
        case UCSI_CONSTAT_PARTNER_TYPE_UFP:
+       case UCSI_CONSTAT_PARTNER_TYPE_CABLE:
+       case UCSI_CONSTAT_PARTNER_TYPE_CABLE_AND_UFP:
                typec_set_data_role(con->port, TYPEC_HOST);
                break;
        case UCSI_CONSTAT_PARTNER_TYPE_DFP:
@@ -1029,6 +1066,7 @@ err_unregister:
        for (con = ucsi->connector; con->port; con++) {
                ucsi_unregister_partner(con);
                ucsi_unregister_altmodes(con, UCSI_RECIPIENT_CON);
+               ucsi_unregister_port_psy(con);
                typec_unregister_port(con->port);
                con->port = NULL;
        }
@@ -1152,6 +1190,7 @@ void ucsi_unregister(struct ucsi *ucsi)
                ucsi_unregister_partner(&ucsi->connector[i]);
                ucsi_unregister_altmodes(&ucsi->connector[i],
                                         UCSI_RECIPIENT_CON);
+               ucsi_unregister_port_psy(&ucsi->connector[i]);
                typec_unregister_port(ucsi->connector[i].port);
        }
 
index 8e83110..cba6f77 100644 (file)
@@ -5,6 +5,7 @@
 
 #include <linux/bitops.h>
 #include <linux/device.h>
+#include <linux/power_supply.h>
 #include <linux/types.h>
 #include <linux/usb/typec.h>
 
@@ -21,7 +22,7 @@ struct ucsi_altmode;
 #define UCSI_MESSAGE_OUT               32
 
 /* Command Status and Connector Change Indication (CCI) bits */
-#define UCSI_CCI_CONNECTOR(_c_)                (((_c_) & GENMASK(7, 0)) >> 1)
+#define UCSI_CCI_CONNECTOR(_c_)                (((_c_) & GENMASK(7, 1)) >> 1)
 #define UCSI_CCI_LENGTH(_c_)           (((_c_) & GENMASK(15, 8)) >> 8)
 #define UCSI_CCI_NOT_SUPPORTED         BIT(25)
 #define UCSI_CCI_CANCEL_COMPLETE       BIT(26)
@@ -130,6 +131,11 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
 #define UCSI_GET_ALTMODE_OFFSET(_r_)           ((u64)(_r_) << 32)
 #define UCSI_GET_ALTMODE_NUM_ALTMODES(_r_)     ((u64)(_r_) << 40)
 
+/* GET_PDOS command bits */
+#define UCSI_GET_PDOS_PARTNER_PDO(_r_)         ((u64)(_r_) << 23)
+#define UCSI_GET_PDOS_NUM_PDOS(_r_)            ((u64)(_r_) << 32)
+#define UCSI_GET_PDOS_SRC_PDOS                 ((u64)1 << 34)
+
 /* -------------------------------------------------------------------------- */
 
 /* Error information returned by PPM in response to GET_ERROR_STATUS command. */
@@ -294,6 +300,11 @@ struct ucsi {
 
 #define UCSI_MAX_SVID          5
 #define UCSI_MAX_ALTMODES      (UCSI_MAX_SVID * 6)
+#define UCSI_MAX_PDOS          (4)
+
+#define UCSI_TYPEC_VSAFE5V     5000
+#define UCSI_TYPEC_1_5_CURRENT 1500
+#define UCSI_TYPEC_3_0_CURRENT 3000
 
 struct ucsi_connector {
        int num;
@@ -313,6 +324,11 @@ struct ucsi_connector {
 
        struct ucsi_connector_status status;
        struct ucsi_connector_capability cap;
+       struct power_supply *psy;
+       struct power_supply_desc psy_desc;
+       u32 rdo;
+       u32 src_pdos[UCSI_MAX_PDOS];
+       int num_pdos;
 };
 
 int ucsi_send_command(struct ucsi *ucsi, u64 command,
@@ -321,6 +337,14 @@ int ucsi_send_command(struct ucsi *ucsi, u64 command,
 void ucsi_altmode_update_active(struct ucsi_connector *con);
 int ucsi_resume(struct ucsi *ucsi);
 
+#if IS_ENABLED(CONFIG_POWER_SUPPLY)
+int ucsi_register_port_psy(struct ucsi_connector *con);
+void ucsi_unregister_port_psy(struct ucsi_connector *con);
+#else
+static inline int ucsi_register_port_psy(struct ucsi_connector *con) { return 0; }
+static inline void ucsi_unregister_port_psy(struct ucsi_connector *con) { }
+#endif /* CONFIG_POWER_SUPPLY */
+
 #if IS_ENABLED(CONFIG_TYPEC_DP_ALTMODE)
 struct typec_altmode *
 ucsi_register_displayport(struct ucsi_connector *con,
index 1f3f866..3727ef7 100644 (file)
@@ -17,5 +17,6 @@
 #define PHY_TYPE_USB3          4
 #define PHY_TYPE_UFS           5
 #define PHY_TYPE_DP            6
+#define PHY_TYPE_XPCS          7
 
 #endif /* _DT_BINDINGS_PHY */
index 5973a63..e23b52d 100644 (file)
@@ -2,68 +2,14 @@
 /*
  * omap_usb.h -- omap usb2 phy header file
  *
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * Copyright (C) 2012-2020 Texas Instruments Incorporated - http://www.ti.com
  * Author: Kishon Vijay Abraham I <kishon@ti.com>
  */
 
 #ifndef __DRIVERS_OMAP_USB2_H
 #define __DRIVERS_OMAP_USB2_H
 
-#include <linux/io.h>
-#include <linux/usb/otg.h>
-
-struct usb_dpll_params {
-       u16     m;
-       u8      n;
-       u8      freq:3;
-       u8      sd;
-       u32     mf;
-};
-
-enum omap_usb_phy_type {
-       TYPE_USB2,    /* USB2_PHY, power down in CONTROL_DEV_CONF */
-       TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
-       TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
-};
-
-struct omap_usb {
-       struct usb_phy          phy;
-       struct phy_companion    *comparator;
-       void __iomem            *pll_ctrl_base;
-       void __iomem            *phy_base;
-       struct device           *dev;
-       struct device           *control_dev;
-       struct clk              *wkupclk;
-       struct clk              *optclk;
-       u8                      flags;
-       enum omap_usb_phy_type  type;
-       struct regmap           *syscon_phy_power; /* ctrl. reg. acces */
-       unsigned int            power_reg; /* power reg. index within syscon */
-       u32                     mask;
-       u32                     power_on;
-       u32                     power_off;
-};
-
-struct usb_phy_data {
-       const char *label;
-       u8 flags;
-       u32 mask;
-       u32 power_on;
-       u32 power_off;
-};
-
-/* Driver Flags */
-#define OMAP_USB2_HAS_START_SRP (1 << 0)
-#define OMAP_USB2_HAS_SET_VBUS (1 << 1)
-#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT (1 << 2)
-
-#define OMAP_DEV_PHY_PD                BIT(0)
-#define OMAP_USB2_PHY_PD       BIT(28)
-
-#define AM437X_USB2_PHY_PD             BIT(0)
-#define AM437X_USB2_OTG_PD             BIT(1)
-#define AM437X_USB2_OTGVDET_EN         BIT(19)
-#define AM437X_USB2_OTGSESSEND_EN      BIT(20)
+#include <linux/usb/phy_companion.h>
 
 #define        phy_to_omapusb(x)       container_of((x), struct omap_usb, phy)
 
@@ -76,15 +22,4 @@ static inline int omap_usb2_set_comparator(struct phy_companion *comparator)
 }
 #endif
 
-static inline u32 omap_usb_readl(void __iomem *addr, unsigned offset)
-{
-       return __raw_readl(addr + offset);
-}
-
-static inline void omap_usb_writel(void __iomem *addr, unsigned offset,
-       u32 data)
-{
-       __raw_writel(data, addr + offset);
-}
-
 #endif /* __DRIVERS_OMAP_USB_H */
index ece782e..ff397c0 100644 (file)
@@ -80,7 +80,7 @@ struct tb {
        int index;
        enum tb_security_level security_level;
        size_t nboot_acl;
-       unsigned long privdata[0];
+       unsigned long privdata[];
 };
 
 extern struct bus_type tb_bus_type;
index edd89b7..54167a2 100644 (file)
@@ -67,6 +67,7 @@ struct ci_hdrc_platform_data {
 #define CI_HDRC_CONTROLLER_STOPPED_EVENT       1
 #define CI_HDRC_IMX_HSIC_ACTIVE_EVENT          2
 #define CI_HDRC_IMX_HSIC_SUSPEND_EVENT         3
+#define CI_HDRC_CONTROLLER_VBUS_EVENT          4
        int     (*notify_event) (struct ci_hdrc *ci, unsigned event);
        struct regulator        *reg_vbus;
        struct usb_otg_caps     ci_otg_caps;
index 8675e14..2040696 100644 (file)
@@ -249,6 +249,9 @@ int usb_function_activate(struct usb_function *);
 
 int usb_interface_id(struct usb_configuration *, struct usb_function *);
 
+int config_ep_by_speed_and_alt(struct usb_gadget *g, struct usb_function *f,
+                               struct usb_ep *_ep, u8 alt);
+
 int config_ep_by_speed(struct usb_gadget *g, struct usb_function *f,
                        struct usb_ep *_ep);
 
index 9411c08..6a17817 100644 (file)
@@ -42,6 +42,8 @@ struct usb_ep;
  * @num_mapped_sgs: number of SG entries mapped to DMA (internal)
  * @length: Length of that data
  * @stream_id: The stream id, when USB3.0 bulk streams are being used
+ * @is_last: Indicates if this is the last request of a stream_id before
+ *     switching to a different stream (required for DWC3 controllers).
  * @no_interrupt: If true, hints that no completion irq is needed.
  *     Helpful sometimes with deep request queues that are handled
  *     directly by DMA controllers.
@@ -104,6 +106,7 @@ struct usb_request {
        unsigned                num_mapped_sgs;
 
        unsigned                stream_id:16;
+       unsigned                is_last:1;
        unsigned                no_interrupt:1;
        unsigned                zero:1;
        unsigned                short_not_ok:1;
@@ -373,6 +376,7 @@ struct usb_gadget_ops {
  * @connected: True if gadget is connected.
  * @lpm_capable: If the gadget max_speed is FULL or HIGH, this flag
  *     indicates that it supports LPM as per the LPM ECN & errata.
+ * @irq: the interrupt number for device controller.
  *
  * Gadgets have a mostly-portable "gadget driver" implementing device
  * functions, handling all usb configurations and interfaces.  Gadget
@@ -427,6 +431,7 @@ struct usb_gadget {
        unsigned                        deactivated:1;
        unsigned                        connected:1;
        unsigned                        lpm_capable:1;
+       int                             irq;
 };
 #define work_to_gadget(w)      (container_of((w), struct usb_gadget, work))
 
@@ -773,6 +778,9 @@ struct usb_gadget_string_container {
 /* put descriptor for string with that id into buf (buflen >= 256) */
 int usb_gadget_get_string(const struct usb_gadget_strings *table, int id, u8 *buf);
 
+/* check if the given language identifier is valid */
+bool usb_validate_langid(u16 langid);
+
 /*-------------------------------------------------------------------------*/
 
 /* utility to simplify managing config descriptors */
index e12105e..3dbb42c 100644 (file)
@@ -479,7 +479,8 @@ extern void usb_hcd_platform_shutdown(struct platform_device *dev);
 struct pci_dev;
 struct pci_device_id;
 extern int usb_hcd_pci_probe(struct pci_dev *dev,
-                               const struct pci_device_id *id);
+                            const struct pci_device_id *id,
+                            const struct hc_driver *driver);
 extern void usb_hcd_pci_remove(struct pci_dev *dev);
 extern void usb_hcd_pci_shutdown(struct pci_dev *dev);
 
index b00a264..5daa1c4 100644 (file)
@@ -254,6 +254,7 @@ int typec_set_mode(struct typec_port *port, int mode);
 
 void *typec_get_drvdata(struct typec_port *port);
 
+int typec_find_orientation(const char *name);
 int typec_find_port_power_role(const char *name);
 int typec_find_power_role(const char *name);
 int typec_find_port_data_role(const char *name);