Merge tag 'usb-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 30 Mar 2020 20:54:11 +0000 (13:54 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 30 Mar 2020 20:54:11 +0000 (13:54 -0700)
Pull USB / PHY updates from Greg KH:
 "Here are the big set of USB and PHY driver patches for 5.7-rc1.

  Nothing huge here, some new PHY drivers, loads of USB gadget fixes and
  updates, xhci updates, usb-serial driver updates and new device ids,
  and other minor things. Full details in the shortlog.

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

* tag 'usb-5.7-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (239 commits)
  USB: cdc-acm: restore capability check order
  usb: cdns3: make signed 1 bit bitfields unsigned
  usb: gadget: fsl: remove unused variable 'driver_desc'
  usb: gadget: f_fs: Fix use after free issue as part of queue failure
  usb: typec: Correct the documentation for typec_cable_put()
  USB: serial: io_edgeport: fix slab-out-of-bounds read in edge_interrupt_callback
  USB: serial: option: add Wistron Neweb D19Q1
  USB: serial: option: add BroadMobi BM806U
  USB: serial: option: add support for ASKEY WWHC050
  usb: core: Add ACPI support for USB interface devices
  driver core: platform: Reimplement devm_platform_ioremap_resource
  usb: dwc2: convert to devm_platform_get_and_ioremap_resource
  usb: host: hisilicon: convert to devm_platform_get_and_ioremap_resource
  usb: host: xhci-plat: convert to devm_platform_get_and_ioremap_resource
  drivers: provide devm_platform_get_and_ioremap_resource()
  phy: qcom-qusb2: Add new overriding tuning parameters in QUSB2 V2 PHY
  phy: qcom-qusb2: Add support for overriding tuning parameters in QUSB2 V2 PHY
  dt-bindings: phy: qcom-qusb2: Add support for overriding Phy tuning parameters
  phy: qcom-qusb2: Add generic QUSB2 V2 PHY support
  dt-bindings: phy: qcom,qusb2: Add compatibles for QUSB2 V2 phy and SC7180
  ...

212 files changed:
Documentation/ABI/testing/sysfs-class-typec
Documentation/devicetree/bindings/phy/amlogic,meson-g12a-usb2-phy.yaml
Documentation/devicetree/bindings/phy/phy-cadence-dp.txt [deleted file]
Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-mtk-tphy.txt
Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom,usb-ss.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt [deleted file]
Documentation/devicetree/bindings/phy/qcom-qmp-phy.txt
Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt [deleted file]
Documentation/devicetree/bindings/phy/ti-phy-gmii-sel.txt
Documentation/devicetree/bindings/phy/uniphier-pcie-phy.txt
Documentation/devicetree/bindings/phy/uniphier-usb3-hsphy.txt
Documentation/devicetree/bindings/phy/uniphier-usb3-ssphy.txt
Documentation/devicetree/bindings/usb/amlogic,meson-g12a-usb-ctrl.yaml
Documentation/devicetree/bindings/usb/aspeed,usb-vhub.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/dwc2.yaml
Documentation/devicetree/bindings/usb/dwc3.txt
Documentation/devicetree/bindings/usb/generic.txt
Documentation/devicetree/bindings/usb/ingenic,jz4740-musb.txt [deleted file]
Documentation/devicetree/bindings/usb/ingenic,jz4770-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/ingenic,musb.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/usb/maxim,max3420-udc.yaml [new file with mode: 0644]
Documentation/driver-api/usb/typec_bus.rst
Documentation/usb/index.rst
Documentation/usb/raw-gadget.rst [new file with mode: 0644]
MAINTAINERS
arch/arm/boot/dts/aspeed-g4.dtsi
arch/arm/boot/dts/aspeed-g5.dtsi
arch/arm/boot/dts/aspeed-g6-pinctrl.dtsi
arch/arm/boot/dts/aspeed-g6.dtsi
drivers/base/platform.c
drivers/phy/amlogic/phy-meson-g12a-usb2.c
drivers/phy/cadence/Kconfig
drivers/phy/cadence/Makefile
drivers/phy/cadence/phy-cadence-dp.c [deleted file]
drivers/phy/cadence/phy-cadence-torrent.c [new file with mode: 0644]
drivers/phy/mediatek/phy-mtk-tphy.c
drivers/phy/qualcomm/Kconfig
drivers/phy/qualcomm/Makefile
drivers/phy/qualcomm/phy-qcom-qmp.c
drivers/phy/qualcomm/phy-qcom-qmp.h
drivers/phy/qualcomm/phy-qcom-qusb2.c
drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c [new file with mode: 0644]
drivers/phy/qualcomm/phy-qcom-usb-ss.c [new file with mode: 0644]
drivers/phy/rockchip/phy-rockchip-inno-usb2.c
drivers/phy/socionext/phy-uniphier-pcie.c
drivers/phy/socionext/phy-uniphier-usb3hs.c
drivers/phy/socionext/phy-uniphier-usb3ss.c
drivers/phy/tegra/Kconfig
drivers/phy/tegra/Makefile
drivers/phy/tegra/xusb-tegra124.c
drivers/phy/tegra/xusb-tegra186.c
drivers/phy/tegra/xusb-tegra210.c
drivers/phy/tegra/xusb.c
drivers/phy/tegra/xusb.h
drivers/phy/ti/phy-gmii-sel.c
drivers/thunderbolt/domain.c
drivers/thunderbolt/eeprom.c
drivers/thunderbolt/icm.c
drivers/thunderbolt/usb4.c
drivers/usb/atm/ueagle-atm.c
drivers/usb/atm/usbatm.h
drivers/usb/c67x00/c67x00-hcd.h
drivers/usb/c67x00/c67x00.h
drivers/usb/cdns3/cdns3-pci-wrap.c
drivers/usb/cdns3/cdns3-ti.c
drivers/usb/cdns3/core.c
drivers/usb/cdns3/gadget.c
drivers/usb/cdns3/gadget.h
drivers/usb/chipidea/bits.h
drivers/usb/chipidea/ci.h
drivers/usb/chipidea/ci_hdrc_imx.h
drivers/usb/chipidea/core.c
drivers/usb/chipidea/otg.c
drivers/usb/chipidea/otg.h
drivers/usb/chipidea/otg_fsm.h
drivers/usb/chipidea/udc.c
drivers/usb/chipidea/udc.h
drivers/usb/class/cdc-acm.c
drivers/usb/core/driver.c
drivers/usb/core/generic.c
drivers/usb/core/message.c
drivers/usb/core/sysfs.c
drivers/usb/core/usb-acpi.c
drivers/usb/core/usb.h
drivers/usb/dwc2/core.h
drivers/usb/dwc2/gadget.c
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/drd.c
drivers/usb/dwc3/dwc3-exynos.c
drivers/usb/dwc3/dwc3-meson-g12a.c
drivers/usb/dwc3/dwc3-qcom.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/host.c
drivers/usb/dwc3/trace.h
drivers/usb/gadget/composite.c
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/function/f_phonet.c
drivers/usb/gadget/function/f_uac1_legacy.c
drivers/usb/gadget/legacy/Kconfig
drivers/usb/gadget/legacy/Makefile
drivers/usb/gadget/legacy/gmidi.c
drivers/usb/gadget/legacy/inode.c
drivers/usb/gadget/legacy/raw_gadget.c [new file with mode: 0644]
drivers/usb/gadget/udc/Kconfig
drivers/usb/gadget/udc/Makefile
drivers/usb/gadget/udc/amd5536udc.h
drivers/usb/gadget/udc/amd5536udc_pci.c
drivers/usb/gadget/udc/aspeed-vhub/Kconfig
drivers/usb/gadget/udc/aspeed-vhub/core.c
drivers/usb/gadget/udc/aspeed-vhub/dev.c
drivers/usb/gadget/udc/aspeed-vhub/epn.c
drivers/usb/gadget/udc/aspeed-vhub/hub.c
drivers/usb/gadget/udc/aspeed-vhub/vhub.h
drivers/usb/gadget/udc/at91_udc.c
drivers/usb/gadget/udc/dummy_hcd.c
drivers/usb/gadget/udc/fotg210-udc.c
drivers/usb/gadget/udc/fsl_udc_core.c
drivers/usb/gadget/udc/fusb300_udc.c
drivers/usb/gadget/udc/goku_udc.c
drivers/usb/gadget/udc/lpc32xx_udc.c
drivers/usb/gadget/udc/m66592-udc.c
drivers/usb/gadget/udc/max3420_udc.c [new file with mode: 0644]
drivers/usb/gadget/udc/net2280.c
drivers/usb/gadget/udc/omap_udc.c
drivers/usb/gadget/udc/r8a66597-udc.c
drivers/usb/gadget/udc/renesas_usb3.c
drivers/usb/gadget/udc/s3c-hsudc.c
drivers/usb/gadget/udc/tegra-xudc.c
drivers/usb/host/ehci-mv.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-hcd.c
drivers/usb/host/fotg210.h
drivers/usb/host/ohci-pci.c
drivers/usb/host/ohci.h
drivers/usb/host/sl811-hcd.c
drivers/usb/host/uhci-pci.c
drivers/usb/host/xhci-histb.c
drivers/usb/host/xhci-hub.c
drivers/usb/host/xhci-mem.c
drivers/usb/host/xhci-mtk.h
drivers/usb/host/xhci-pci.c
drivers/usb/host/xhci-plat.c
drivers/usb/host/xhci-ring.c
drivers/usb/host/xhci-tegra.c
drivers/usb/host/xhci.c
drivers/usb/host/xhci.h
drivers/usb/misc/Kconfig
drivers/usb/misc/Makefile
drivers/usb/misc/apple-mfi-fastcharge.c [new file with mode: 0644]
drivers/usb/mon/mon_text.c
drivers/usb/mtu3/mtu3_dr.c
drivers/usb/musb/Kconfig
drivers/usb/musb/jz4740.c
drivers/usb/musb/mediatek.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_host.c
drivers/usb/musb/tusb6010.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
drivers/usb/phy/phy-jz4770.c [new file with mode: 0644]
drivers/usb/phy/phy-tegra-usb.c
drivers/usb/roles/class.c
drivers/usb/roles/intel-xhci-usb-role-switch.c
drivers/usb/serial/digi_acceleport.c
drivers/usb/serial/f81232.c
drivers/usb/serial/generic.c
drivers/usb/serial/io_edgeport.c
drivers/usb/serial/io_usbvend.h
drivers/usb/serial/option.c
drivers/usb/serial/ti_usb_3410_5052.c
drivers/usb/serial/usb-serial.c
drivers/usb/storage/usb.c
drivers/usb/storage/usb.h
drivers/usb/storage/usual-tables.c
drivers/usb/typec/bus.c
drivers/usb/typec/bus.h
drivers/usb/typec/class.c
drivers/usb/typec/mux.c
drivers/usb/typec/mux/Kconfig
drivers/usb/typec/mux/Makefile
drivers/usb/typec/mux/intel_pmc_mux.c [new file with mode: 0644]
drivers/usb/typec/tcpm/tcpm.c
drivers/usb/typec/ucsi/ucsi.c
drivers/usb/typec/ucsi/ucsi.h
drivers/usb/typec/ucsi/ucsi_ccg.c
include/linux/phy/tegra/xusb.h
include/linux/platform_device.h
include/linux/usb.h
include/linux/usb/audio-v2.h
include/linux/usb/audio-v3.h
include/linux/usb/ehci_def.h
include/linux/usb/gadget.h
include/linux/usb/hcd.h
include/linux/usb/role.h
include/linux/usb/typec.h
include/linux/usb/typec_altmode.h
include/linux/usb/typec_mux.h
include/linux/usb/typec_tbt.h [new file with mode: 0644]
include/linux/usb_usual.h
include/linux/usbdevice_fs.h
include/uapi/linux/usb/raw_gadget.h [new file with mode: 0644]

index d7647b258c3ca84255b3a95ceebb1c486dabf9f3..b834671522d6f98c76a66981c2e9ef36693ad82b 100644 (file)
@@ -20,13 +20,13 @@ Date:               April 2017
 Contact:       Heikki Krogerus <heikki.krogerus@linux.intel.com>
 Description:
                The supported power roles. This attribute can be used to request
-               power role swap on the port when the port supports USB Power
-               Delivery. Swapping is supported as synchronous operation, so
-               write(2) to the attribute will not return until the operation
-               has finished. The attribute is notified about role changes so
-               that poll(2) on the attribute wakes up. Change on the role will
-               also generate uevent KOBJ_CHANGE. The current role is show in
-               brackets, for example "[source] sink" when in source mode.
+               power role swap on the port. Swapping is supported as
+               synchronous operation, so write(2) to the attribute will not
+               return until the operation has finished. The attribute is
+               notified about role changes so that poll(2) on the attribute
+               wakes up. Change on the role will also generate uevent
+               KOBJ_CHANGE. The current role is show in brackets, for example
+               "[source] sink" when in source mode.
 
                Valid values: source, sink
 
@@ -108,6 +108,15 @@ Contact:   Heikki Krogerus <heikki.krogerus@linux.intel.com>
 Description:
                Revision number of the supported USB Type-C specification.
 
+What:          /sys/class/typec/<port>/orientation
+Date:          February 2020
+Contact:       Badhri Jagan Sridharan <badhri@google.com>
+Description:
+               Indicates the active orientation of the Type-C connector.
+               Valid values:
+               - "normal": CC1 orientation
+               - "reverse": CC2 orientation
+               - "unknown": Orientation cannot be determined.
 
 USB Type-C partner devices (eg. /sys/class/typec/port0-partner/)
 
index 57d8603076bdbf733f6b7d5ed7e87e8d98ca115e..9e32cb43fb219c37be730f7e2154b11d150d494d 100644 (file)
@@ -14,6 +14,7 @@ properties:
   compatible:
     enum:
       - amlogic,meson-g12a-usb2-phy
+      - amlogic,meson-a1-usb2-phy
 
   reg:
     maxItems: 1
@@ -49,6 +50,19 @@ required:
   - reset-names
   - "#phy-cells"
 
+if:
+  properties:
+    compatible:
+      enum:
+        - amlogic,meson-a1-usb-ctrl
+
+then:
+  properties:
+    power-domains:
+      maxItems: 1
+  required:
+    - power-domains
+
 examples:
   - |
     phy@36000 {
diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-dp.txt b/Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
deleted file mode 100644 (file)
index 7f49fd5..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-Cadence MHDP DisplayPort SD0801 PHY binding
-===========================================
-
-This binding describes the Cadence SD0801 PHY hardware included with
-the Cadence MHDP DisplayPort controller.
-
--------------------------------------------------------------------------------
-Required properties (controller (parent) node):
-- compatible   : Should be "cdns,dp-phy"
-- reg          : Defines the following sets of registers in the parent
-                 mhdp device:
-                       - Offset of the DPTX PHY configuration registers
-                       - Offset of the SD0801 PHY configuration registers
-- #phy-cells   : from the generic PHY bindings, must be 0.
-
-Optional properties:
-- num_lanes    : Number of DisplayPort lanes to use (1, 2 or 4)
-- max_bit_rate : Maximum DisplayPort link bit rate to use, in Mbps (2160,
-                 2430, 2700, 3240, 4320, 5400 or 8100)
--------------------------------------------------------------------------------
-
-Example:
-       dp_phy: phy@f0fb030a00 {
-               compatible = "cdns,dp-phy";
-               reg = <0xf0 0xfb030a00 0x0 0x00000040>,
-                     <0xf0 0xfb500000 0x0 0x00100000>;
-               num_lanes = <4>;
-               max_bit_rate = <8100>;
-               #phy-cells = <0>;
-       };
diff --git a/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml b/Documentation/devicetree/bindings/phy/phy-cadence-torrent.yaml
new file mode 100644 (file)
index 0000000..c779a3c
--- /dev/null
@@ -0,0 +1,143 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/phy-cadence-torrent.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Cadence Torrent SD0801 PHY binding for DisplayPort
+
+description:
+  This binding describes the Cadence SD0801 PHY (also known as Torrent PHY)
+  hardware included with the Cadence MHDP DisplayPort controller.
+
+maintainers:
+  - Swapnil Jakhade <sjakhade@cadence.com>
+  - Yuti Amonkar <yamonkar@cadence.com>
+
+properties:
+  compatible:
+    enum:
+      - cdns,torrent-phy
+      - ti,j721e-serdes-10g
+
+  '#address-cells':
+    const: 1
+
+  '#size-cells':
+    const: 0
+
+  clocks:
+    maxItems: 1
+    description:
+      PHY reference clock. Must contain an entry in clock-names.
+
+  clock-names:
+    const: refclk
+
+  reg:
+    minItems: 1
+    maxItems: 2
+    items:
+      - description: Offset of the Torrent PHY configuration registers.
+      - description: Offset of the DPTX PHY configuration registers.
+
+  reg-names:
+    minItems: 1
+    maxItems: 2
+    items:
+      - const: torrent_phy
+      - const: dptx_phy
+
+  resets:
+    maxItems: 1
+    description:
+      Torrent PHY reset.
+      See Documentation/devicetree/bindings/reset/reset.txt
+
+patternProperties:
+  '^phy@[0-7]+$':
+    type: object
+    description:
+      Each group of PHY lanes with a single master lane should be represented as a sub-node.
+    properties:
+      reg:
+        description:
+          The master lane number. This is the lowest numbered lane in the lane group.
+
+      resets:
+        minItems: 1
+        maxItems: 4
+        description:
+          Contains list of resets, one per lane, to get all the link lanes out of reset.
+
+      "#phy-cells":
+        const: 0
+
+      cdns,phy-type:
+        description:
+          Specifies the type of PHY for which the group of PHY lanes is used.
+          Refer include/dt-bindings/phy/phy.h. Constants from the header should be used.
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [1, 2, 3, 4, 5, 6]
+
+      cdns,num-lanes:
+        description:
+          Number of DisplayPort lanes.
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [1, 2, 4]
+        default: 4
+
+      cdns,max-bit-rate:
+        description:
+          Maximum DisplayPort link bit rate to use, in Mbps
+        allOf:
+          - $ref: /schemas/types.yaml#/definitions/uint32
+          - enum: [2160, 2430, 2700, 3240, 4320, 5400, 8100]
+        default: 8100
+
+    required:
+      - reg
+      - resets
+      - "#phy-cells"
+      - cdns,phy-type
+
+    additionalProperties: false
+
+required:
+  - compatible
+  - "#address-cells"
+  - "#size-cells"
+  - clocks
+  - clock-names
+  - reg
+  - reg-names
+  - resets
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/phy/phy.h>
+    torrent_phy: torrent-phy@f0fb500000 {
+          compatible = "cdns,torrent-phy";
+          reg = <0xf0 0xfb500000 0x0 0x00100000>,
+                <0xf0 0xfb030a00 0x0 0x00000040>;
+          reg-names = "torrent_phy", "dptx_phy";
+          resets = <&phyrst 0>;
+          clocks = <&ref_clk>;
+          clock-names = "refclk";
+          #address-cells = <1>;
+          #size-cells = <0>;
+          torrent_phy_dp: phy@0 {
+                    reg = <0>;
+                    resets = <&phyrst 1>, <&phyrst 2>,
+                             <&phyrst 3>, <&phyrst 4>;
+                    #phy-cells = <0>;
+                    cdns,phy-type = <PHY_TYPE_DP>;
+                    cdns,num-lanes = <4>;
+                    cdns,max-bit-rate = <8100>;
+          };
+    };
+...
index a5f7a4f0dbc1a8609270b32b761a447183222a57..dd75b676b71d05c2e8d840954d038902a66ac72a 100644 (file)
@@ -13,10 +13,16 @@ Required properties (controller (parent) node):
                  "mediatek,mt8173-u3phy";
                  make use of "mediatek,generic-tphy-v1" on mt2701 instead and
                  "mediatek,generic-tphy-v2" on mt2712 instead.
- - clocks      : (deprecated, use port's clocks instead) a list of phandle +
-                 clock-specifier pairs, one for each entry in clock-names
- - clock-names : (deprecated, use port's one instead) must contain
-                 "u3phya_ref": for reference clock of usb3.0 analog phy.
+
+- #address-cells:      the number of cells used to represent physical
+               base addresses.
+- #size-cells: the number of cells used to represent the size of an address.
+- ranges:      the address mapping relationship to the parent, defined with
+               - empty value: if optional 'reg' is used.
+               - non-empty value: if optional 'reg' is not used. should set
+                       the child's base address to 0, the physical address
+                       within parent's address space, and the length of
+                       the address map.
 
 Required nodes : a sub-node is required for each port the controller
                  provides. Address range information including the usual
@@ -34,12 +40,6 @@ Optional properties (controller (parent) node):
 
 Required properties (port (child) node):
 - reg          : address and length of the register set for the port.
-- clocks       : a list of phandle + clock-specifier pairs, one for each
-                 entry in clock-names
-- clock-names  : must contain
-                 "ref": 48M reference clock for HighSpeed analog phy; and 26M
-                       reference clock for SuperSpeed analog phy, sometimes is
-                       24M, 25M or 27M, depended on platform.
 - #phy-cells   : should be 1 (See second example)
                  cell after port phandle is phy type from:
                        - PHY_TYPE_USB2
@@ -48,10 +48,22 @@ Required properties (port (child) node):
                        - PHY_TYPE_SATA
 
 Optional properties (PHY_TYPE_USB2 port (child) node):
+- clocks       : a list of phandle + clock-specifier pairs, one for each
+                 entry in clock-names
+- clock-names  : may contain
+                 "ref": 48M reference clock for HighSpeed (digital) phy; and 26M
+                       reference clock for SuperSpeed (digital) phy, sometimes is
+                       24M, 25M or 27M, depended on platform.
+                 "da_ref": the reference clock of analog phy, used if the clocks
+                       of analog and digital phys are separated, otherwise uses
+                       "ref" clock only if needed.
+
 - mediatek,eye-src     : u32, the value of slew rate calibrate
 - mediatek,eye-vrt     : u32, the selection of VRT reference voltage
 - mediatek,eye-term    : u32, the selection of HS_TX TERM reference voltage
 - mediatek,bc12        : bool, enable BC12 of u2phy if support it
+- mediatek,discth      : u32, the selection of disconnect threshold
+- mediatek,intr        : u32, the selection of internal R (resistance)
 
 Example:
 
diff --git a/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml b/Documentation/devicetree/bindings/phy/qcom,qusb2-phy.yaml
new file mode 100644 (file)
index 0000000..144ae29
--- /dev/null
@@ -0,0 +1,185 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/qcom,qusb2-phy.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcomm QUSB2 phy controller
+
+maintainers:
+  - Manu Gautam <mgautam@codeaurora.org>
+
+description:
+  QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
+
+properties:
+  compatible:
+    oneOf:
+      - items:
+        - enum:
+          - qcom,msm8996-qusb2-phy
+          - qcom,msm8998-qusb2-phy
+      - items:
+        - enum:
+          - qcom,sc7180-qusb2-phy
+          - qcom,sdm845-qusb2-phy
+        - const: qcom,qusb2-v2-phy
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+  clocks:
+    minItems: 2
+    maxItems: 3
+    items:
+      - description: phy config clock
+      - description: 19.2 MHz ref clk
+      - description: phy interface clock (Optional)
+
+  clock-names:
+    minItems: 2
+    maxItems: 3
+    items:
+      - const: cfg_ahb
+      - const: ref
+      - const: iface
+
+  vdda-pll-supply:
+     description:
+       Phandle to 1.8V regulator supply to PHY refclk pll block.
+
+  vdda-phy-dpdm-supply:
+     description:
+       Phandle to 3.1V regulator supply to Dp/Dm port signals.
+
+  resets:
+    maxItems: 1
+    description:
+      Phandle to reset to phy block.
+
+  nvmem-cells:
+    maxItems: 1
+    description:
+        Phandle to nvmem cell that contains 'HS Tx trim'
+        tuning parameter value for qusb2 phy.
+
+  qcom,tcsr-syscon:
+    description:
+        Phandle to TCSR syscon register region.
+    $ref: /schemas/types.yaml#/definitions/phandle
+
+if:
+  properties:
+    compatible:
+      contains:
+        const: qcom,qusb2-v2-phy
+then:
+  properties:
+    qcom,imp-res-offset-value:
+      description:
+        It is a 6 bit value that specifies offset to be
+        added to PHY refgen RESCODE via IMP_CTRL1 register. It is a PHY
+        tuning parameter that may vary for different boards of same SOC.
+      allOf:
+        - $ref: /schemas/types.yaml#/definitions/uint32
+        - minimum: 0
+          maximum: 63
+          default: 0
+
+    qcom,bias-ctrl-value:
+      description:
+        It is a 6 bit value that specifies bias-ctrl-value. It is a PHY
+        tuning parameter that may vary for different boards of same SOC.
+      allOf:
+        - $ref: /schemas/types.yaml#/definitions/uint32
+        - minimum: 0
+          maximum: 63
+          default: 0
+
+    qcom,charge-ctrl-value:
+     description:
+        It is a 2 bit value that specifies charge-ctrl-value. It is a PHY
+        tuning parameter that may vary for different boards of same SOC.
+     allOf:
+       - $ref: /schemas/types.yaml#/definitions/uint32
+       - minimum: 0
+         maximum: 3
+         default: 0
+
+    qcom,hstx-trim-value:
+      description:
+        It is a 4 bit value that specifies tuning for HSTX
+        output current.
+        Possible range is - 15mA to 24mA (stepsize of 600 uA).
+        See dt-bindings/phy/phy-qcom-qusb2.h for applicable values.
+      allOf:
+        - $ref: /schemas/types.yaml#/definitions/uint32
+        - minimum: 0
+          maximum: 15
+          default: 3
+
+    qcom,preemphasis-level:
+      description:
+        It is a 2 bit value that specifies pre-emphasis level.
+        Possible range is 0 to 15% (stepsize of 5%).
+        See dt-bindings/phy/phy-qcom-qusb2.h for applicable values.
+      allOf:
+        - $ref: /schemas/types.yaml#/definitions/uint32
+        - minimum: 0
+          maximum: 3
+          default: 2
+
+    qcom,preemphasis-width:
+      description:
+        It is a 1 bit value that specifies how long the HSTX
+        pre-emphasis (specified using qcom,preemphasis-level) must be in
+        effect. Duration could be half-bit of full-bit.
+        See dt-bindings/phy/phy-qcom-qusb2.h for applicable values.
+      allOf:
+        - $ref: /schemas/types.yaml#/definitions/uint32
+        - minimum: 0
+          maximum: 1
+          default: 0
+
+    qcom,hsdisc-trim-value:
+      description:
+        It is a 2 bit value tuning parameter that control disconnect
+        threshold and may vary for different boards of same SOC.
+      allOf:
+        - $ref: /schemas/types.yaml#/definitions/uint32
+        - minimum: 0
+          maximum: 3
+          default: 0
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+  - clocks
+  - clock-names
+  - vdda-pll-supply
+  - vdda-phy-dpdm-supply
+  - resets
+
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-msm8996.h>
+    hsusb_phy: phy@7411000 {
+        compatible = "qcom,msm8996-qusb2-phy";
+        reg = <0x7411000 0x180>;
+        #phy-cells = <0>;
+
+        clocks = <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
+                 <&gcc GCC_RX1_USB2_CLKREF_CLK>;
+        clock-names = "cfg_ahb", "ref";
+
+        vdda-pll-supply = <&pm8994_l12>;
+        vdda-phy-dpdm-supply = <&pm8994_l24>;
+
+        resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>;
+        nvmem-cells = <&qusb2p_hstx_trim>;
+    };
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-hs-28nm.yaml
new file mode 100644 (file)
index 0000000..ca6a083
--- /dev/null
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/qcom,usb-hs-28nm.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcomm Synopsys DesignWare Core 28nm High-Speed PHY
+
+maintainers:
+  - Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+
+description: |
+  Qualcomm Low-Speed, Full-Speed, Hi-Speed 28nm USB PHY
+
+properties:
+  compatible:
+    enum:
+      - qcom,usb-hs-28nm-femtophy
+
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+  clocks:
+    items:
+      - description: rpmcc ref clock
+      - description: PHY AHB clock
+      - description: Rentention clock
+
+  clock-names:
+    items:
+      - const: ref
+      - const: ahb
+      - const: sleep
+
+  resets:
+    items:
+      - description: PHY core reset
+      - description: POR reset
+
+  reset-names:
+    items:
+      - const: phy
+      - const: por
+
+  vdd-supply:
+    description: phandle to the regulator VDD supply node.
+
+  vdda1p8-supply:
+    description: phandle to the regulator 1.8V supply node.
+
+  vdda3p3-supply:
+    description: phandle to the regulator 3.3V supply node.
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+  - vdd-supply
+  - vdda1p8-supply
+  - vdda3p3-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-qcs404.h>
+    #include <dt-bindings/clock/qcom,rpmcc.h>
+    usb2_phy_prim: phy@7a000 {
+        compatible = "qcom,usb-hs-28nm-femtophy";
+        reg = <0x0007a000 0x200>;
+        #phy-cells = <0>;
+        clocks = <&rpmcc RPM_SMD_LN_BB_CLK>,
+                 <&gcc GCC_USB_HS_PHY_CFG_AHB_CLK>,
+                 <&gcc GCC_USB2A_PHY_SLEEP_CLK>;
+        clock-names = "ref", "ahb", "sleep";
+        resets = <&gcc GCC_USB_HS_PHY_CFG_AHB_BCR>,
+                 <&gcc GCC_USB2A_PHY_BCR>;
+        reset-names = "phy", "por";
+        vdd-supply = <&vreg_l4_1p2>;
+        vdda1p8-supply = <&vreg_l5_1p8>;
+        vdda3p3-supply = <&vreg_l12_3p3>;
+    };
+...
diff --git a/Documentation/devicetree/bindings/phy/qcom,usb-ss.yaml b/Documentation/devicetree/bindings/phy/qcom,usb-ss.yaml
new file mode 100644 (file)
index 0000000..bd1388d
--- /dev/null
@@ -0,0 +1,83 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/qcom,usb-ss.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: Qualcomm Synopsys 1.0.0 SuperSpeed USB PHY
+
+maintainers:
+  - Bryan O'Donoghue <bryan.odonoghue@linaro.org>
+
+description: |
+  Qualcomm Synopsys 1.0.0 SuperSpeed USB PHY
+
+properties:
+  compatible:
+    enum:
+      - qcom,usb-ss-28nm-phy
+
+  reg:
+    maxItems: 1
+
+  "#phy-cells":
+    const: 0
+
+  clocks:
+    items:
+      - description: rpmcc clock
+      - description: PHY AHB clock
+      - description: SuperSpeed pipe clock
+
+  clock-names:
+    items:
+      - const: ref
+      - const: ahb
+      - const: pipe
+
+  vdd-supply:
+    description: phandle to the regulator VDD supply node.
+
+  vdda1p8-supply:
+    description: phandle to the regulator 1.8V supply node.
+
+  resets:
+    items:
+      - description: COM reset
+      - description: PHY reset line
+
+  reset-names:
+    items:
+      - const: com
+      - const: phy
+
+required:
+  - compatible
+  - reg
+  - "#phy-cells"
+  - clocks
+  - clock-names
+  - vdd-supply
+  - vdda1p8-supply
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/qcom,gcc-qcs404.h>
+    #include <dt-bindings/clock/qcom,rpmcc.h>
+    usb3_phy: usb3-phy@78000 {
+        compatible = "qcom,usb-ss-28nm-phy";
+        reg = <0x78000 0x400>;
+        #phy-cells = <0>;
+        clocks = <&rpmcc RPM_SMD_LN_BB_CLK>,
+                 <&gcc GCC_USB_HS_PHY_CFG_AHB_CLK>,
+                 <&gcc GCC_USB3_PHY_PIPE_CLK>;
+        clock-names = "ref", "ahb", "pipe";
+        resets = <&gcc GCC_USB3_PHY_BCR>,
+                 <&gcc GCC_USB3PHY_PHY_BCR>;
+        reset-names = "com", "phy";
+        vdd-supply = <&vreg_l3_1p05>;
+        vdda1p8-supply = <&vreg_l5_1p8>;
+    };
+...
diff --git a/Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt b/Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt
deleted file mode 100644 (file)
index a1697c2..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-Qualcomm DWC3 HS AND SS PHY CONTROLLER
---------------------------------------
-
-DWC3 PHY nodes are defined to describe on-chip Synopsis Physical layer
-controllers.  Each DWC3 PHY controller should have its own node.
-
-Required properties:
-- compatible: should contain one of the following:
-       - "qcom,dwc3-hs-usb-phy" for High Speed Synopsis PHY controller
-       - "qcom,dwc3-ss-usb-phy" for Super Speed Synopsis PHY controller
-- reg: offset and length of the DWC3 PHY controller register set
-- #phy-cells: must be zero
-- clocks: a list of phandles and clock-specifier pairs, one for each entry in
-  clock-names.
-- clock-names: Should contain "ref" for the PHY reference clock
-
-Optional clocks:
-  "xo"         External reference clock
-
-Example:
-               phy@100f8800 {
-                       compatible = "qcom,dwc3-hs-usb-phy";
-                       reg = <0x100f8800 0x30>;
-                       clocks = <&gcc USB30_0_UTMI_CLK>;
-                       clock-names = "ref";
-                       #phy-cells = <0>;
-
-               };
-
-               phy@100f8830 {
-                       compatible = "qcom,dwc3-ss-usb-phy";
-                       reg = <0x100f8830 0x30>;
-                       clocks = <&gcc USB30_0_MASTER_CLK>;
-                       clock-names = "ref";
-                       #phy-cells = <0>;
-
-               };
index eac9ad3cbbc84766ea7cb2dff64e8f5dd83b52f4..54d6f8d4350827c87a168cce89dc63914182f3bc 100644 (file)
@@ -8,10 +8,13 @@ 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,
@@ -44,6 +47,8 @@ Required properties:
                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:
@@ -52,6 +57,10 @@ Required properties:
                        "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:
@@ -72,6 +81,8 @@ Required properties:
                        "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
@@ -80,6 +91,10 @@ Required properties:
                        "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:
diff --git a/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt b/Documentation/devicetree/bindings/phy/qcom-qusb2-phy.txt
deleted file mode 100644 (file)
index fe29f9e..0000000
+++ /dev/null
@@ -1,68 +0,0 @@
-Qualcomm QUSB2 phy controller
-=============================
-
-QUSB2 controller supports LS/FS/HS usb connectivity on Qualcomm chipsets.
-
-Required properties:
- - compatible: compatible list, contains
-              "qcom,msm8996-qusb2-phy" for 14nm PHY on msm8996,
-              "qcom,msm8998-qusb2-phy" for 10nm PHY on msm8998,
-              "qcom,sdm845-qusb2-phy" for 10nm PHY on sdm845.
-
- - reg: offset and length of the PHY register set.
- - #phy-cells: must be 0.
-
- - clocks: a list of phandles and clock-specifier pairs,
-          one for each entry in clock-names.
- - clock-names: must be "cfg_ahb" for phy config clock,
-                       "ref" for 19.2 MHz ref clk,
-                       "iface" for phy interface clock (Optional).
-
- - vdda-pll-supply: Phandle to 1.8V regulator supply to PHY refclk pll block.
- - vdda-phy-dpdm-supply: Phandle to 3.1V regulator supply to Dp/Dm port signals.
-
- - resets: Phandle to reset to phy block.
-
-Optional properties:
- - nvmem-cells: Phandle to nvmem cell that contains 'HS Tx trim'
-               tuning parameter value for qusb2 phy.
-
- - qcom,tcsr-syscon: Phandle to TCSR syscon register region.
- - qcom,imp-res-offset-value: It is a 6 bit value that specifies offset to be
-               added to PHY refgen RESCODE via IMP_CTRL1 register. It is a PHY
-               tuning parameter that may vary for different boards of same SOC.
-               This property is applicable to only QUSB2 v2 PHY (sdm845).
- - qcom,hstx-trim-value: It is a 4 bit value that specifies tuning for HSTX
-               output current.
-               Possible range is - 15mA to 24mA (stepsize of 600 uA).
-               See dt-bindings/phy/phy-qcom-qusb2.h for applicable values.
-               This property is applicable to only QUSB2 v2 PHY (sdm845).
-               Default value is 22.2mA for sdm845.
- - qcom,preemphasis-level: It is a 2 bit value that specifies pre-emphasis level.
-               Possible range is 0 to 15% (stepsize of 5%).
-               See dt-bindings/phy/phy-qcom-qusb2.h for applicable values.
-               This property is applicable to only QUSB2 v2 PHY (sdm845).
-               Default value is 10% for sdm845.
-- qcom,preemphasis-width: It is a 1 bit value that specifies how long the HSTX
-               pre-emphasis (specified using qcom,preemphasis-level) must be in
-               effect. Duration could be half-bit of full-bit.
-               See dt-bindings/phy/phy-qcom-qusb2.h for applicable values.
-               This property is applicable to only QUSB2 v2 PHY (sdm845).
-               Default value is full-bit width for sdm845.
-
-Example:
-       hsusb_phy: phy@7411000 {
-               compatible = "qcom,msm8996-qusb2-phy";
-               reg = <0x7411000 0x180>;
-               #phy-cells = <0>;
-
-               clocks = <&gcc GCC_USB_PHY_CFG_AHB2PHY_CLK>,
-                       <&gcc GCC_RX1_USB2_CLKREF_CLK>,
-               clock-names = "cfg_ahb", "ref";
-
-               vdda-pll-supply = <&pm8994_l12>;
-               vdda-phy-dpdm-supply = <&pm8994_l24>;
-
-               resets = <&gcc GCC_QUSB2PHY_PRIM_BCR>;
-               nvmem-cells = <&qusb2p_hstx_trim>;
-        };
index 50ce9ae0f7a5e3f66262268d2b6111e65c619573..83b78c1c0644fca9a6428417dbffe13f6c96cc92 100644 (file)
@@ -40,6 +40,7 @@ Required properties:
                          "ti,dra7xx-phy-gmii-sel" for dra7xx/am57xx platform
                          "ti,am43xx-phy-gmii-sel" for am43xx platform
                          "ti,dm814-phy-gmii-sel" for dm814x platform
+                         "ti,am654-phy-gmii-sel" for AM654x/J721E platform
 - reg                  : Address and length of the register set for the device
 - #phy-cells           : must be 2.
                          cell 1 - CPSW port number (starting from 1)
index 1889d3b89d68f1106e5a060556d913893286d9ea..3cee372c5742990abcc9290927c469663d78d751 100644 (file)
@@ -5,14 +5,19 @@ PCIe controller implemented on Socionext UniPhier SoCs.
 
 Required properties:
 - compatible: Should contain one of the following:
+    "socionext,uniphier-pro5-pcie-phy" - for Pro5 PHY
     "socionext,uniphier-ld20-pcie-phy" - for LD20 PHY
     "socionext,uniphier-pxs3-pcie-phy" - for PXs3 PHY
 - reg: Specifies offset and length of the register set for the device.
 - #phy-cells: Must be zero.
-- clocks: A phandle to the clock gate for PCIe glue layer including
-       this phy.
-- resets: A phandle to the reset line for PCIe glue layer including
-       this phy.
+- clocks: A list of phandles to the clock gate for PCIe glue layer
+       including this phy.
+- clock-names: For Pro5 only, should contain the following:
+    "gio", "link" - for Pro5 SoC
+- resets: A list of phandles to the reset line for PCIe glue layer
+       including this phy.
+- reset-names: For Pro5 only, should contain the following:
+    "gio", "link" - for Pro5 SoC
 
 Optional properties:
 - socionext,syscon: A phandle to system control to set configurations
index e8d8086a7ae95c57aec34082324b71c0558b9864..093d4f08705f7523fa40712fd97fe0f0961c30a9 100644 (file)
@@ -7,7 +7,7 @@ this describes about High-Speed PHY.
 
 Required properties:
 - compatible: Should contain one of the following:
-    "socionext,uniphier-pro4-usb3-hsphy" - for Pro4 SoC
+    "socionext,uniphier-pro5-usb3-hsphy" - for Pro5 SoC
     "socionext,uniphier-pxs2-usb3-hsphy" - for PXs2 SoC
     "socionext,uniphier-ld20-usb3-hsphy" - for LD20 SoC
     "socionext,uniphier-pxs3-usb3-hsphy" - for PXs3 SoC
@@ -16,13 +16,13 @@ Required properties:
 - clocks: A list of phandles to the clock gate for USB3 glue layer.
        According to the clock-names, appropriate clocks are required.
 - clock-names: Should contain the following:
-    "gio", "link" - for Pro4 SoC
+    "gio", "link" - for Pro5 SoC
     "phy", "phy-ext", "link" - for PXs3 SoC, "phy-ext" is optional.
     "phy", "link" - for others
 - resets: A list of phandles to the reset control for USB3 glue layer.
        According to the reset-names, appropriate resets are required.
 - reset-names: Should contain the following:
-    "gio", "link" - for Pro4 SoC
+    "gio", "link" - for Pro5 SoC
     "phy", "link" - for others
 
 Optional properties:
index 490b815445e810955ed5a3d039df03b663fdd432..9df2bc2f5999e3710d3fcde3316ceac6574594e6 100644 (file)
@@ -8,6 +8,7 @@ this describes about Super-Speed PHY.
 Required properties:
 - compatible: Should contain one of the following:
     "socionext,uniphier-pro4-usb3-ssphy" - for Pro4 SoC
+    "socionext,uniphier-pro5-usb3-ssphy" - for Pro5 SoC
     "socionext,uniphier-pxs2-usb3-ssphy" - for PXs2 SoC
     "socionext,uniphier-ld20-usb3-ssphy" - for LD20 SoC
     "socionext,uniphier-pxs3-usb3-ssphy" - for PXs3 SoC
@@ -16,13 +17,13 @@ Required properties:
 - clocks: A list of phandles to the clock gate for USB3 glue layer.
        According to the clock-names, appropriate clocks are required.
 - clock-names:
-    "gio", "link" - for Pro4 SoC
+    "gio", "link" - for Pro4 and Pro5 SoC
     "phy", "phy-ext", "link" - for PXs3 SoC, "phy-ext" is optional.
     "phy", "link" - for others
 - resets: A list of phandles to the reset control for USB3 glue layer.
        According to the reset-names, appropriate resets are required.
 - reset-names:
-    "gio", "link" - for Pro4 SoC
+    "gio", "link" - for Pro4 and Pro5 SoC
     "phy", "link" - for others
 
 Optional properties:
index 267fce1659943f78b71982a918f07b61c00a834f..b0e5e0fe938631f07ef3029940c50e36eb5493cc 100644 (file)
@@ -22,10 +22,14 @@ description: |
   The DWC3 Glue controls the PHY routing and power, an interrupt line is
   connected to the Glue to serve as OTG ID change detection.
 
+  The Amlogic A1 embeds a DWC3 USB IP Core configured for USB2 in
+  host-only mode.
+
 properties:
   compatible:
     enum:
       - amlogic,meson-g12a-usb-ctrl
+      - amlogic,meson-a1-usb-ctrl
 
   ranges: true
 
@@ -84,6 +88,25 @@ required:
   - phys
   - dr_mode
 
+allOf:
+  - if:
+      properties:
+        compatible:
+          enum:
+            - amlogic,meson-a1-usb-ctrl
+
+    then:
+      properties:
+        clocks:
+          minItems: 3
+        clock-names:
+          items:
+            - const: usb_ctrl
+            - const: usb_bus
+            - const: xtal_usb_ctrl
+      required:
+        - clock-names
+
 examples:
   - |
     usb: usb@ffe09000 {
diff --git a/Documentation/devicetree/bindings/usb/aspeed,usb-vhub.yaml b/Documentation/devicetree/bindings/usb/aspeed,usb-vhub.yaml
new file mode 100644 (file)
index 0000000..06399ba
--- /dev/null
@@ -0,0 +1,77 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (c) 2020 Facebook Inc.
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/aspeed,usb-vhub.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: ASPEED USB 2.0 Virtual Hub Controller
+
+maintainers:
+  - Benjamin Herrenschmidt <benh@kernel.crashing.org>
+
+description: |+
+  The ASPEED USB 2.0 Virtual Hub Controller implements 1 set of USB Hub
+  register and several sets of Device and Endpoint registers to support
+  the Virtual Hub's downstream USB devices.
+
+  Supported number of devices and endpoints vary depending on hardware
+  revisions. AST2400 and AST2500 Virtual Hub supports 5 downstream devices
+  and 15 generic endpoints, while AST2600 Virtual Hub supports 7 downstream
+  devices and 21 generic endpoints.
+
+properties:
+  compatible:
+    enum:
+      - aspeed,ast2400-usb-vhub
+      - aspeed,ast2500-usb-vhub
+      - aspeed,ast2600-usb-vhub
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  interrupts:
+    maxItems: 1
+
+  aspeed,vhub-downstream-ports:
+    description: Number of downstream ports supported by the Virtual Hub
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - default: 5
+        minimum: 1
+        maximum: 7
+
+  aspeed,vhub-generic-endpoints:
+    description: Number of generic endpoints supported by the Virtual Hub
+    allOf:
+      - $ref: /schemas/types.yaml#/definitions/uint32
+      - default: 15
+        minimum: 1
+        maximum: 21
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - interrupts
+  - aspeed,vhub-downstream-ports
+  - aspeed,vhub-generic-endpoints
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/aspeed-clock.h>
+    vhub: usb-vhub@1e6a0000 {
+            compatible = "aspeed,ast2500-usb-vhub";
+            reg = <0x1e6a0000 0x300>;
+            interrupts = <5>;
+            clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
+            aspeed,vhub-downstream-ports = <5>;
+            aspeed,vhub-generic-endpoints = <15>;
+            pinctrl-names = "default";
+            pinctrl-0 = <&pinctrl_usb2ad_default>;
+    };
index 71cf7ba32237613b8caf72cc83a51e8c34af0890..6baf00e7d0a9c36dc0a98a8ce541a044d25f5eb0 100644 (file)
@@ -18,27 +18,15 @@ properties:
           - const: rockchip,rk3066-usb
           - const: snps,dwc2
       - items:
-          - const: rockchip,px30-usb
-          - const: rockchip,rk3066-usb
-          - const: snps,dwc2
-      - items:
-          - const: rockchip,rk3036-usb
-          - const: rockchip,rk3066-usb
-          - const: snps,dwc2
-      - items:
-          - const: rockchip,rv1108-usb
-          - const: rockchip,rk3066-usb
-          - const: snps,dwc2
-      - items:
-          - const: rockchip,rk3188-usb
-          - const: rockchip,rk3066-usb
-          - const: snps,dwc2
-      - items:
-          - const: rockchip,rk3228-usb
-          - const: rockchip,rk3066-usb
-          - const: snps,dwc2
-      - items:
-          - const: rockchip,rk3288-usb
+          - enum:
+            - rockchip,px30-usb
+            - rockchip,rk3036-usb
+            - rockchip,rk3188-usb
+            - rockchip,rk3228-usb
+            - rockchip,rk3288-usb
+            - rockchip,rk3328-usb
+            - rockchip,rk3368-usb
+            - rockchip,rv1108-usb
           - const: rockchip,rk3066-usb
           - const: snps,dwc2
       - const: lantiq,arx100-usb
index 66780a47ad8595576458f0b639d6b892c57f633e..9946ff9ba735a8f518cefc5b71d6f8a5d1b05be8 100644 (file)
@@ -7,7 +7,8 @@ Required properties:
  - compatible: must be "snps,dwc3"
  - reg : Address and length of the register set for the device
  - interrupts: Interrupts used by the dwc3 controller.
- - clock-names: should contain "ref", "bus_early", "suspend"
+ - clock-names: list of clock names. Ideally should be "ref",
+                "bus_early", "suspend" but may be less or more.
  - clocks: list of phandle and clock specifier pairs corresponding to
            entries in the clock-names property.
 
@@ -36,7 +37,7 @@ Optional properties:
  - phys: from the *Generic PHY* bindings
  - phy-names: from the *Generic PHY* bindings; supported names are "usb2-phy"
        or "usb3-phy".
- - resets: a single pair of phandle and reset specifier
+ - resets: set of phandle and reset specifier pairs
  - snps,usb2-lpm-disable: indicate if we don't want to enable USB2 HW LPM
  - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
  - snps,dis-start-transfer-quirk: when set, disable isoc START TRANSFER command
@@ -75,6 +76,8 @@ Optional properties:
                        from P0 to P1/P2/P3 without delay.
  - snps,dis-tx-ipgap-linecheck-quirk: when set, disable u2mac linestate check
                        during HS transmit.
+ - snps,parkmode-disable-ss-quirk: when set, all SuperSpeed bus instances in
+                       park mode are disabled.
  - snps,dis_metastability_quirk: when set, disable metastability workaround.
                        CAUTION: use only if you are absolutely sure of it.
  - snps,is-utmi-l1-suspend: true when DWC3 asserts output signal
index e6790d2a4da9bd103d91f2db1dbe3d5331781cc7..67c51759a6422e0bf39f9f7dea8d61d791ee9919 100644 (file)
@@ -35,6 +35,12 @@ Optional properties:
                        the USB data role (USB host or USB device) for a given
                        USB connector, such as Type-C, Type-B(micro).
                        see connector/usb-connector.txt.
+ - role-switch-default-mode: indicating if usb-role-switch is enabled, the
+                       device default operation mode of controller while usb
+                       role is USB_ROLE_NONE. Valid arguments are "host" and
+                       "peripheral". Defaults to "peripheral" if not
+                       specified.
+
 
 This is an attribute to a USB controller such as:
 
diff --git a/Documentation/devicetree/bindings/usb/ingenic,jz4740-musb.txt b/Documentation/devicetree/bindings/usb/ingenic,jz4740-musb.txt
deleted file mode 100644 (file)
index 1680872..0000000
+++ /dev/null
@@ -1,32 +0,0 @@
-Ingenic JZ4740 MUSB driver
-
-Required properties:
-
-- compatible: Must be "ingenic,jz4740-musb"
-- reg: Address range of the UDC register set
-- interrupts: IRQ number related to the UDC hardware
-- interrupt-names: must be "mc"
-- clocks: phandle to the "udc" clock
-- clock-names: must be "udc"
-- phys: phandle to the USB PHY
-
-Example:
-
-usb_phy: usb-phy@0 {
-       compatible = "usb-nop-xceiv";
-       #phy-cells = <0>;
-};
-
-udc: usb@13040000 {
-       compatible = "ingenic,jz4740-musb";
-       reg = <0x13040000 0x10000>;
-
-       interrupt-parent = <&intc>;
-       interrupts = <24>;
-       interrupt-names = "mc";
-
-       clocks = <&cgu JZ4740_CLK_UDC>;
-       clock-names = "udc";
-
-       phys = <&usb_phy>;
-};
diff --git a/Documentation/devicetree/bindings/usb/ingenic,jz4770-phy.yaml b/Documentation/devicetree/bindings/usb/ingenic,jz4770-phy.yaml
new file mode 100644 (file)
index 0000000..a81b0b1
--- /dev/null
@@ -0,0 +1,52 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/ingenic,jz4770-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic JZ4770 USB PHY devicetree bindings
+
+maintainers:
+  - Paul Cercueil <paul@crapouillou.net>
+
+properties:
+  $nodename:
+    pattern: '^usb-phy@.*'
+
+  compatible:
+    enum:
+      - ingenic,jz4770-phy
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  vcc-supply:
+    description: VCC power supply
+
+  '#phy-cells':
+    const: 0
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - vcc-supply
+  - '#phy-cells'
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/jz4770-cgu.h>
+    otg_phy: usb-phy@3c {
+      compatible = "ingenic,jz4770-phy";
+      reg = <0x3c 0x10>;
+
+      vcc-supply = <&vcc>;
+      clocks = <&cgu JZ4770_CLK_OTG_PHY>;
+
+      #phy-cells = <0>;
+    };
diff --git a/Documentation/devicetree/bindings/usb/ingenic,musb.yaml b/Documentation/devicetree/bindings/usb/ingenic,musb.yaml
new file mode 100644 (file)
index 0000000..1d68778
--- /dev/null
@@ -0,0 +1,76 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/ingenic,musb.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Ingenic JZ47xx USB IP DT bindings
+
+maintainers:
+  - Paul Cercueil <paul@crapouillou.net>
+
+properties:
+  $nodename:
+    pattern: '^usb@.*'
+
+  compatible:
+    oneOf:
+      - enum:
+        - ingenic,jz4770-musb
+        - ingenic,jz4740-musb
+      - items:
+        - const: ingenic,jz4725b-musb
+        - const: ingenic,jz4740-musb
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+  clock-names:
+    items:
+      - const: udc
+
+  interrupts:
+    maxItems: 1
+
+  interrupt-names:
+    items:
+      - const: mc
+
+  phys:
+    description: PHY specifier for the USB PHY
+
+required:
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - interrupts
+  - interrupt-names
+  - phys
+
+additionalProperties: false
+
+examples:
+  - |
+    #include <dt-bindings/clock/jz4740-cgu.h>
+    usb_phy: usb-phy@0 {
+      compatible = "usb-nop-xceiv";
+      #phy-cells = <0>;
+    };
+
+    udc: usb@13040000 {
+      compatible = "ingenic,jz4740-musb";
+      reg = <0x13040000 0x10000>;
+
+      interrupt-parent = <&intc>;
+      interrupts = <24>;
+      interrupt-names = "mc";
+
+      clocks = <&cgu JZ4740_CLK_UDC>;
+      clock-names = "udc";
+
+      phys = <&usb_phy>;
+    };
diff --git a/Documentation/devicetree/bindings/usb/maxim,max3420-udc.yaml b/Documentation/devicetree/bindings/usb/maxim,max3420-udc.yaml
new file mode 100644 (file)
index 0000000..4241d38
--- /dev/null
@@ -0,0 +1,69 @@
+# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/usb/maxim,max3420-udc.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: MAXIM MAX3420/1 USB Peripheral Controller
+
+maintainers:
+  - Jassi Brar <jaswinder.singh@linaro.org>
+
+description: |
+  The controller provices USB2.0 compliant FullSpeed peripheral
+  implementation over the SPI interface.
+
+  Specifications about the part can be found at:
+    http://datasheets.maximintegrated.com/en/ds/MAX3420E.pdf
+
+properties:
+  compatible:
+    enum:
+      - maxim,max3420-udc
+      - maxim,max3421-udc
+
+  reg:
+    maxItems: 1
+
+  interrupts:
+    items:
+      - description: usb irq from max3420
+      - description: vbus detection irq
+    minItems: 1
+    maxItems: 2
+
+  interrupt-names:
+    items:
+      - const: udc
+      - const: vbus
+    minItems: 1
+    maxItems: 2
+
+  spi-max-frequency:
+    maximum: 26000000
+
+required:
+  - compatible
+  - reg
+  - interrupts
+  - interrupt-names
+
+additionalProperties: false
+
+examples:
+  - |
+      #include <dt-bindings/gpio/gpio.h>
+      #include <dt-bindings/interrupt-controller/irq.h>
+      spi0 {
+            #address-cells = <1>;
+            #size-cells = <0>;
+
+            udc@0 {
+                  compatible = "maxim,max3420-udc";
+                  reg = <0>;
+                  interrupt-parent = <&gpio>;
+                  interrupts = <0 IRQ_TYPE_EDGE_FALLING>, <10 IRQ_TYPE_EDGE_BOTH>;
+                  interrupt-names = "udc", "vbus";
+                  spi-max-frequency = <12500000>;
+            };
+      };
index f47a69bff4986257cc4eb760f2fde3e2265c2a36..03dfa9c018b76616802252e15bc6a9286c2f7aec 100644 (file)
@@ -53,9 +53,7 @@ in need to reconfigure the pins on the connector, the alternate mode driver
 needs to notify the bus using :c:func:`typec_altmode_notify()`. The driver
 passes the negotiated SVID specific pin configuration value to the function as
 parameter. The bus driver will then configure the mux behind the connector using
-that value as the state value for the mux, and also call blocking notification
-chain to notify the external drivers about the state of the connector that need
-to know it.
+that value as the state value for the mux.
 
 NOTE: The SVID specific pin configuration values must always start from
 ``TYPEC_STATE_MODAL``. USB Type-C specification defines two default states for
@@ -80,19 +78,6 @@ Helper macro ``TYPEC_MODAL_STATE()`` can also be used::
 #define ALTMODEX_CONF_A = TYPEC_MODAL_STATE(0);
 #define ALTMODEX_CONF_B = TYPEC_MODAL_STATE(1);
 
-Notification chain
-~~~~~~~~~~~~~~~~~~
-
-The drivers for the components that the alternate modes are designed for need to
-get details regarding the results of the negotiation with the partner, and the
-pin configuration of the connector. In case of DisplayPort alternate mode for
-example, the GPU drivers will need to know those details. In case of
-Thunderbolt alternate mode, the thunderbolt drivers will need to know them, and
-so on.
-
-The notification chain is designed for this purpose. The drivers can register
-notifiers with :c:func:`typec_altmode_register_notifier()`.
-
 Cable plug alternate modes
 ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
@@ -129,8 +114,3 @@ Cable Plug operations
 
 .. kernel-doc:: drivers/usb/typec/bus.c
    :functions: typec_altmode_get_plug typec_altmode_put_plug
-
-Notifications
-~~~~~~~~~~~~~
-.. kernel-doc:: drivers/usb/typec/class.c
-   :functions: typec_altmode_register_notifier typec_altmode_unregister_notifier
index 36b6ebd9a9d93dd9db8c9b24a394ac039f0e5c03..b656c9be23ed230af5b499ef68689a9bfdfa5094 100644 (file)
@@ -22,6 +22,7 @@ USB support
     misc_usbsevseg
     mtouchusb
     ohci
+    raw-gadget
     usbip_protocol
     usbmon
     usb-serial
diff --git a/Documentation/usb/raw-gadget.rst b/Documentation/usb/raw-gadget.rst
new file mode 100644 (file)
index 0000000..9e78cb8
--- /dev/null
@@ -0,0 +1,61 @@
+==============
+USB Raw Gadget
+==============
+
+USB Raw Gadget is a kernel module that provides a userspace interface for
+the USB Gadget subsystem. Essentially it allows to emulate USB devices
+from userspace. Enabled with CONFIG_USB_RAW_GADGET. Raw Gadget is
+currently a strictly debugging feature and shouldn't be used in
+production, use GadgetFS instead.
+
+Comparison to GadgetFS
+~~~~~~~~~~~~~~~~~~~~~~
+
+Raw Gadget is similar to GadgetFS, but provides a more low-level and
+direct access to the USB Gadget layer for the userspace. The key
+differences are:
+
+1. Every USB request is passed to the userspace to get a response, while
+   GadgetFS responds to some USB requests internally based on the provided
+   descriptors. However note, that the UDC driver might respond to some
+   requests on its own and never forward them to the Gadget layer.
+
+2. GadgetFS performs some sanity checks on the provided USB descriptors,
+   while Raw Gadget allows you to provide arbitrary data as responses to
+   USB requests.
+
+3. Raw Gadget provides a way to select a UDC device/driver to bind to,
+   while GadgetFS currently binds to the first available UDC.
+
+4. Raw Gadget uses predictable endpoint names (handles) across different
+   UDCs (as long as UDCs have enough endpoints of each required transfer
+   type).
+
+5. Raw Gadget has ioctl-based interface instead of a filesystem-based one.
+
+Userspace interface
+~~~~~~~~~~~~~~~~~~~
+
+To create a Raw Gadget instance open /dev/raw-gadget. Multiple raw-gadget
+instances (bound to different UDCs) can be used at the same time. The
+interaction with the opened file happens through the ioctl() calls, see
+comments in include/uapi/linux/usb/raw_gadget.h for details.
+
+The typical usage of Raw Gadget looks like:
+
+1. Open Raw Gadget instance via /dev/raw-gadget.
+2. Initialize the instance via USB_RAW_IOCTL_INIT.
+3. Launch the instance with USB_RAW_IOCTL_RUN.
+4. In a loop issue USB_RAW_IOCTL_EVENT_FETCH calls to receive events from
+   Raw Gadget and react to those depending on what kind of USB device
+   needs to be emulated.
+
+Potential future improvements
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+- Implement ioctl's for setting/clearing halt status on endpoints.
+
+- Reporting more events (suspend, resume, etc.) through
+  USB_RAW_IOCTL_EVENT_FETCH.
+
+- Support O_NONBLOCK I/O.
index 1b37feaf18e652eaeddac4b59f0b330a47aeff0c..91c7db807208f0ae57c038e0c23e4ee42c478768 100644 (file)
@@ -17218,6 +17218,12 @@ S:     Maintained
 F:     Documentation/usb/acm.rst
 F:     drivers/usb/class/cdc-acm.*
 
+USB APPLE MFI FASTCHARGE DRIVER
+M:     Bastien Nocera <hadess@hadess.net>
+L:     linux-usb@vger.kernel.org
+S:     Maintained
+F:     drivers/usb/misc/apple-mfi-fastcharge.c
+
 USB AR5523 WIRELESS DRIVER
 M:     Pontus Fuchs <pontus.fuchs@gmail.com>
 L:     linux-wireless@vger.kernel.org
index 807a0fc2067007a89e7b1c680b9805d732917eee..8e04303e85142b594504096e2e973abf207d34e3 100644 (file)
                        reg = <0x1e6a0000 0x300>;
                        interrupts = <5>;
                        clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
+                       aspeed,vhub-downstream-ports = <5>;
+                       aspeed,vhub-generic-endpoints = <15>;
                        pinctrl-names = "default";
                        pinctrl-0 = <&pinctrl_usb2d_default>;
                        status = "disabled";
index ebec0fa8baa7015f15dd6a92c8504d42936b6680..f12ec04d3cbca92c20b56a04fb51cea0c0fdf43d 100644 (file)
                        reg = <0x1e6a0000 0x300>;
                        interrupts = <5>;
                        clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
+                       aspeed,vhub-downstream-ports = <5>;
+                       aspeed,vhub-generic-endpoints = <15>;
                        pinctrl-names = "default";
                        pinctrl-0 = <&pinctrl_usb2ad_default>;
                        status = "disabled";
index 045ce66ca8761b2c4c22a071e8fae6f97596f75e..7028e21bdd9808935b278a4bb95910512c5c3d54 100644 (file)
                groups = "UART9";
        };
 
+       pinctrl_usb2ah_default: usb2ah_default {
+               function = "USB2AH";
+               groups = "USBA";
+       };
+
+       pinctrl_usb2ad_default: usb2ad_default {
+               function = "USB2AD";
+               groups = "USBA";
+       };
+
+       pinctrl_usb2bh_default: usb2bh_default {
+               function = "USB2BH";
+               groups = "USBB";
+       };
+
+       pinctrl_usb2bd_default: usb2bd_default {
+               function = "USB2BD";
+               groups = "USBB";
+       };
+
+       pinctrl_usb11bhid_default: usb11bhid_default {
+               function = "USB11BHID";
+               groups = "USBB";
+       };
+
        pinctrl_vb_default: vb_default {
                function = "VB";
                groups = "VB";
index 796976d275e125a05a7d5113f207295c4066e383..0a29b3b57a9dc86ff0a7fd7a2d5af7793db5448e 100644 (file)
                        status = "disabled";
                };
 
+               ehci0: usb@1e6a1000 {
+                       compatible = "aspeed,ast2600-ehci", "generic-ehci";
+                       reg = <0x1e6a1000 0x100>;
+                       interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&pinctrl_usb2ah_default>;
+                       status = "disabled";
+               };
+
+               ehci1: usb@1e6a3000 {
+                       compatible = "aspeed,ast2600-ehci", "generic-ehci";
+                       reg = <0x1e6a3000 0x100>;
+                       interrupts = <GIC_SPI 9 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&syscon ASPEED_CLK_GATE_USBPORT2CLK>;
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&pinctrl_usb2bh_default>;
+                       status = "disabled";
+               };
+
+               uhci: usb@1e6b0000 {
+                       compatible = "aspeed,ast2600-uhci", "generic-uhci";
+                       reg = <0x1e6b0000 0x100>;
+                       interrupts = <GIC_SPI 10 IRQ_TYPE_LEVEL_HIGH>;
+                       #ports = <2>;
+                       clocks = <&syscon ASPEED_CLK_GATE_USBUHCICLK>;
+                       status = "disabled";
+                       /*
+                        * No default pinmux, it will follow EHCI, use an
+                        * explicit pinmux override if EHCI is not enabled.
+                        */
+               };
+
+               vhub: usb-vhub@1e6a0000 {
+                       compatible = "aspeed,ast2600-usb-vhub";
+                       reg = <0x1e6a0000 0x350>;
+                       interrupts = <GIC_SPI 5 IRQ_TYPE_LEVEL_HIGH>;
+                       clocks = <&syscon ASPEED_CLK_GATE_USBPORT1CLK>;
+                       aspeed,vhub-downstream-ports = <7>;
+                       aspeed,vhub-generic-endpoints = <21>;
+                       pinctrl-names = "default";
+                       pinctrl-0 = <&pinctrl_usb2ad_default>;
+                       status = "disabled";
+               };
+
                apb {
                        compatible = "simple-bus";
                        #address-cells = <1>;
index b5ce7b0857953e32c90743c39b920a9f61a89f2a..5255550b7c34f1aca050d76785c09713049b2e1c 100644 (file)
@@ -62,6 +62,28 @@ struct resource *platform_get_resource(struct platform_device *dev,
 EXPORT_SYMBOL_GPL(platform_get_resource);
 
 #ifdef CONFIG_HAS_IOMEM
+/**
+ * devm_platform_get_and_ioremap_resource - call devm_ioremap_resource() for a
+ *                                         platform device and get resource
+ *
+ * @pdev: platform device to use both for memory resource lookup as well as
+ *        resource management
+ * @index: resource index
+ * @res: optional output parameter to store a pointer to the obtained resource.
+ */
+void __iomem *
+devm_platform_get_and_ioremap_resource(struct platform_device *pdev,
+                               unsigned int index, struct resource **res)
+{
+       struct resource *r;
+
+       r = platform_get_resource(pdev, IORESOURCE_MEM, index);
+       if (res)
+               *res = r;
+       return devm_ioremap_resource(&pdev->dev, r);
+}
+EXPORT_SYMBOL_GPL(devm_platform_get_and_ioremap_resource);
+
 /**
  * devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform
  *                                 device
@@ -73,10 +95,7 @@ EXPORT_SYMBOL_GPL(platform_get_resource);
 void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev,
                                             unsigned int index)
 {
-       struct resource *res;
-
-       res = platform_get_resource(pdev, IORESOURCE_MEM, index);
-       return devm_ioremap_resource(&pdev->dev, res);
+       return devm_platform_get_and_ioremap_resource(pdev, index, NULL);
 }
 EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource);
 
index 9065ffc85eb476b6b407065ce50c214523a13043..b26e30e1afaf3040401e846915d45385e9c50b9b 100644 (file)
@@ -66,7 +66,7 @@
 #define PHY_CTRL_R14                                           0x38
        #define PHY_CTRL_R14_I_RDP_EN                           BIT(0)
        #define PHY_CTRL_R14_I_RPU_SW1_EN                       BIT(1)
-       #define PHY_CTRL_R14_I_RPU_SW2_EN                       GENMASK(2, 3)
+       #define PHY_CTRL_R14_I_RPU_SW2_EN                       GENMASK(3, 2)
        #define PHY_CTRL_R14_PG_RSTN                            BIT(4)
        #define PHY_CTRL_R14_I_C2L_DATA_16_8                    BIT(5)
        #define PHY_CTRL_R14_I_C2L_ASSERT_SINGLE_EN_ZERO        BIT(6)
 #define RESET_COMPLETE_TIME                                    1000
 #define PLL_RESET_COMPLETE_TIME                                        100
 
+enum meson_soc_id {
+       MESON_SOC_G12A  = 0,
+       MESON_SOC_A1,
+};
+
 struct phy_meson_g12a_usb2_priv {
        struct device           *dev;
        struct regmap           *regmap;
        struct clk              *clk;
        struct reset_control    *reset;
+       int                     soc_id;
 };
 
 static const struct regmap_config phy_meson_g12a_usb2_regmap_conf = {
@@ -164,6 +170,7 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
 {
        struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy);
        int ret;
+       unsigned int value;
 
        ret = reset_control_reset(priv->reset);
        if (ret)
@@ -192,18 +199,22 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
                     FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT2, 2) |
                     FIELD_PREP(PHY_CTRL_R17_MPLL_FILTER_PVT1, 9));
 
-       regmap_write(priv->regmap, PHY_CTRL_R18,
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) |
-                    FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) |
-                    PHY_CTRL_R18_MPLL_ACG_RANGE);
+       value = FIELD_PREP(PHY_CTRL_R18_MPLL_LKW_SEL, 1) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_LK_W, 9) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_LK_S, 0x27) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_PFD_GAIN, 1) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_ROU, 7) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_DATA_SEL, 3) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_BIAS_ADJ, 1) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_BB_MODE, 0) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_ALPHA, 3) |
+               FIELD_PREP(PHY_CTRL_R18_MPLL_ADJ_LDO, 1) |
+               PHY_CTRL_R18_MPLL_ACG_RANGE;
+
+       if (priv->soc_id == MESON_SOC_A1)
+               value |= PHY_CTRL_R18_MPLL_DCO_CLK_SEL;
+
+       regmap_write(priv->regmap, PHY_CTRL_R18, value);
 
        udelay(PLL_RESET_COMPLETE_TIME);
 
@@ -227,13 +238,24 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
                     FIELD_PREP(PHY_CTRL_R20_USB2_BGR_VREF_4_0, 0) |
                     FIELD_PREP(PHY_CTRL_R20_USB2_BGR_DBG_1_0, 0));
 
-       regmap_write(priv->regmap, PHY_CTRL_R4,
-                    FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) |
-                    FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) |
-                    FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) |
-                    PHY_CTRL_R4_TEST_BYPASS_MODE_EN |
-                    FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) |
-                    FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0));
+       if (priv->soc_id == MESON_SOC_G12A)
+               regmap_write(priv->regmap, PHY_CTRL_R4,
+                            FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_7_0, 0xf) |
+                            FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_15_8, 0xf) |
+                            FIELD_PREP(PHY_CTRL_R4_CALIB_CODE_23_16, 0xf) |
+                            PHY_CTRL_R4_TEST_BYPASS_MODE_EN |
+                            FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_1_0, 0) |
+                            FIELD_PREP(PHY_CTRL_R4_I_C2L_BIAS_TRIM_3_2, 0));
+       else if (priv->soc_id == MESON_SOC_A1) {
+               regmap_write(priv->regmap, PHY_CTRL_R21,
+                            PHY_CTRL_R21_USB2_CAL_ACK_EN |
+                            PHY_CTRL_R21_USB2_TX_STRG_PD |
+                            FIELD_PREP(PHY_CTRL_R21_USB2_OTG_ACA_TRIM_1_0, 2));
+
+               /* Analog Settings */
+               regmap_write(priv->regmap, PHY_CTRL_R13,
+                            FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
+       }
 
        /* Tuning Disconnect Threshold */
        regmap_write(priv->regmap, PHY_CTRL_R3,
@@ -241,11 +263,13 @@ static int phy_meson_g12a_usb2_init(struct phy *phy)
                     FIELD_PREP(PHY_CTRL_R3_HSDIC_REF, 1) |
                     FIELD_PREP(PHY_CTRL_R3_DISC_THRESH, 3));
 
-       /* Analog Settings */
-       regmap_write(priv->regmap, PHY_CTRL_R14, 0);
-       regmap_write(priv->regmap, PHY_CTRL_R13,
-                    PHY_CTRL_R13_UPDATE_PMA_SIGNALS |
-                    FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
+       if (priv->soc_id == MESON_SOC_G12A) {
+               /* Analog Settings */
+               regmap_write(priv->regmap, PHY_CTRL_R14, 0);
+               regmap_write(priv->regmap, PHY_CTRL_R13,
+                            PHY_CTRL_R13_UPDATE_PMA_SIGNALS |
+                            FIELD_PREP(PHY_CTRL_R13_MIN_COUNT_FOR_SYNC_DET, 7));
+       }
 
        return 0;
 }
@@ -286,6 +310,8 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev)
        if (IS_ERR(base))
                return PTR_ERR(base);
 
+       priv->soc_id = (enum meson_soc_id)of_device_get_match_data(&pdev->dev);
+
        priv->regmap = devm_regmap_init_mmio(dev, base,
                                             &phy_meson_g12a_usb2_regmap_conf);
        if (IS_ERR(priv->regmap))
@@ -321,8 +347,15 @@ static int phy_meson_g12a_usb2_probe(struct platform_device *pdev)
 }
 
 static const struct of_device_id phy_meson_g12a_usb2_of_match[] = {
-       { .compatible = "amlogic,g12a-usb2-phy", },
-       { },
+       {
+               .compatible = "amlogic,g12a-usb2-phy",
+               .data = (void *)MESON_SOC_G12A,
+       },
+       {
+               .compatible = "amlogic,a1-usb2-phy",
+               .data = (void *)MESON_SOC_A1,
+       },
+       { /* Sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, phy_meson_g12a_usb2_of_match);
 
index b2db916db64ba43b2641a99c0bba334fa05d5869..45954587160832823a46e4da7a818fe43c2b720a 100644 (file)
@@ -3,13 +3,13 @@
 # Phy drivers for Cadence PHYs
 #
 
-config PHY_CADENCE_DP
-       tristate "Cadence MHDP DisplayPort PHY driver"
+config PHY_CADENCE_TORRENT
+       tristate "Cadence Torrent PHY driver"
        depends on OF
        depends on HAS_IOMEM
        select GENERIC_PHY
        help
-         Support for Cadence MHDP DisplayPort PHY.
+         Support for Cadence Torrent PHY.
 
 config PHY_CADENCE_DPHY
        tristate "Cadence D-PHY Support"
index 8f89560f171173125e4abc5e33a55388fe1d89f5..6a7ffc6ea5998c3d6cd30f5af4aa360144377160 100644 (file)
@@ -1,4 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0-only
-obj-$(CONFIG_PHY_CADENCE_DP)   += phy-cadence-dp.o
+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
diff --git a/drivers/phy/cadence/phy-cadence-dp.c b/drivers/phy/cadence/phy-cadence-dp.c
deleted file mode 100644 (file)
index bc10cb2..0000000
+++ /dev/null
@@ -1,541 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Cadence MHDP DisplayPort SD0801 PHY driver.
- *
- * Copyright 2018 Cadence Design Systems, Inc.
- *
- */
-
-#include <linux/delay.h>
-#include <linux/err.h>
-#include <linux/io.h>
-#include <linux/iopoll.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_address.h>
-#include <linux/of_device.h>
-#include <linux/phy/phy.h>
-#include <linux/platform_device.h>
-
-#define DEFAULT_NUM_LANES      2
-#define MAX_NUM_LANES          4
-#define DEFAULT_MAX_BIT_RATE   8100 /* in Mbps */
-
-#define POLL_TIMEOUT_US                2000
-#define LANE_MASK              0x7
-
-/*
- * register offsets from DPTX PHY register block base (i.e MHDP
- * register base + 0x30a00)
- */
-#define PHY_AUX_CONFIG                 0x00
-#define PHY_AUX_CTRL                   0x04
-#define PHY_RESET                      0x20
-#define PHY_PMA_XCVR_PLLCLK_EN         0x24
-#define PHY_PMA_XCVR_PLLCLK_EN_ACK     0x28
-#define PHY_PMA_XCVR_POWER_STATE_REQ   0x2c
-#define PHY_POWER_STATE_LN_0   0x0000
-#define PHY_POWER_STATE_LN_1   0x0008
-#define PHY_POWER_STATE_LN_2   0x0010
-#define PHY_POWER_STATE_LN_3   0x0018
-#define PHY_PMA_XCVR_POWER_STATE_ACK   0x30
-#define PHY_PMA_CMN_READY              0x34
-#define PHY_PMA_XCVR_TX_VMARGIN                0x38
-#define PHY_PMA_XCVR_TX_DEEMPH         0x3c
-
-/*
- * register offsets from SD0801 PHY register block base (i.e MHDP
- * register base + 0x500000)
- */
-#define CMN_SSM_BANDGAP_TMR            0x00084
-#define CMN_SSM_BIAS_TMR               0x00088
-#define CMN_PLLSM0_PLLPRE_TMR          0x000a8
-#define CMN_PLLSM0_PLLLOCK_TMR         0x000b0
-#define CMN_PLLSM1_PLLPRE_TMR          0x000c8
-#define CMN_PLLSM1_PLLLOCK_TMR         0x000d0
-#define CMN_BGCAL_INIT_TMR             0x00190
-#define CMN_BGCAL_ITER_TMR             0x00194
-#define CMN_IBCAL_INIT_TMR             0x001d0
-#define CMN_PLL0_VCOCAL_INIT_TMR       0x00210
-#define CMN_PLL0_VCOCAL_ITER_TMR       0x00214
-#define CMN_PLL0_VCOCAL_REFTIM_START   0x00218
-#define CMN_PLL0_VCOCAL_PLLCNT_START   0x00220
-#define CMN_PLL0_INTDIV_M0             0x00240
-#define CMN_PLL0_FRACDIVL_M0           0x00244
-#define CMN_PLL0_FRACDIVH_M0           0x00248
-#define CMN_PLL0_HIGH_THR_M0           0x0024c
-#define CMN_PLL0_DSM_DIAG_M0           0x00250
-#define CMN_PLL0_LOCK_PLLCNT_START     0x00278
-#define CMN_PLL1_VCOCAL_INIT_TMR       0x00310
-#define CMN_PLL1_VCOCAL_ITER_TMR       0x00314
-#define CMN_PLL1_DSM_DIAG_M0           0x00350
-#define CMN_TXPUCAL_INIT_TMR           0x00410
-#define CMN_TXPUCAL_ITER_TMR           0x00414
-#define CMN_TXPDCAL_INIT_TMR           0x00430
-#define CMN_TXPDCAL_ITER_TMR           0x00434
-#define CMN_RXCAL_INIT_TMR             0x00450
-#define CMN_RXCAL_ITER_TMR             0x00454
-#define CMN_SD_CAL_INIT_TMR            0x00490
-#define CMN_SD_CAL_ITER_TMR            0x00494
-#define CMN_SD_CAL_REFTIM_START                0x00498
-#define CMN_SD_CAL_PLLCNT_START                0x004a0
-#define CMN_PDIAG_PLL0_CTRL_M0         0x00680
-#define CMN_PDIAG_PLL0_CLK_SEL_M0      0x00684
-#define CMN_PDIAG_PLL0_CP_PADJ_M0      0x00690
-#define CMN_PDIAG_PLL0_CP_IADJ_M0      0x00694
-#define CMN_PDIAG_PLL0_FILT_PADJ_M0    0x00698
-#define CMN_PDIAG_PLL0_CP_PADJ_M1      0x006d0
-#define CMN_PDIAG_PLL0_CP_IADJ_M1      0x006d4
-#define CMN_PDIAG_PLL1_CLK_SEL_M0      0x00704
-#define XCVR_DIAG_PLLDRC_CTRL          0x10394
-#define XCVR_DIAG_HSCLK_SEL            0x10398
-#define XCVR_DIAG_HSCLK_DIV            0x1039c
-#define TX_PSC_A0                      0x10400
-#define TX_PSC_A1                      0x10404
-#define TX_PSC_A2                      0x10408
-#define TX_PSC_A3                      0x1040c
-#define RX_PSC_A0                      0x20000
-#define RX_PSC_A1                      0x20004
-#define RX_PSC_A2                      0x20008
-#define RX_PSC_A3                      0x2000c
-#define PHY_PLL_CFG                    0x30038
-
-struct cdns_dp_phy {
-       void __iomem *base;     /* DPTX registers base */
-       void __iomem *sd_base; /* SD0801 registers base */
-       u32 num_lanes; /* Number of lanes to use */
-       u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
-       struct device *dev;
-};
-
-static int cdns_dp_phy_init(struct phy *phy);
-static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
-                                        unsigned int lane);
-static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy);
-static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
-                                       unsigned int offset,
-                                       unsigned char start_bit,
-                                       unsigned char num_bits,
-                                       unsigned int val);
-
-static const struct phy_ops cdns_dp_phy_ops = {
-       .init           = cdns_dp_phy_init,
-       .owner          = THIS_MODULE,
-};
-
-static int cdns_dp_phy_init(struct phy *phy)
-{
-       unsigned char lane_bits;
-
-       struct cdns_dp_phy *cdns_phy = phy_get_drvdata(phy);
-
-       writel(0x0003, cdns_phy->base + PHY_AUX_CTRL); /* enable AUX */
-
-       /* PHY PMA registers configuration function */
-       cdns_dp_phy_pma_cfg(cdns_phy);
-
-       /*
-        * Set lines power state to A0
-        * Set lines pll clk enable to 0
-        */
-
-       cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_POWER_STATE_REQ,
-                               PHY_POWER_STATE_LN_0, 6, 0x0000);
-
-       if (cdns_phy->num_lanes >= 2) {
-               cdns_dp_phy_write_field(cdns_phy,
-                                       PHY_PMA_XCVR_POWER_STATE_REQ,
-                                       PHY_POWER_STATE_LN_1, 6, 0x0000);
-
-               if (cdns_phy->num_lanes == 4) {
-                       cdns_dp_phy_write_field(cdns_phy,
-                                               PHY_PMA_XCVR_POWER_STATE_REQ,
-                                               PHY_POWER_STATE_LN_2, 6, 0);
-                       cdns_dp_phy_write_field(cdns_phy,
-                                               PHY_PMA_XCVR_POWER_STATE_REQ,
-                                               PHY_POWER_STATE_LN_3, 6, 0);
-               }
-       }
-
-       cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
-                               0, 1, 0x0000);
-
-       if (cdns_phy->num_lanes >= 2) {
-               cdns_dp_phy_write_field(cdns_phy, PHY_PMA_XCVR_PLLCLK_EN,
-                                       1, 1, 0x0000);
-               if (cdns_phy->num_lanes == 4) {
-                       cdns_dp_phy_write_field(cdns_phy,
-                                               PHY_PMA_XCVR_PLLCLK_EN,
-                                               2, 1, 0x0000);
-                       cdns_dp_phy_write_field(cdns_phy,
-                                               PHY_PMA_XCVR_PLLCLK_EN,
-                                               3, 1, 0x0000);
-               }
-       }
-
-       /*
-        * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
-        * used lanes
-        */
-       lane_bits = (1 << cdns_phy->num_lanes) - 1;
-       writel(((0xF & ~lane_bits) << 4) | (0xF & lane_bits),
-                  cdns_phy->base + PHY_RESET);
-
-       /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
-       writel(0x0001, cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN);
-
-       /* PHY PMA registers configuration functions */
-       cdns_dp_phy_pma_cmn_vco_cfg_25mhz(cdns_phy);
-       cdns_dp_phy_pma_cmn_rate(cdns_phy);
-
-       /* take out of reset */
-       cdns_dp_phy_write_field(cdns_phy, PHY_RESET, 8, 1, 1);
-       cdns_dp_phy_wait_pma_cmn_ready(cdns_phy);
-       cdns_dp_phy_run(cdns_phy);
-
-       return 0;
-}
-
-static void cdns_dp_phy_wait_pma_cmn_ready(struct cdns_dp_phy *cdns_phy)
-{
-       unsigned int reg;
-       int ret;
-
-       ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_CMN_READY, reg,
-                                reg & 1, 0, 500);
-       if (ret == -ETIMEDOUT)
-               dev_err(cdns_phy->dev,
-                       "timeout waiting for PMA common ready\n");
-}
-
-static void cdns_dp_phy_pma_cfg(struct cdns_dp_phy *cdns_phy)
-{
-       unsigned int i;
-
-       /* PMA common configuration */
-       cdns_dp_phy_pma_cmn_cfg_25mhz(cdns_phy);
-
-       /* PMA lane configuration to deal with multi-link operation */
-       for (i = 0; i < cdns_phy->num_lanes; i++)
-               cdns_dp_phy_pma_lane_cfg(cdns_phy, i);
-}
-
-static void cdns_dp_phy_pma_cmn_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
-{
-       /* refclock registers - assumes 25 MHz refclock */
-       writel(0x0019, cdns_phy->sd_base + CMN_SSM_BIAS_TMR);
-       writel(0x0032, cdns_phy->sd_base + CMN_PLLSM0_PLLPRE_TMR);
-       writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM0_PLLLOCK_TMR);
-       writel(0x0032, cdns_phy->sd_base + CMN_PLLSM1_PLLPRE_TMR);
-       writel(0x00D1, cdns_phy->sd_base + CMN_PLLSM1_PLLLOCK_TMR);
-       writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_INIT_TMR);
-       writel(0x007D, cdns_phy->sd_base + CMN_BGCAL_ITER_TMR);
-       writel(0x0019, cdns_phy->sd_base + CMN_IBCAL_INIT_TMR);
-       writel(0x001E, cdns_phy->sd_base + CMN_TXPUCAL_INIT_TMR);
-       writel(0x0006, cdns_phy->sd_base + CMN_TXPUCAL_ITER_TMR);
-       writel(0x001E, cdns_phy->sd_base + CMN_TXPDCAL_INIT_TMR);
-       writel(0x0006, cdns_phy->sd_base + CMN_TXPDCAL_ITER_TMR);
-       writel(0x02EE, cdns_phy->sd_base + CMN_RXCAL_INIT_TMR);
-       writel(0x0006, cdns_phy->sd_base + CMN_RXCAL_ITER_TMR);
-       writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_INIT_TMR);
-       writel(0x0002, cdns_phy->sd_base + CMN_SD_CAL_ITER_TMR);
-       writel(0x000E, cdns_phy->sd_base + CMN_SD_CAL_REFTIM_START);
-       writel(0x012B, cdns_phy->sd_base + CMN_SD_CAL_PLLCNT_START);
-       /* PLL registers */
-       writel(0x0409, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_PADJ_M0);
-       writel(0x1001, cdns_phy->sd_base + CMN_PDIAG_PLL0_CP_IADJ_M0);
-       writel(0x0F08, cdns_phy->sd_base + CMN_PDIAG_PLL0_FILT_PADJ_M0);
-       writel(0x0004, cdns_phy->sd_base + CMN_PLL0_DSM_DIAG_M0);
-       writel(0x00FA, cdns_phy->sd_base + CMN_PLL0_VCOCAL_INIT_TMR);
-       writel(0x0004, cdns_phy->sd_base + CMN_PLL0_VCOCAL_ITER_TMR);
-       writel(0x00FA, cdns_phy->sd_base + CMN_PLL1_VCOCAL_INIT_TMR);
-       writel(0x0004, cdns_phy->sd_base + CMN_PLL1_VCOCAL_ITER_TMR);
-       writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_REFTIM_START);
-}
-
-static void cdns_dp_phy_pma_cmn_vco_cfg_25mhz(struct cdns_dp_phy *cdns_phy)
-{
-       /* Assumes 25 MHz refclock */
-       switch (cdns_phy->max_bit_rate) {
-               /* Setting VCO for 10.8GHz */
-       case 2700:
-       case 5400:
-               writel(0x01B0, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
-               writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
-               writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
-               writel(0x0120, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
-               break;
-               /* Setting VCO for 9.72GHz */
-       case 2430:
-       case 3240:
-               writel(0x0184, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
-               writel(0xCCCD, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
-               writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
-               writel(0x0104, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
-               break;
-               /* Setting VCO for 8.64GHz */
-       case 2160:
-       case 4320:
-               writel(0x0159, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
-               writel(0x999A, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
-               writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
-               writel(0x00E7, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
-               break;
-               /* Setting VCO for 8.1GHz */
-       case 8100:
-               writel(0x0144, cdns_phy->sd_base + CMN_PLL0_INTDIV_M0);
-               writel(0x0000, cdns_phy->sd_base + CMN_PLL0_FRACDIVL_M0);
-               writel(0x0002, cdns_phy->sd_base + CMN_PLL0_FRACDIVH_M0);
-               writel(0x00D8, cdns_phy->sd_base + CMN_PLL0_HIGH_THR_M0);
-               break;
-       }
-
-       writel(0x0002, cdns_phy->sd_base + CMN_PDIAG_PLL0_CTRL_M0);
-       writel(0x0318, cdns_phy->sd_base + CMN_PLL0_VCOCAL_PLLCNT_START);
-}
-
-static void cdns_dp_phy_pma_cmn_rate(struct cdns_dp_phy *cdns_phy)
-{
-       unsigned int clk_sel_val = 0;
-       unsigned int hsclk_div_val = 0;
-       unsigned int i;
-
-       /* 16'h0000 for single DP link configuration */
-       writel(0x0000, cdns_phy->sd_base + PHY_PLL_CFG);
-
-       switch (cdns_phy->max_bit_rate) {
-       case 1620:
-               clk_sel_val = 0x0f01;
-               hsclk_div_val = 2;
-               break;
-       case 2160:
-       case 2430:
-       case 2700:
-               clk_sel_val = 0x0701;
-                hsclk_div_val = 1;
-               break;
-       case 3240:
-               clk_sel_val = 0x0b00;
-               hsclk_div_val = 2;
-               break;
-       case 4320:
-       case 5400:
-               clk_sel_val = 0x0301;
-               hsclk_div_val = 0;
-               break;
-       case 8100:
-               clk_sel_val = 0x0200;
-               hsclk_div_val = 0;
-               break;
-       }
-
-       writel(clk_sel_val, cdns_phy->sd_base + CMN_PDIAG_PLL0_CLK_SEL_M0);
-
-       /* PMA lane configuration to deal with multi-link operation */
-       for (i = 0; i < cdns_phy->num_lanes; i++) {
-               writel(hsclk_div_val,
-                      cdns_phy->sd_base + (XCVR_DIAG_HSCLK_DIV | (i<<11)));
-       }
-}
-
-static void cdns_dp_phy_pma_lane_cfg(struct cdns_dp_phy *cdns_phy,
-                                    unsigned int lane)
-{
-       unsigned int lane_bits = (lane & LANE_MASK) << 11;
-
-       /* Writing Tx/Rx Power State Controllers registers */
-       writel(0x00FB, cdns_phy->sd_base + (TX_PSC_A0 | lane_bits));
-       writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A2 | lane_bits));
-       writel(0x04AA, cdns_phy->sd_base + (TX_PSC_A3 | lane_bits));
-       writel(0x0000, cdns_phy->sd_base + (RX_PSC_A0 | lane_bits));
-       writel(0x0000, cdns_phy->sd_base + (RX_PSC_A2 | lane_bits));
-       writel(0x0000, cdns_phy->sd_base + (RX_PSC_A3 | lane_bits));
-
-       writel(0x0001, cdns_phy->sd_base + (XCVR_DIAG_PLLDRC_CTRL | lane_bits));
-       writel(0x0000, cdns_phy->sd_base + (XCVR_DIAG_HSCLK_SEL | lane_bits));
-}
-
-static void cdns_dp_phy_run(struct cdns_dp_phy *cdns_phy)
-{
-       unsigned int read_val;
-       u32 write_val1 = 0;
-       u32 write_val2 = 0;
-       u32 mask = 0;
-       int ret;
-
-       /*
-        * waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the
-        * master lane
-        */
-       ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_PLLCLK_EN_ACK,
-                                read_val, read_val & 1, 0, POLL_TIMEOUT_US);
-       if (ret == -ETIMEDOUT)
-               dev_err(cdns_phy->dev,
-                       "timeout waiting for link PLL clock enable ack\n");
-
-       ndelay(100);
-
-       switch (cdns_phy->num_lanes) {
-
-       case 1: /* lane 0 */
-               write_val1 = 0x00000004;
-               write_val2 = 0x00000001;
-               mask = 0x0000003f;
-               break;
-       case 2: /* lane 0-1 */
-               write_val1 = 0x00000404;
-               write_val2 = 0x00000101;
-               mask = 0x00003f3f;
-               break;
-       case 4: /* lane 0-3 */
-               write_val1 = 0x04040404;
-               write_val2 = 0x01010101;
-               mask = 0x3f3f3f3f;
-               break;
-       }
-
-       writel(write_val1, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
-
-       ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
-                                read_val, (read_val & mask) == write_val1, 0,
-                                POLL_TIMEOUT_US);
-       if (ret == -ETIMEDOUT)
-               dev_err(cdns_phy->dev,
-                       "timeout waiting for link power state ack\n");
-
-       writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
-       ndelay(100);
-
-       writel(write_val2, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
-
-       ret = readl_poll_timeout(cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_ACK,
-                                read_val, (read_val & mask) == write_val2, 0,
-                                POLL_TIMEOUT_US);
-       if (ret == -ETIMEDOUT)
-               dev_err(cdns_phy->dev,
-                       "timeout waiting for link power state ack\n");
-
-       writel(0, cdns_phy->base + PHY_PMA_XCVR_POWER_STATE_REQ);
-       ndelay(100);
-}
-
-static void cdns_dp_phy_write_field(struct cdns_dp_phy *cdns_phy,
-                                   unsigned int offset,
-                                   unsigned char start_bit,
-                                   unsigned char num_bits,
-                                   unsigned int val)
-{
-       unsigned int read_val;
-
-       read_val = readl(cdns_phy->base + offset);
-       writel(((val << start_bit) | (read_val & ~(((1 << num_bits) - 1) <<
-               start_bit))), cdns_phy->base + offset);
-}
-
-static int cdns_dp_phy_probe(struct platform_device *pdev)
-{
-       struct resource *regs;
-       struct cdns_dp_phy *cdns_phy;
-       struct device *dev = &pdev->dev;
-       struct phy_provider *phy_provider;
-       struct phy *phy;
-       int err;
-
-       cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
-       if (!cdns_phy)
-               return -ENOMEM;
-
-       cdns_phy->dev = &pdev->dev;
-
-       phy = devm_phy_create(dev, NULL, &cdns_dp_phy_ops);
-       if (IS_ERR(phy)) {
-               dev_err(dev, "failed to create DisplayPort PHY\n");
-               return PTR_ERR(phy);
-       }
-
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       cdns_phy->base = devm_ioremap_resource(&pdev->dev, regs);
-       if (IS_ERR(cdns_phy->base))
-               return PTR_ERR(cdns_phy->base);
-
-       regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs);
-       if (IS_ERR(cdns_phy->sd_base))
-               return PTR_ERR(cdns_phy->sd_base);
-
-       err = device_property_read_u32(dev, "num_lanes",
-                                      &(cdns_phy->num_lanes));
-       if (err)
-               cdns_phy->num_lanes = DEFAULT_NUM_LANES;
-
-       switch (cdns_phy->num_lanes) {
-       case 1:
-       case 2:
-       case 4:
-               /* valid number of lanes */
-               break;
-       default:
-               dev_err(dev, "unsupported number of lanes: %d\n",
-                       cdns_phy->num_lanes);
-               return -EINVAL;
-       }
-
-       err = device_property_read_u32(dev, "max_bit_rate",
-                  &(cdns_phy->max_bit_rate));
-       if (err)
-               cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
-
-       switch (cdns_phy->max_bit_rate) {
-       case 2160:
-       case 2430:
-       case 2700:
-       case 3240:
-       case 4320:
-       case 5400:
-       case 8100:
-               /* valid bit rate */
-               break;
-       default:
-               dev_err(dev, "unsupported max bit rate: %dMbps\n",
-                       cdns_phy->max_bit_rate);
-               return -EINVAL;
-       }
-
-       phy_set_drvdata(phy, cdns_phy);
-
-       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
-
-       dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
-                cdns_phy->num_lanes,
-                cdns_phy->max_bit_rate / 1000,
-                cdns_phy->max_bit_rate % 1000);
-
-       return PTR_ERR_OR_ZERO(phy_provider);
-}
-
-static const struct of_device_id cdns_dp_phy_of_match[] = {
-       {
-               .compatible = "cdns,dp-phy"
-       },
-       {}
-};
-MODULE_DEVICE_TABLE(of, cdns_dp_phy_of_match);
-
-static struct platform_driver cdns_dp_phy_driver = {
-       .probe  = cdns_dp_phy_probe,
-       .driver = {
-               .name   = "cdns-dp-phy",
-               .of_match_table = cdns_dp_phy_of_match,
-       }
-};
-module_platform_driver(cdns_dp_phy_driver);
-
-MODULE_AUTHOR("Cadence Design Systems, Inc.");
-MODULE_DESCRIPTION("Cadence MHDP PHY driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c
new file mode 100644 (file)
index 0000000..7116127
--- /dev/null
@@ -0,0 +1,1944 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Cadence Torrent SD0801 PHY driver.
+ *
+ * Copyright 2018 Cadence Design Systems, Inc.
+ *
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+
+#define REF_CLK_19_2MHz                19200000
+#define REF_CLK_25MHz          25000000
+
+#define DEFAULT_NUM_LANES      4
+#define MAX_NUM_LANES          4
+#define DEFAULT_MAX_BIT_RATE   8100 /* in Mbps */
+
+#define POLL_TIMEOUT_US                5000
+
+#define TORRENT_COMMON_CDB_OFFSET      0x0
+
+#define TORRENT_TX_LANE_CDB_OFFSET(ln, block_offset, reg_offset)       \
+                               ((0x4000 << (block_offset)) +           \
+                               (((ln) << 9) << (reg_offset)))
+
+#define TORRENT_RX_LANE_CDB_OFFSET(ln, block_offset, reg_offset)       \
+                               ((0x8000 << (block_offset)) +           \
+                               (((ln) << 9) << (reg_offset)))
+
+#define TORRENT_PHY_PCS_COMMON_OFFSET(block_offset)    \
+                               (0xC000 << (block_offset))
+
+#define TORRENT_PHY_PMA_COMMON_OFFSET(block_offset)    \
+                               (0xE000 << (block_offset))
+
+#define TORRENT_DPTX_PHY_OFFSET                0x0
+
+/*
+ * register offsets from DPTX PHY register block base (i.e MHDP
+ * register base + 0x30a00)
+ */
+#define PHY_AUX_CTRL                   0x04
+#define PHY_RESET                      0x20
+#define PMA_TX_ELEC_IDLE_MASK          0xF0U
+#define PMA_TX_ELEC_IDLE_SHIFT         4
+#define PHY_L00_RESET_N_MASK           0x01U
+#define PHY_PMA_XCVR_PLLCLK_EN         0x24
+#define PHY_PMA_XCVR_PLLCLK_EN_ACK     0x28
+#define PHY_PMA_XCVR_POWER_STATE_REQ   0x2c
+#define PHY_POWER_STATE_LN_0   0x0000
+#define PHY_POWER_STATE_LN_1   0x0008
+#define PHY_POWER_STATE_LN_2   0x0010
+#define PHY_POWER_STATE_LN_3   0x0018
+#define PMA_XCVR_POWER_STATE_REQ_LN_MASK       0x3FU
+#define PHY_PMA_XCVR_POWER_STATE_ACK   0x30
+#define PHY_PMA_CMN_READY              0x34
+
+/*
+ * register offsets from SD0801 PHY register block base (i.e MHDP
+ * register base + 0x500000)
+ */
+#define CMN_SSM_BANDGAP_TMR            0x0021U
+#define CMN_SSM_BIAS_TMR               0x0022U
+#define CMN_PLLSM0_PLLPRE_TMR          0x002AU
+#define CMN_PLLSM0_PLLLOCK_TMR         0x002CU
+#define CMN_PLLSM1_PLLPRE_TMR          0x0032U
+#define CMN_PLLSM1_PLLLOCK_TMR         0x0034U
+#define CMN_BGCAL_INIT_TMR             0x0064U
+#define CMN_BGCAL_ITER_TMR             0x0065U
+#define CMN_IBCAL_INIT_TMR             0x0074U
+#define CMN_PLL0_VCOCAL_TCTRL          0x0082U
+#define CMN_PLL0_VCOCAL_INIT_TMR       0x0084U
+#define CMN_PLL0_VCOCAL_ITER_TMR       0x0085U
+#define CMN_PLL0_VCOCAL_REFTIM_START   0x0086U
+#define CMN_PLL0_VCOCAL_PLLCNT_START   0x0088U
+#define CMN_PLL0_INTDIV_M0             0x0090U
+#define CMN_PLL0_FRACDIVL_M0           0x0091U
+#define CMN_PLL0_FRACDIVH_M0           0x0092U
+#define CMN_PLL0_HIGH_THR_M0           0x0093U
+#define CMN_PLL0_DSM_DIAG_M0           0x0094U
+#define CMN_PLL0_SS_CTRL1_M0           0x0098U
+#define CMN_PLL0_SS_CTRL2_M0            0x0099U
+#define CMN_PLL0_SS_CTRL3_M0            0x009AU
+#define CMN_PLL0_SS_CTRL4_M0            0x009BU
+#define CMN_PLL0_LOCK_REFCNT_START      0x009CU
+#define CMN_PLL0_LOCK_PLLCNT_START     0x009EU
+#define CMN_PLL0_LOCK_PLLCNT_THR        0x009FU
+#define CMN_PLL1_VCOCAL_TCTRL          0x00C2U
+#define CMN_PLL1_VCOCAL_INIT_TMR       0x00C4U
+#define CMN_PLL1_VCOCAL_ITER_TMR       0x00C5U
+#define CMN_PLL1_VCOCAL_REFTIM_START   0x00C6U
+#define CMN_PLL1_VCOCAL_PLLCNT_START   0x00C8U
+#define CMN_PLL1_INTDIV_M0             0x00D0U
+#define CMN_PLL1_FRACDIVL_M0           0x00D1U
+#define CMN_PLL1_FRACDIVH_M0           0x00D2U
+#define CMN_PLL1_HIGH_THR_M0           0x00D3U
+#define CMN_PLL1_DSM_DIAG_M0           0x00D4U
+#define CMN_PLL1_SS_CTRL1_M0           0x00D8U
+#define CMN_PLL1_SS_CTRL2_M0            0x00D9U
+#define CMN_PLL1_SS_CTRL3_M0            0x00DAU
+#define CMN_PLL1_SS_CTRL4_M0            0x00DBU
+#define CMN_PLL1_LOCK_REFCNT_START      0x00DCU
+#define CMN_PLL1_LOCK_PLLCNT_START     0x00DEU
+#define CMN_PLL1_LOCK_PLLCNT_THR        0x00DFU
+#define CMN_TXPUCAL_INIT_TMR           0x0104U
+#define CMN_TXPUCAL_ITER_TMR           0x0105U
+#define CMN_TXPDCAL_INIT_TMR           0x010CU
+#define CMN_TXPDCAL_ITER_TMR           0x010DU
+#define CMN_RXCAL_INIT_TMR             0x0114U
+#define CMN_RXCAL_ITER_TMR             0x0115U
+#define CMN_SD_CAL_INIT_TMR            0x0124U
+#define CMN_SD_CAL_ITER_TMR            0x0125U
+#define CMN_SD_CAL_REFTIM_START                0x0126U
+#define CMN_SD_CAL_PLLCNT_START                0x0128U
+#define CMN_PDIAG_PLL0_CTRL_M0         0x01A0U
+#define CMN_PDIAG_PLL0_CLK_SEL_M0      0x01A1U
+#define CMN_PDIAG_PLL0_CP_PADJ_M0      0x01A4U
+#define CMN_PDIAG_PLL0_CP_IADJ_M0      0x01A5U
+#define CMN_PDIAG_PLL0_FILT_PADJ_M0    0x01A6U
+#define CMN_PDIAG_PLL0_CP_PADJ_M1      0x01B4U
+#define CMN_PDIAG_PLL0_CP_IADJ_M1      0x01B5U
+#define CMN_PDIAG_PLL1_CTRL_M0         0x01C0U
+#define CMN_PDIAG_PLL1_CLK_SEL_M0      0x01C1U
+#define CMN_PDIAG_PLL1_CP_PADJ_M0      0x01C4U
+#define CMN_PDIAG_PLL1_CP_IADJ_M0      0x01C5U
+#define CMN_PDIAG_PLL1_FILT_PADJ_M0    0x01C6U
+
+/* PMA TX Lane registers */
+#define TX_TXCC_CTRL                   0x0040U
+#define TX_TXCC_CPOST_MULT_00          0x004CU
+#define TX_TXCC_MGNFS_MULT_000         0x0050U
+#define DRV_DIAG_TX_DRV                        0x00C6U
+#define XCVR_DIAG_PLLDRC_CTRL          0x00E5U
+#define XCVR_DIAG_HSCLK_SEL            0x00E6U
+#define XCVR_DIAG_HSCLK_DIV            0x00E7U
+#define XCVR_DIAG_BIDI_CTRL            0x00EAU
+#define TX_PSC_A0                      0x0100U
+#define TX_PSC_A2                      0x0102U
+#define TX_PSC_A3                      0x0103U
+#define TX_RCVDET_ST_TMR               0x0123U
+#define TX_DIAG_ACYA                   0x01E7U
+#define TX_DIAG_ACYA_HBDC_MASK         0x0001U
+
+/* PMA RX Lane registers */
+#define RX_PSC_A0                      0x0000U
+#define RX_PSC_A2                      0x0002U
+#define RX_PSC_A3                      0x0003U
+#define RX_PSC_CAL                     0x0006U
+#define RX_REE_GCSM1_CTRL              0x0108U
+#define RX_REE_GCSM2_CTRL              0x0110U
+#define RX_REE_PERGCSM_CTRL            0x0118U
+
+/* PHY PCS common registers */
+#define PHY_PLL_CFG                    0x000EU
+
+/* PHY PMA common registers */
+#define PHY_PMA_CMN_CTRL2              0x0001U
+#define PHY_PMA_PLL_RAW_CTRL           0x0003U
+
+static const struct reg_field phy_pll_cfg =
+                               REG_FIELD(PHY_PLL_CFG, 0, 1);
+
+static const struct reg_field phy_pma_cmn_ctrl_2 =
+                               REG_FIELD(PHY_PMA_CMN_CTRL2, 0, 7);
+
+static const struct reg_field phy_pma_pll_raw_ctrl =
+                               REG_FIELD(PHY_PMA_PLL_RAW_CTRL, 0, 1);
+
+static const struct reg_field phy_reset_ctrl =
+                               REG_FIELD(PHY_RESET, 8, 8);
+
+static const struct of_device_id cdns_torrent_phy_of_match[];
+
+struct cdns_torrent_inst {
+       struct phy *phy;
+       u32 mlane;
+       u32 phy_type;
+       u32 num_lanes;
+       struct reset_control *lnk_rst;
+};
+
+struct cdns_torrent_phy {
+       void __iomem *base;     /* DPTX registers base */
+       void __iomem *sd_base; /* SD0801 registers base */
+       u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */
+       struct reset_control *phy_rst;
+       struct device *dev;
+       struct clk *clk;
+       unsigned long ref_clk_rate;
+       struct cdns_torrent_inst phys[MAX_NUM_LANES];
+       int nsubnodes;
+       struct regmap *regmap;
+       struct regmap *regmap_common_cdb;
+       struct regmap *regmap_phy_pcs_common_cdb;
+       struct regmap *regmap_phy_pma_common_cdb;
+       struct regmap *regmap_tx_lane_cdb[MAX_NUM_LANES];
+       struct regmap *regmap_rx_lane_cdb[MAX_NUM_LANES];
+       struct regmap *regmap_dptx_phy_reg;
+       struct regmap_field *phy_pll_cfg;
+       struct regmap_field *phy_pma_cmn_ctrl_2;
+       struct regmap_field *phy_pma_pll_raw_ctrl;
+       struct regmap_field *phy_reset_ctrl;
+};
+
+enum phy_powerstate {
+       POWERSTATE_A0 = 0,
+       /* Powerstate A1 is unused */
+       POWERSTATE_A2 = 2,
+       POWERSTATE_A3 = 3,
+};
+
+static int cdns_torrent_dp_init(struct phy *phy);
+static int cdns_torrent_dp_exit(struct phy *phy);
+static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy,
+                              u32 num_lanes);
+static
+int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy);
+static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy,
+                                   struct cdns_torrent_inst *inst);
+static
+void cdns_torrent_dp_pma_cmn_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy);
+static
+void cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy,
+                                            u32 rate, bool ssc);
+static
+void cdns_torrent_dp_pma_cmn_cfg_25mhz(struct cdns_torrent_phy *cdns_phy);
+static
+void cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(struct cdns_torrent_phy *cdns_phy,
+                                          u32 rate, bool ssc);
+static void cdns_torrent_dp_pma_lane_cfg(struct cdns_torrent_phy *cdns_phy,
+                                        unsigned int lane);
+static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy,
+                                        u32 rate, u32 num_lanes);
+static int cdns_torrent_dp_configure(struct phy *phy,
+                                    union phy_configure_opts *opts);
+static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
+                                          u32 num_lanes,
+                                          enum phy_powerstate powerstate);
+static int cdns_torrent_phy_on(struct phy *phy);
+static int cdns_torrent_phy_off(struct phy *phy);
+
+static const struct phy_ops cdns_torrent_phy_ops = {
+       .init           = cdns_torrent_dp_init,
+       .exit           = cdns_torrent_dp_exit,
+       .configure      = cdns_torrent_dp_configure,
+       .power_on       = cdns_torrent_phy_on,
+       .power_off      = cdns_torrent_phy_off,
+       .owner          = THIS_MODULE,
+};
+
+struct cdns_torrent_data {
+               u8 block_offset_shift;
+               u8 reg_offset_shift;
+};
+
+struct cdns_regmap_cdb_context {
+       struct device *dev;
+       void __iomem *base;
+       u8 reg_offset_shift;
+};
+
+static int cdns_regmap_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct cdns_regmap_cdb_context *ctx = context;
+       u32 offset = reg << ctx->reg_offset_shift;
+
+       writew(val, ctx->base + offset);
+
+       return 0;
+}
+
+static int cdns_regmap_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct cdns_regmap_cdb_context *ctx = context;
+       u32 offset = reg << ctx->reg_offset_shift;
+
+       *val = readw(ctx->base + offset);
+       return 0;
+}
+
+static int cdns_regmap_dptx_write(void *context, unsigned int reg,
+                                 unsigned int val)
+{
+       struct cdns_regmap_cdb_context *ctx = context;
+       u32 offset = reg;
+
+       writel(val, ctx->base + offset);
+
+       return 0;
+}
+
+static int cdns_regmap_dptx_read(void *context, unsigned int reg,
+                                unsigned int *val)
+{
+       struct cdns_regmap_cdb_context *ctx = context;
+       u32 offset = reg;
+
+       *val = readl(ctx->base + offset);
+       return 0;
+}
+
+#define TORRENT_TX_LANE_CDB_REGMAP_CONF(n) \
+{ \
+       .name = "torrent_tx_lane" n "_cdb", \
+       .reg_stride = 1, \
+       .fast_io = true, \
+       .reg_write = cdns_regmap_write, \
+       .reg_read = cdns_regmap_read, \
+}
+
+#define TORRENT_RX_LANE_CDB_REGMAP_CONF(n) \
+{ \
+       .name = "torrent_rx_lane" n "_cdb", \
+       .reg_stride = 1, \
+       .fast_io = true, \
+       .reg_write = cdns_regmap_write, \
+       .reg_read = cdns_regmap_read, \
+}
+
+static struct regmap_config cdns_torrent_tx_lane_cdb_config[] = {
+       TORRENT_TX_LANE_CDB_REGMAP_CONF("0"),
+       TORRENT_TX_LANE_CDB_REGMAP_CONF("1"),
+       TORRENT_TX_LANE_CDB_REGMAP_CONF("2"),
+       TORRENT_TX_LANE_CDB_REGMAP_CONF("3"),
+};
+
+static struct regmap_config cdns_torrent_rx_lane_cdb_config[] = {
+       TORRENT_RX_LANE_CDB_REGMAP_CONF("0"),
+       TORRENT_RX_LANE_CDB_REGMAP_CONF("1"),
+       TORRENT_RX_LANE_CDB_REGMAP_CONF("2"),
+       TORRENT_RX_LANE_CDB_REGMAP_CONF("3"),
+};
+
+static struct regmap_config cdns_torrent_common_cdb_config = {
+       .name = "torrent_common_cdb",
+       .reg_stride = 1,
+       .fast_io = true,
+       .reg_write = cdns_regmap_write,
+       .reg_read = cdns_regmap_read,
+};
+
+static struct regmap_config cdns_torrent_phy_pcs_cmn_cdb_config = {
+       .name = "torrent_phy_pcs_cmn_cdb",
+       .reg_stride = 1,
+       .fast_io = true,
+       .reg_write = cdns_regmap_write,
+       .reg_read = cdns_regmap_read,
+};
+
+static struct regmap_config cdns_torrent_phy_pma_cmn_cdb_config = {
+       .name = "torrent_phy_pma_cmn_cdb",
+       .reg_stride = 1,
+       .fast_io = true,
+       .reg_write = cdns_regmap_write,
+       .reg_read = cdns_regmap_read,
+};
+
+static struct regmap_config cdns_torrent_dptx_phy_config = {
+       .name = "torrent_dptx_phy",
+       .reg_stride = 1,
+       .fast_io = true,
+       .reg_write = cdns_regmap_dptx_write,
+       .reg_read = cdns_regmap_dptx_read,
+};
+
+/* PHY mmr access functions */
+
+static void cdns_torrent_phy_write(struct regmap *regmap, u32 offset, u32 val)
+{
+       regmap_write(regmap, offset, val);
+}
+
+static u32 cdns_torrent_phy_read(struct regmap *regmap, u32 offset)
+{
+       unsigned int val;
+
+       regmap_read(regmap, offset, &val);
+       return val;
+}
+
+/* DPTX mmr access functions */
+
+static void cdns_torrent_dp_write(struct regmap *regmap, u32 offset, u32 val)
+{
+       regmap_write(regmap, offset, val);
+}
+
+static u32 cdns_torrent_dp_read(struct regmap *regmap, u32 offset)
+{
+       u32 val;
+
+       regmap_read(regmap, offset, &val);
+       return val;
+}
+
+/*
+ * Structure used to store values of PHY registers for voltage-related
+ * coefficients, for particular voltage swing and pre-emphasis level. Values
+ * are shared across all physical lanes.
+ */
+struct coefficients {
+       /* Value of DRV_DIAG_TX_DRV register to use */
+       u16 diag_tx_drv;
+       /* Value of TX_TXCC_MGNFS_MULT_000 register to use */
+       u16 mgnfs_mult;
+       /* Value of TX_TXCC_CPOST_MULT_00 register to use */
+       u16 cpost_mult;
+};
+
+/*
+ * Array consists of values of voltage-related registers for sd0801 PHY. A value
+ * of 0xFFFF is a placeholder for invalid combination, and will never be used.
+ */
+static const struct coefficients vltg_coeff[4][4] = {
+       /* voltage swing 0, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x002A,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F,
+                .cpost_mult = 0x0014},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0012,
+                .cpost_mult = 0x0020},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x002A}
+       },
+
+       /* voltage swing 1, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x001F,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013,
+                .cpost_mult = 0x0012},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x001F},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF}
+       },
+
+       /* voltage swing 2, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0013,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x0013},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF}
+       },
+
+       /* voltage swing 3, pre-emphasis 0->3 */
+       {       {.diag_tx_drv = 0x0003, .mgnfs_mult = 0x0000,
+                .cpost_mult = 0x0000},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF},
+               {.diag_tx_drv = 0xFFFF, .mgnfs_mult = 0xFFFF,
+                .cpost_mult = 0xFFFF}
+       }
+};
+
+/*
+ * Enable or disable PLL for selected lanes.
+ */
+static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy,
+                                     struct phy_configure_opts_dp *dp,
+                                     bool enable)
+{
+       u32 rd_val;
+       u32 ret;
+       struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+       /*
+        * Used to determine, which bits to check for or enable in
+        * PHY_PMA_XCVR_PLLCLK_EN register.
+        */
+       u32 pll_bits;
+       /* Used to enable or disable lanes. */
+       u32 pll_val;
+
+       /* Select values of registers and mask, depending on enabled lane
+        * count.
+        */
+       switch (dp->lanes) {
+       /* lane 0 */
+       case (1):
+               pll_bits = 0x00000001;
+               break;
+       /* lanes 0-1 */
+       case (2):
+               pll_bits = 0x00000003;
+               break;
+       /* lanes 0-3, all */
+       default:
+               pll_bits = 0x0000000F;
+               break;
+       }
+
+       if (enable)
+               pll_val = pll_bits;
+       else
+               pll_val = 0x00000000;
+
+       cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_val);
+
+       /* Wait for acknowledgment from PHY. */
+       ret = regmap_read_poll_timeout(regmap,
+                                      PHY_PMA_XCVR_PLLCLK_EN_ACK,
+                                      rd_val,
+                                      (rd_val & pll_bits) == pll_val,
+                                      0, POLL_TIMEOUT_US);
+       ndelay(100);
+       return ret;
+}
+
+/*
+ * Perform register operations related to setting link rate, once powerstate is
+ * set and PLL disable request was processed.
+ */
+static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy,
+                                         struct phy_configure_opts_dp *dp)
+{
+       u32 ret;
+       u32 read_val;
+
+       /* Disable the cmn_pll0_en before re-programming the new data rate. */
+       regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x0);
+
+       /*
+        * Wait for PLL ready de-assertion.
+        * For PLL0 - PHY_PMA_CMN_CTRL2[2] == 1
+        */
+       ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+                                            read_val,
+                                            ((read_val >> 2) & 0x01) != 0,
+                                            0, POLL_TIMEOUT_US);
+       if (ret)
+               return ret;
+       ndelay(200);
+
+       /* DP Rate Change - VCO Output settings. */
+       if (cdns_phy->ref_clk_rate == REF_CLK_19_2MHz) {
+               /* PMA common configuration 19.2MHz */
+               cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(cdns_phy, dp->link_rate,
+                                                       dp->ssc);
+               cdns_torrent_dp_pma_cmn_cfg_19_2mhz(cdns_phy);
+       } else if (cdns_phy->ref_clk_rate == REF_CLK_25MHz) {
+               /* PMA common configuration 25MHz */
+               cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(cdns_phy, dp->link_rate,
+                                                     dp->ssc);
+               cdns_torrent_dp_pma_cmn_cfg_25mhz(cdns_phy);
+       }
+       cdns_torrent_dp_pma_cmn_rate(cdns_phy, dp->link_rate, dp->lanes);
+
+       /* Enable the cmn_pll0_en. */
+       regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x3);
+
+       /*
+        * Wait for PLL ready assertion.
+        * For PLL0 - PHY_PMA_CMN_CTRL2[0] == 1
+        */
+       ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2,
+                                            read_val,
+                                            (read_val & 0x01) != 0,
+                                            0, POLL_TIMEOUT_US);
+       return ret;
+}
+
+/*
+ * Verify, that parameters to configure PHY with are correct.
+ */
+static int cdns_torrent_dp_verify_config(struct cdns_torrent_inst *inst,
+                                        struct phy_configure_opts_dp *dp)
+{
+       u8 i;
+
+       /* If changing link rate was required, verify it's supported. */
+       if (dp->set_rate) {
+               switch (dp->link_rate) {
+               case 1620:
+               case 2160:
+               case 2430:
+               case 2700:
+               case 3240:
+               case 4320:
+               case 5400:
+               case 8100:
+                       /* valid bit rate */
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       /* Verify lane count. */
+       switch (dp->lanes) {
+       case 1:
+       case 2:
+       case 4:
+               /* valid lane count. */
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Check against actual number of PHY's lanes. */
+       if (dp->lanes > inst->num_lanes)
+               return -EINVAL;
+
+       /*
+        * If changing voltages is required, check swing and pre-emphasis
+        * levels, per-lane.
+        */
+       if (dp->set_voltages) {
+               /* Lane count verified previously. */
+               for (i = 0; i < dp->lanes; i++) {
+                       if (dp->voltage[i] > 3 || dp->pre[i] > 3)
+                               return -EINVAL;
+
+                       /* Sum of voltage swing and pre-emphasis levels cannot
+                        * exceed 3.
+                        */
+                       if (dp->voltage[i] + dp->pre[i] > 3)
+                               return -EINVAL;
+               }
+       }
+
+       return 0;
+}
+
+/* Set power state A0 and PLL clock enable to 0 on enabled lanes. */
+static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy,
+                                      u32 num_lanes)
+{
+       struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+       u32 pwr_state = cdns_torrent_dp_read(regmap,
+                                            PHY_PMA_XCVR_POWER_STATE_REQ);
+       u32 pll_clk_en = cdns_torrent_dp_read(regmap,
+                                             PHY_PMA_XCVR_PLLCLK_EN);
+
+       /* Lane 0 is always enabled. */
+       pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+                      PHY_POWER_STATE_LN_0);
+       pll_clk_en &= ~0x01U;
+
+       if (num_lanes > 1) {
+               /* lane 1 */
+               pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+                              PHY_POWER_STATE_LN_1);
+               pll_clk_en &= ~(0x01U << 1);
+       }
+
+       if (num_lanes > 2) {
+               /* lanes 2 and 3 */
+               pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+                              PHY_POWER_STATE_LN_2);
+               pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK <<
+                              PHY_POWER_STATE_LN_3);
+               pll_clk_en &= ~(0x01U << 2);
+               pll_clk_en &= ~(0x01U << 3);
+       }
+
+       cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, pwr_state);
+       cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_clk_en);
+}
+
+/* Configure lane count as required. */
+static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy,
+                                    struct phy_configure_opts_dp *dp)
+{
+       u32 value;
+       u32 ret;
+       struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+       u8 lane_mask = (1 << dp->lanes) - 1;
+
+       value = cdns_torrent_dp_read(regmap, PHY_RESET);
+       /* clear pma_tx_elec_idle_ln_* bits. */
+       value &= ~PMA_TX_ELEC_IDLE_MASK;
+       /* Assert pma_tx_elec_idle_ln_* for disabled lanes. */
+       value |= ((~lane_mask) << PMA_TX_ELEC_IDLE_SHIFT) &
+                PMA_TX_ELEC_IDLE_MASK;
+       cdns_torrent_dp_write(regmap, PHY_RESET, value);
+
+       /* reset the link by asserting phy_l00_reset_n low */
+       cdns_torrent_dp_write(regmap, PHY_RESET,
+                             value & (~PHY_L00_RESET_N_MASK));
+
+       /*
+        * Assert lane reset on unused lanes and lane 0 so they remain in reset
+        * and powered down when re-enabling the link
+        */
+       value = (value & 0x0000FFF0) | (0x0000000E & lane_mask);
+       cdns_torrent_dp_write(regmap, PHY_RESET, value);
+
+       cdns_torrent_dp_set_a0_pll(cdns_phy, dp->lanes);
+
+       /* release phy_l0*_reset_n based on used laneCount */
+       value = (value & 0x0000FFF0) | (0x0000000F & lane_mask);
+       cdns_torrent_dp_write(regmap, PHY_RESET, value);
+
+       /* Wait, until PHY gets ready after releasing PHY reset signal. */
+       ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy);
+       if (ret)
+               return ret;
+
+       ndelay(100);
+
+       /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
+       cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001);
+
+       ret = cdns_torrent_dp_run(cdns_phy, dp->lanes);
+
+       return ret;
+}
+
+/* Configure link rate as required. */
+static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy,
+                                   struct phy_configure_opts_dp *dp)
+{
+       u32 ret;
+
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+                                             POWERSTATE_A3);
+       if (ret)
+               return ret;
+       ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, false);
+       if (ret)
+               return ret;
+       ndelay(200);
+
+       ret = cdns_torrent_dp_configure_rate(cdns_phy, dp);
+       if (ret)
+               return ret;
+       ndelay(200);
+
+       ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, true);
+       if (ret)
+               return ret;
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+                                             POWERSTATE_A2);
+       if (ret)
+               return ret;
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes,
+                                             POWERSTATE_A0);
+       if (ret)
+               return ret;
+       ndelay(900);
+
+       return ret;
+}
+
+/* Configure voltage swing and pre-emphasis for all enabled lanes. */
+static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy,
+                                        struct phy_configure_opts_dp *dp)
+{
+       u8 lane;
+       u16 val;
+
+       for (lane = 0; lane < dp->lanes; lane++) {
+               val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane],
+                                           TX_DIAG_ACYA);
+               /*
+                * Write 1 to register bit TX_DIAG_ACYA[0] to freeze the
+                * current state of the analog TX driver.
+                */
+               val |= TX_DIAG_ACYA_HBDC_MASK;
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      TX_DIAG_ACYA, val);
+
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      TX_TXCC_CTRL, 0x08A4);
+               val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].diag_tx_drv;
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      DRV_DIAG_TX_DRV, val);
+               val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].mgnfs_mult;
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      TX_TXCC_MGNFS_MULT_000,
+                                      val);
+               val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].cpost_mult;
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      TX_TXCC_CPOST_MULT_00,
+                                      val);
+
+               val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane],
+                                           TX_DIAG_ACYA);
+               /*
+                * Write 0 to register bit TX_DIAG_ACYA[0] to allow the state of
+                * analog TX driver to reflect the new programmed one.
+                */
+               val &= ~TX_DIAG_ACYA_HBDC_MASK;
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      TX_DIAG_ACYA, val);
+       }
+};
+
+static int cdns_torrent_dp_configure(struct phy *phy,
+                                    union phy_configure_opts *opts)
+{
+       struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+       struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+       int ret;
+
+       ret = cdns_torrent_dp_verify_config(inst, &opts->dp);
+       if (ret) {
+               dev_err(&phy->dev, "invalid params for phy configure\n");
+               return ret;
+       }
+
+       if (opts->dp.set_lanes) {
+               ret = cdns_torrent_dp_set_lanes(cdns_phy, &opts->dp);
+               if (ret) {
+                       dev_err(&phy->dev, "cdns_torrent_dp_set_lanes failed\n");
+                       return ret;
+               }
+       }
+
+       if (opts->dp.set_rate) {
+               ret = cdns_torrent_dp_set_rate(cdns_phy, &opts->dp);
+               if (ret) {
+                       dev_err(&phy->dev, "cdns_torrent_dp_set_rate failed\n");
+                       return ret;
+               }
+       }
+
+       if (opts->dp.set_voltages)
+               cdns_torrent_dp_set_voltages(cdns_phy, &opts->dp);
+
+       return ret;
+}
+
+static int cdns_torrent_dp_init(struct phy *phy)
+{
+       unsigned char lane_bits;
+       int ret;
+       struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+       struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+       struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+       ret = clk_prepare_enable(cdns_phy->clk);
+       if (ret) {
+               dev_err(cdns_phy->dev, "Failed to prepare ref clock\n");
+               return ret;
+       }
+
+       cdns_phy->ref_clk_rate = clk_get_rate(cdns_phy->clk);
+       if (!(cdns_phy->ref_clk_rate)) {
+               dev_err(cdns_phy->dev, "Failed to get ref clock rate\n");
+               clk_disable_unprepare(cdns_phy->clk);
+               return -EINVAL;
+       }
+
+       switch (cdns_phy->ref_clk_rate) {
+       case REF_CLK_19_2MHz:
+       case REF_CLK_25MHz:
+               /* Valid Ref Clock Rate */
+               break;
+       default:
+               dev_err(cdns_phy->dev, "Unsupported Ref Clock Rate\n");
+               return -EINVAL;
+       }
+
+       cdns_torrent_dp_write(regmap, PHY_AUX_CTRL, 0x0003); /* enable AUX */
+
+       /* PHY PMA registers configuration function */
+       cdns_torrent_dp_pma_cfg(cdns_phy, inst);
+
+       /*
+        * Set lines power state to A0
+        * Set lines pll clk enable to 0
+        */
+       cdns_torrent_dp_set_a0_pll(cdns_phy, inst->num_lanes);
+
+       /*
+        * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on
+        * used lanes
+        */
+       lane_bits = (1 << inst->num_lanes) - 1;
+       cdns_torrent_dp_write(regmap, PHY_RESET,
+                             ((0xF & ~lane_bits) << 4) | (0xF & lane_bits));
+
+       /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */
+       cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001);
+
+       /* PHY PMA registers configuration functions */
+       /* Initialize PHY with max supported link rate, without SSC. */
+       if (cdns_phy->ref_clk_rate == REF_CLK_19_2MHz)
+               cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(cdns_phy,
+                                                       cdns_phy->max_bit_rate,
+                                                       false);
+       else if (cdns_phy->ref_clk_rate == REF_CLK_25MHz)
+               cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(cdns_phy,
+                                                     cdns_phy->max_bit_rate,
+                                                     false);
+       cdns_torrent_dp_pma_cmn_rate(cdns_phy, cdns_phy->max_bit_rate,
+                                    inst->num_lanes);
+
+       /* take out of reset */
+       regmap_field_write(cdns_phy->phy_reset_ctrl, 0x1);
+
+       cdns_torrent_phy_on(phy);
+
+       ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy);
+       if (ret)
+               return ret;
+
+       ret = cdns_torrent_dp_run(cdns_phy, inst->num_lanes);
+
+       return ret;
+}
+
+static int cdns_torrent_dp_exit(struct phy *phy)
+{
+       struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+
+       clk_disable_unprepare(cdns_phy->clk);
+       return 0;
+}
+
+static
+int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy)
+{
+       unsigned int reg;
+       int ret;
+       struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+       ret = regmap_read_poll_timeout(regmap, PHY_PMA_CMN_READY, reg,
+                                      reg & 1, 0, POLL_TIMEOUT_US);
+       if (ret == -ETIMEDOUT) {
+               dev_err(cdns_phy->dev,
+                       "timeout waiting for PMA common ready\n");
+               return -ETIMEDOUT;
+       }
+
+       return 0;
+}
+
+static void cdns_torrent_dp_pma_cfg(struct cdns_torrent_phy *cdns_phy,
+                                   struct cdns_torrent_inst *inst)
+{
+       unsigned int i;
+
+       if (cdns_phy->ref_clk_rate == REF_CLK_19_2MHz)
+               /* PMA common configuration 19.2MHz */
+               cdns_torrent_dp_pma_cmn_cfg_19_2mhz(cdns_phy);
+       else if (cdns_phy->ref_clk_rate == REF_CLK_25MHz)
+               /* PMA common configuration 25MHz */
+               cdns_torrent_dp_pma_cmn_cfg_25mhz(cdns_phy);
+
+       /* PMA lane configuration to deal with multi-link operation */
+       for (i = 0; i < inst->num_lanes; i++)
+               cdns_torrent_dp_pma_lane_cfg(cdns_phy, i);
+}
+
+static
+void cdns_torrent_dp_pma_cmn_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy)
+{
+       struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+       /* refclock registers - assumes 19.2 MHz refclock */
+       cdns_torrent_phy_write(regmap, CMN_SSM_BIAS_TMR, 0x0014);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM0_PLLPRE_TMR, 0x0027);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM0_PLLLOCK_TMR, 0x00A1);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM1_PLLPRE_TMR, 0x0027);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM1_PLLLOCK_TMR, 0x00A1);
+       cdns_torrent_phy_write(regmap, CMN_BGCAL_INIT_TMR, 0x0060);
+       cdns_torrent_phy_write(regmap, CMN_BGCAL_ITER_TMR, 0x0060);
+       cdns_torrent_phy_write(regmap, CMN_IBCAL_INIT_TMR, 0x0014);
+       cdns_torrent_phy_write(regmap, CMN_TXPUCAL_INIT_TMR, 0x0018);
+       cdns_torrent_phy_write(regmap, CMN_TXPUCAL_ITER_TMR, 0x0005);
+       cdns_torrent_phy_write(regmap, CMN_TXPDCAL_INIT_TMR, 0x0018);
+       cdns_torrent_phy_write(regmap, CMN_TXPDCAL_ITER_TMR, 0x0005);
+       cdns_torrent_phy_write(regmap, CMN_RXCAL_INIT_TMR, 0x0240);
+       cdns_torrent_phy_write(regmap, CMN_RXCAL_ITER_TMR, 0x0005);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_INIT_TMR, 0x0002);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_ITER_TMR, 0x0002);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_REFTIM_START, 0x000B);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_PLLCNT_START, 0x0137);
+
+       /* PLL registers */
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_INIT_TMR, 0x00C0);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_ITER_TMR, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_INIT_TMR, 0x00C0);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_ITER_TMR, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_REFTIM_START, 0x0260);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_TCTRL, 0x0003);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_REFTIM_START, 0x0260);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_TCTRL, 0x0003);
+}
+
+/*
+ * Set registers responsible for enabling and configuring SSC, with second and
+ * third register values provided by parameters.
+ */
+static
+void cdns_torrent_dp_enable_ssc_19_2mhz(struct cdns_torrent_phy *cdns_phy,
+                                       u32 ctrl2_val, u32 ctrl3_val)
+{
+       struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0001);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl2_val);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl3_val);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0003);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0001);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl2_val);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl3_val);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0003);
+}
+
+static
+void cdns_torrent_dp_pma_cmn_vco_cfg_19_2mhz(struct cdns_torrent_phy *cdns_phy,
+                                            u32 rate, bool ssc)
+{
+       struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+       /* Assumes 19.2 MHz refclock */
+       switch (rate) {
+       /* Setting VCO for 10.8GHz */
+       case 2700:
+       case 5400:
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_INTDIV_M0, 0x0119);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVL_M0, 0x4000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_HIGH_THR_M0, 0x00BC);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL0_CTRL_M0, 0x0012);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_INTDIV_M0, 0x0119);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVL_M0, 0x4000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_HIGH_THR_M0, 0x00BC);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL1_CTRL_M0, 0x0012);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x033A,
+                                                          0x006A);
+               break;
+       /* Setting VCO for 9.72GHz */
+       case 1620:
+       case 2430:
+       case 3240:
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_INTDIV_M0, 0x01FA);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVL_M0, 0x4000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_HIGH_THR_M0, 0x0152);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_INTDIV_M0, 0x01FA);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVL_M0, 0x4000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_HIGH_THR_M0, 0x0152);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x05DD,
+                                                          0x0069);
+               break;
+       /* Setting VCO for 8.64GHz */
+       case 2160:
+       case 4320:
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_INTDIV_M0, 0x01C2);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVL_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_HIGH_THR_M0, 0x012C);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_INTDIV_M0, 0x01C2);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVL_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_HIGH_THR_M0, 0x012C);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x0536,
+                                                          0x0069);
+               break;
+       /* Setting VCO for 8.1GHz */
+       case 8100:
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_INTDIV_M0, 0x01A5);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVL_M0, 0xE000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_HIGH_THR_M0, 0x011A);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_INTDIV_M0, 0x01A5);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVL_M0, 0xE000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_HIGH_THR_M0, 0x011A);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_19_2mhz(cdns_phy, 0x04D7,
+                                                          0x006A);
+               break;
+       }
+
+       if (ssc) {
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_VCOCAL_PLLCNT_START, 0x025E);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_LOCK_PLLCNT_THR, 0x0005);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_VCOCAL_PLLCNT_START, 0x025E);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_LOCK_PLLCNT_THR, 0x0005);
+       } else {
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_VCOCAL_PLLCNT_START, 0x0260);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_VCOCAL_PLLCNT_START, 0x0260);
+               /* Set reset register values to disable SSC */
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_SS_CTRL1_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_SS_CTRL2_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_SS_CTRL3_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_SS_CTRL4_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_LOCK_PLLCNT_THR, 0x0003);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_SS_CTRL1_M0, 0x0002);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_SS_CTRL2_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_SS_CTRL3_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_SS_CTRL4_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_LOCK_PLLCNT_THR, 0x0003);
+       }
+
+       cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_REFCNT_START, 0x0099);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_START, 0x0099);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_REFCNT_START, 0x0099);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_START, 0x0099);
+}
+
+static
+void cdns_torrent_dp_pma_cmn_cfg_25mhz(struct cdns_torrent_phy *cdns_phy)
+{
+       struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+       /* refclock registers - assumes 25 MHz refclock */
+       cdns_torrent_phy_write(regmap, CMN_SSM_BIAS_TMR, 0x0019);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM0_PLLPRE_TMR, 0x0032);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM0_PLLLOCK_TMR, 0x00D1);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM1_PLLPRE_TMR, 0x0032);
+       cdns_torrent_phy_write(regmap, CMN_PLLSM1_PLLLOCK_TMR, 0x00D1);
+       cdns_torrent_phy_write(regmap, CMN_BGCAL_INIT_TMR, 0x007D);
+       cdns_torrent_phy_write(regmap, CMN_BGCAL_ITER_TMR, 0x007D);
+       cdns_torrent_phy_write(regmap, CMN_IBCAL_INIT_TMR, 0x0019);
+       cdns_torrent_phy_write(regmap, CMN_TXPUCAL_INIT_TMR, 0x001E);
+       cdns_torrent_phy_write(regmap, CMN_TXPUCAL_ITER_TMR, 0x0006);
+       cdns_torrent_phy_write(regmap, CMN_TXPDCAL_INIT_TMR, 0x001E);
+       cdns_torrent_phy_write(regmap, CMN_TXPDCAL_ITER_TMR, 0x0006);
+       cdns_torrent_phy_write(regmap, CMN_RXCAL_INIT_TMR, 0x02EE);
+       cdns_torrent_phy_write(regmap, CMN_RXCAL_ITER_TMR, 0x0006);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_INIT_TMR, 0x0002);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_ITER_TMR, 0x0002);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_REFTIM_START, 0x000E);
+       cdns_torrent_phy_write(regmap, CMN_SD_CAL_PLLCNT_START, 0x012B);
+
+       /* PLL registers */
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_INIT_TMR, 0x00FA);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_ITER_TMR, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_INIT_TMR, 0x00FA);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_ITER_TMR, 0x0004);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_REFTIM_START, 0x0317);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_VCOCAL_TCTRL, 0x0003);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_REFTIM_START, 0x0317);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_VCOCAL_TCTRL, 0x0003);
+}
+
+/*
+ * Set registers responsible for enabling and configuring SSC, with second
+ * register value provided by a parameter.
+ */
+static void cdns_torrent_dp_enable_ssc_25mhz(struct cdns_torrent_phy *cdns_phy,
+                                            u32 ctrl2_val)
+{
+       struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0001);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, ctrl2_val);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x007F);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0003);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0001);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, ctrl2_val);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x007F);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0003);
+}
+
+static
+void cdns_torrent_dp_pma_cmn_vco_cfg_25mhz(struct cdns_torrent_phy *cdns_phy,
+                                          u32 rate, bool ssc)
+{
+       struct regmap *regmap = cdns_phy->regmap_common_cdb;
+
+       /* Assumes 25 MHz refclock */
+       switch (rate) {
+       /* Setting VCO for 10.8GHz */
+       case 2700:
+       case 5400:
+               cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x01B0);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0120);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x01B0);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0120);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x0423);
+               break;
+       /* Setting VCO for 9.72GHz */
+       case 1620:
+       case 2430:
+       case 3240:
+               cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0184);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0xCCCD);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0104);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0184);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0xCCCD);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0104);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x03B9);
+               break;
+       /* Setting VCO for 8.64GHz */
+       case 2160:
+       case 4320:
+               cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0159);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x999A);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00E7);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0159);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x999A);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00E7);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x034F);
+               break;
+       /* Setting VCO for 8.1GHz */
+       case 8100:
+               cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0144);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x00D8);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0144);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x00D8);
+               if (ssc)
+                       cdns_torrent_dp_enable_ssc_25mhz(cdns_phy, 0x031A);
+               break;
+       }
+
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002);
+       cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002);
+
+       if (ssc) {
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_VCOCAL_PLLCNT_START, 0x0315);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_LOCK_PLLCNT_THR, 0x0005);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_VCOCAL_PLLCNT_START, 0x0315);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_LOCK_PLLCNT_THR, 0x0005);
+       } else {
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_VCOCAL_PLLCNT_START, 0x0317);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_VCOCAL_PLLCNT_START, 0x0317);
+               /* Set reset register values to disable SSC */
+               cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL1_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL2_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL3_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL0_SS_CTRL4_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL0_LOCK_PLLCNT_THR, 0x0003);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL1_M0, 0x0002);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL2_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL3_M0, 0x0000);
+               cdns_torrent_phy_write(regmap, CMN_PLL1_SS_CTRL4_M0, 0x0000);
+               cdns_torrent_phy_write(regmap,
+                                      CMN_PLL1_LOCK_PLLCNT_THR, 0x0003);
+       }
+
+       cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_REFCNT_START, 0x00C7);
+       cdns_torrent_phy_write(regmap, CMN_PLL0_LOCK_PLLCNT_START, 0x00C7);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_REFCNT_START, 0x00C7);
+       cdns_torrent_phy_write(regmap, CMN_PLL1_LOCK_PLLCNT_START, 0x00C7);
+}
+
+static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy,
+                                        u32 rate, u32 num_lanes)
+{
+       unsigned int clk_sel_val = 0;
+       unsigned int hsclk_div_val = 0;
+       unsigned int i;
+
+       /* 16'h0000 for single DP link configuration */
+       regmap_field_write(cdns_phy->phy_pll_cfg, 0x0);
+
+       switch (rate) {
+       case 1620:
+               clk_sel_val = 0x0f01;
+               hsclk_div_val = 2;
+               break;
+       case 2160:
+       case 2430:
+       case 2700:
+               clk_sel_val = 0x0701;
+               hsclk_div_val = 1;
+               break;
+       case 3240:
+               clk_sel_val = 0x0b00;
+               hsclk_div_val = 2;
+               break;
+       case 4320:
+       case 5400:
+               clk_sel_val = 0x0301;
+               hsclk_div_val = 0;
+               break;
+       case 8100:
+               clk_sel_val = 0x0200;
+               hsclk_div_val = 0;
+               break;
+       }
+
+       cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
+                              CMN_PDIAG_PLL0_CLK_SEL_M0, clk_sel_val);
+       cdns_torrent_phy_write(cdns_phy->regmap_common_cdb,
+                              CMN_PDIAG_PLL1_CLK_SEL_M0, clk_sel_val);
+
+       /* PMA lane configuration to deal with multi-link operation */
+       for (i = 0; i < num_lanes; i++)
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[i],
+                                      XCVR_DIAG_HSCLK_DIV, hsclk_div_val);
+}
+
+static void cdns_torrent_dp_pma_lane_cfg(struct cdns_torrent_phy *cdns_phy,
+                                        unsigned int lane)
+{
+       /* Per lane, refclock-dependent receiver detection setting */
+       if (cdns_phy->ref_clk_rate == REF_CLK_19_2MHz)
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      TX_RCVDET_ST_TMR, 0x0780);
+       else if (cdns_phy->ref_clk_rate == REF_CLK_25MHz)
+               cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                                      TX_RCVDET_ST_TMR, 0x09C4);
+
+       /* Writing Tx/Rx Power State Controllers registers */
+       cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                              TX_PSC_A0, 0x00FB);
+       cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                              TX_PSC_A2, 0x04AA);
+       cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                              TX_PSC_A3, 0x04AA);
+       cdns_torrent_phy_write(cdns_phy->regmap_rx_lane_cdb[lane],
+                              RX_PSC_A0, 0x0000);
+       cdns_torrent_phy_write(cdns_phy->regmap_rx_lane_cdb[lane],
+                              RX_PSC_A2, 0x0000);
+       cdns_torrent_phy_write(cdns_phy->regmap_rx_lane_cdb[lane],
+                              RX_PSC_A3, 0x0000);
+
+       cdns_torrent_phy_write(cdns_phy->regmap_rx_lane_cdb[lane],
+                              RX_PSC_CAL, 0x0000);
+
+       cdns_torrent_phy_write(cdns_phy->regmap_rx_lane_cdb[lane],
+                              RX_REE_GCSM1_CTRL, 0x0000);
+       cdns_torrent_phy_write(cdns_phy->regmap_rx_lane_cdb[lane],
+                              RX_REE_GCSM2_CTRL, 0x0000);
+       cdns_torrent_phy_write(cdns_phy->regmap_rx_lane_cdb[lane],
+                              RX_REE_PERGCSM_CTRL, 0x0000);
+
+       cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                              XCVR_DIAG_BIDI_CTRL, 0x000F);
+       cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                              XCVR_DIAG_PLLDRC_CTRL, 0x0001);
+       cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane],
+                              XCVR_DIAG_HSCLK_SEL, 0x0000);
+}
+
+static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy,
+                                          u32 num_lanes,
+                                          enum phy_powerstate powerstate)
+{
+       /* Register value for power state for a single byte. */
+       u32 value_part;
+       u32 value;
+       u32 mask;
+       u32 read_val;
+       u32 ret;
+       struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+       switch (powerstate) {
+       case (POWERSTATE_A0):
+               value_part = 0x01U;
+               break;
+       case (POWERSTATE_A2):
+               value_part = 0x04U;
+               break;
+       default:
+               /* Powerstate A3 */
+               value_part = 0x08U;
+               break;
+       }
+
+       /* Select values of registers and mask, depending on enabled
+        * lane count.
+        */
+       switch (num_lanes) {
+       /* lane 0 */
+       case (1):
+               value = value_part;
+               mask = 0x0000003FU;
+               break;
+       /* lanes 0-1 */
+       case (2):
+               value = (value_part
+                        | (value_part << 8));
+               mask = 0x00003F3FU;
+               break;
+       /* lanes 0-3, all */
+       default:
+               value = (value_part
+                        | (value_part << 8)
+                        | (value_part << 16)
+                        | (value_part << 24));
+               mask = 0x3F3F3F3FU;
+               break;
+       }
+
+       /* Set power state A<n>. */
+       cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, value);
+       /* Wait, until PHY acknowledges power state completion. */
+       ret = regmap_read_poll_timeout(regmap, PHY_PMA_XCVR_POWER_STATE_ACK,
+                                      read_val, (read_val & mask) == value, 0,
+                                      POLL_TIMEOUT_US);
+       cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, 0x00000000);
+       ndelay(100);
+
+       return ret;
+}
+
+static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes)
+{
+       unsigned int read_val;
+       int ret;
+       struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg;
+
+       /*
+        * waiting for ACK of pma_xcvr_pllclk_en_ln_*, only for the
+        * master lane
+        */
+       ret = regmap_read_poll_timeout(regmap, PHY_PMA_XCVR_PLLCLK_EN_ACK,
+                                      read_val, read_val & 1,
+                                      0, POLL_TIMEOUT_US);
+       if (ret == -ETIMEDOUT) {
+               dev_err(cdns_phy->dev,
+                       "timeout waiting for link PLL clock enable ack\n");
+               return ret;
+       }
+
+       ndelay(100);
+
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
+                                             POWERSTATE_A2);
+       if (ret)
+               return ret;
+
+       ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes,
+                                             POWERSTATE_A0);
+
+       return ret;
+}
+
+static int cdns_torrent_phy_on(struct phy *phy)
+{
+       struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+       struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+       int ret;
+
+       /* Take the PHY out of reset */
+       ret = reset_control_deassert(cdns_phy->phy_rst);
+       if (ret)
+               return ret;
+
+       /* Take the PHY lane group out of reset */
+       return reset_control_deassert(inst->lnk_rst);
+}
+
+static int cdns_torrent_phy_off(struct phy *phy)
+{
+       struct cdns_torrent_inst *inst = phy_get_drvdata(phy);
+       struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent);
+       int ret;
+
+       ret = reset_control_assert(cdns_phy->phy_rst);
+       if (ret)
+               return ret;
+
+       return reset_control_assert(inst->lnk_rst);
+}
+
+static struct regmap *cdns_regmap_init(struct device *dev, void __iomem *base,
+                                      u32 block_offset,
+                                      u8 reg_offset_shift,
+                                      const struct regmap_config *config)
+{
+       struct cdns_regmap_cdb_context *ctx;
+
+       ctx = devm_kzalloc(dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return ERR_PTR(-ENOMEM);
+
+       ctx->dev = dev;
+       ctx->base = base + block_offset;
+       ctx->reg_offset_shift = reg_offset_shift;
+
+       return devm_regmap_init(dev, NULL, ctx, config);
+}
+
+static int cdns_regfield_init(struct cdns_torrent_phy *cdns_phy)
+{
+       struct device *dev = cdns_phy->dev;
+       struct regmap_field *field;
+       struct regmap *regmap;
+
+       regmap = cdns_phy->regmap_phy_pcs_common_cdb;
+       field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg);
+       if (IS_ERR(field)) {
+               dev_err(dev, "PHY_PLL_CFG reg field init failed\n");
+               return PTR_ERR(field);
+       }
+       cdns_phy->phy_pll_cfg = field;
+
+       regmap = cdns_phy->regmap_phy_pma_common_cdb;
+       field = devm_regmap_field_alloc(dev, regmap, phy_pma_cmn_ctrl_2);
+       if (IS_ERR(field)) {
+               dev_err(dev, "PHY_PMA_CMN_CTRL2 reg field init failed\n");
+               return PTR_ERR(field);
+       }
+       cdns_phy->phy_pma_cmn_ctrl_2 = field;
+
+       regmap = cdns_phy->regmap_phy_pma_common_cdb;
+       field = devm_regmap_field_alloc(dev, regmap, phy_pma_pll_raw_ctrl);
+       if (IS_ERR(field)) {
+               dev_err(dev, "PHY_PMA_PLL_RAW_CTRL reg field init failed\n");
+               return PTR_ERR(field);
+       }
+       cdns_phy->phy_pma_pll_raw_ctrl = field;
+
+       regmap = cdns_phy->regmap_dptx_phy_reg;
+       field = devm_regmap_field_alloc(dev, regmap, phy_reset_ctrl);
+       if (IS_ERR(field)) {
+               dev_err(dev, "PHY_RESET reg field init failed\n");
+               return PTR_ERR(field);
+       }
+       cdns_phy->phy_reset_ctrl = field;
+
+       return 0;
+}
+
+static int cdns_regmap_init_torrent_dp(struct cdns_torrent_phy *cdns_phy,
+                                      void __iomem *sd_base,
+                                      void __iomem *base,
+                                      u8 block_offset_shift,
+                                      u8 reg_offset_shift)
+{
+       struct device *dev = cdns_phy->dev;
+       struct regmap *regmap;
+       u32 block_offset;
+       int i;
+
+       for (i = 0; i < MAX_NUM_LANES; i++) {
+               block_offset = TORRENT_TX_LANE_CDB_OFFSET(i, block_offset_shift,
+                                                         reg_offset_shift);
+               regmap = cdns_regmap_init(dev, sd_base, block_offset,
+                                         reg_offset_shift,
+                                         &cdns_torrent_tx_lane_cdb_config[i]);
+               if (IS_ERR(regmap)) {
+                       dev_err(dev, "Failed to init tx lane CDB regmap\n");
+                       return PTR_ERR(regmap);
+               }
+               cdns_phy->regmap_tx_lane_cdb[i] = regmap;
+
+               block_offset = TORRENT_RX_LANE_CDB_OFFSET(i, block_offset_shift,
+                                                         reg_offset_shift);
+               regmap = cdns_regmap_init(dev, sd_base, block_offset,
+                                         reg_offset_shift,
+                                         &cdns_torrent_rx_lane_cdb_config[i]);
+               if (IS_ERR(regmap)) {
+                       dev_err(dev, "Failed to init rx lane CDB regmap\n");
+                       return PTR_ERR(regmap);
+               }
+               cdns_phy->regmap_rx_lane_cdb[i] = regmap;
+       }
+
+       block_offset = TORRENT_COMMON_CDB_OFFSET;
+       regmap = cdns_regmap_init(dev, sd_base, block_offset,
+                                 reg_offset_shift,
+                                 &cdns_torrent_common_cdb_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to init common CDB regmap\n");
+               return PTR_ERR(regmap);
+       }
+       cdns_phy->regmap_common_cdb = regmap;
+
+       block_offset = TORRENT_PHY_PCS_COMMON_OFFSET(block_offset_shift);
+       regmap = cdns_regmap_init(dev, sd_base, block_offset,
+                                 reg_offset_shift,
+                                 &cdns_torrent_phy_pcs_cmn_cdb_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to init PHY PCS common CDB regmap\n");
+               return PTR_ERR(regmap);
+       }
+       cdns_phy->regmap_phy_pcs_common_cdb = regmap;
+
+       block_offset = TORRENT_PHY_PMA_COMMON_OFFSET(block_offset_shift);
+       regmap = cdns_regmap_init(dev, sd_base, block_offset,
+                                 reg_offset_shift,
+                                 &cdns_torrent_phy_pma_cmn_cdb_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to init PHY PMA common CDB regmap\n");
+               return PTR_ERR(regmap);
+       }
+       cdns_phy->regmap_phy_pma_common_cdb = regmap;
+
+       block_offset = TORRENT_DPTX_PHY_OFFSET;
+       regmap = cdns_regmap_init(dev, base, block_offset,
+                                 reg_offset_shift,
+                                 &cdns_torrent_dptx_phy_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to init DPTX PHY regmap\n");
+               return PTR_ERR(regmap);
+       }
+       cdns_phy->regmap_dptx_phy_reg = regmap;
+
+       return 0;
+}
+
+static int cdns_torrent_phy_probe(struct platform_device *pdev)
+{
+       struct resource *regs;
+       struct cdns_torrent_phy *cdns_phy;
+       struct device *dev = &pdev->dev;
+       struct phy_provider *phy_provider;
+       const struct of_device_id *match;
+       struct cdns_torrent_data *data;
+       struct device_node *child;
+       int ret, subnodes, node = 0, i;
+
+       /* Get init data for this PHY */
+       match = of_match_device(cdns_torrent_phy_of_match, dev);
+       if (!match)
+               return -EINVAL;
+
+       data = (struct cdns_torrent_data *)match->data;
+
+       cdns_phy = devm_kzalloc(dev, sizeof(*cdns_phy), GFP_KERNEL);
+       if (!cdns_phy)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, cdns_phy);
+       cdns_phy->dev = dev;
+
+       cdns_phy->phy_rst = devm_reset_control_get_exclusive_by_index(dev, 0);
+       if (IS_ERR(cdns_phy->phy_rst)) {
+               dev_err(dev, "%s: failed to get reset\n",
+                       dev->of_node->full_name);
+               return PTR_ERR(cdns_phy->phy_rst);
+       }
+
+       cdns_phy->clk = devm_clk_get(dev, "refclk");
+       if (IS_ERR(cdns_phy->clk)) {
+               dev_err(dev, "phy ref clock not found\n");
+               return PTR_ERR(cdns_phy->clk);
+       }
+
+       regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       cdns_phy->sd_base = devm_ioremap_resource(&pdev->dev, regs);
+       if (IS_ERR(cdns_phy->sd_base))
+               return PTR_ERR(cdns_phy->sd_base);
+
+       subnodes = of_get_available_child_count(dev->of_node);
+       if (subnodes == 0) {
+               dev_err(dev, "No available link subnodes found\n");
+               return -EINVAL;
+       } else if (subnodes != 1) {
+               dev_err(dev, "Driver supports only one link subnode.\n");
+               return -EINVAL;
+       }
+
+       for_each_available_child_of_node(dev->of_node, child) {
+               struct phy *gphy;
+
+               cdns_phy->phys[node].lnk_rst =
+                               of_reset_control_array_get_exclusive(child);
+               if (IS_ERR(cdns_phy->phys[node].lnk_rst)) {
+                       dev_err(dev, "%s: failed to get reset\n",
+                               child->full_name);
+                       ret = PTR_ERR(cdns_phy->phys[node].lnk_rst);
+                       goto put_lnk_rst;
+               }
+
+               if (of_property_read_u32(child, "reg",
+                                        &cdns_phy->phys[node].mlane)) {
+                       dev_err(dev, "%s: No \"reg\"-property.\n",
+                               child->full_name);
+                       ret = -EINVAL;
+                       goto put_child;
+               }
+
+               if (cdns_phy->phys[node].mlane != 0) {
+                       dev_err(dev,
+                               "%s: Driver supports only lane-0 as master lane.\n",
+                               child->full_name);
+                       ret = -EINVAL;
+                       goto put_child;
+               }
+
+               if (of_property_read_u32(child, "cdns,phy-type",
+                                        &cdns_phy->phys[node].phy_type)) {
+                       dev_err(dev, "%s: No \"cdns,phy-type\"-property.\n",
+                               child->full_name);
+                       ret = -EINVAL;
+                       goto put_child;
+               }
+
+               cdns_phy->phys[node].num_lanes = DEFAULT_NUM_LANES;
+               of_property_read_u32(child, "cdns,num-lanes",
+                                    &cdns_phy->phys[node].num_lanes);
+
+               if (cdns_phy->phys[node].phy_type == PHY_TYPE_DP) {
+                       switch (cdns_phy->phys[node].num_lanes) {
+                       case 1:
+                       case 2:
+                       case 4:
+                       /* valid number of lanes */
+                               break;
+                       default:
+                               dev_err(dev, "unsupported number of lanes: %d\n",
+                                       cdns_phy->phys[node].num_lanes);
+                               ret = -EINVAL;
+                               goto put_child;
+                       }
+
+                       cdns_phy->max_bit_rate = DEFAULT_MAX_BIT_RATE;
+                       of_property_read_u32(child, "cdns,max-bit-rate",
+                                            &cdns_phy->max_bit_rate);
+
+                       switch (cdns_phy->max_bit_rate) {
+                       case 1620:
+                       case 2160:
+                       case 2430:
+                       case 2700:
+                       case 3240:
+                       case 4320:
+                       case 5400:
+                       case 8100:
+                       /* valid bit rate */
+                               break;
+                       default:
+                               dev_err(dev, "unsupported max bit rate: %dMbps\n",
+                                       cdns_phy->max_bit_rate);
+                               ret = -EINVAL;
+                               goto put_child;
+                       }
+
+                       /* DPTX registers */
+                       regs = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+                       cdns_phy->base = devm_ioremap_resource(&pdev->dev,
+                                                              regs);
+                       if (IS_ERR(cdns_phy->base)) {
+                               ret = PTR_ERR(cdns_phy->base);
+                               goto put_child;
+                       }
+
+                       gphy = devm_phy_create(dev, child,
+                                              &cdns_torrent_phy_ops);
+                       if (IS_ERR(gphy)) {
+                               ret = PTR_ERR(gphy);
+                               goto put_child;
+                       }
+
+                       dev_info(dev, "%d lanes, max bit rate %d.%03d Gbps\n",
+                                cdns_phy->phys[node].num_lanes,
+                                cdns_phy->max_bit_rate / 1000,
+                                cdns_phy->max_bit_rate % 1000);
+               } else {
+                       dev_err(dev, "Driver supports only PHY_TYPE_DP\n");
+                       ret = -ENOTSUPP;
+                       goto put_child;
+               }
+               cdns_phy->phys[node].phy = gphy;
+               phy_set_drvdata(gphy, &cdns_phy->phys[node]);
+
+               node++;
+       }
+       cdns_phy->nsubnodes = node;
+
+       ret = cdns_regmap_init_torrent_dp(cdns_phy, cdns_phy->sd_base,
+                                         cdns_phy->base,
+                                         data->block_offset_shift,
+                                         data->reg_offset_shift);
+       if (ret)
+               goto put_lnk_rst;
+
+       ret = cdns_regfield_init(cdns_phy);
+       if (ret)
+               goto put_lnk_rst;
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               ret = PTR_ERR(phy_provider);
+               goto put_lnk_rst;
+       }
+
+       return 0;
+
+put_child:
+       node++;
+put_lnk_rst:
+       for (i = 0; i < node; i++)
+               reset_control_put(cdns_phy->phys[i].lnk_rst);
+       of_node_put(child);
+       return ret;
+}
+
+static int cdns_torrent_phy_remove(struct platform_device *pdev)
+{
+       struct cdns_torrent_phy *cdns_phy = platform_get_drvdata(pdev);
+       int i;
+
+       reset_control_assert(cdns_phy->phy_rst);
+       for (i = 0; i < cdns_phy->nsubnodes; i++) {
+               reset_control_assert(cdns_phy->phys[i].lnk_rst);
+               reset_control_put(cdns_phy->phys[i].lnk_rst);
+       }
+
+       return 0;
+}
+
+static const struct cdns_torrent_data cdns_map_torrent = {
+       .block_offset_shift = 0x2,
+       .reg_offset_shift = 0x2,
+};
+
+static const struct cdns_torrent_data ti_j721e_map_torrent = {
+       .block_offset_shift = 0x0,
+       .reg_offset_shift = 0x1,
+};
+
+static const struct of_device_id cdns_torrent_phy_of_match[] = {
+       {
+               .compatible = "cdns,torrent-phy",
+               .data = &cdns_map_torrent,
+       },
+       {
+               .compatible = "ti,j721e-serdes-10g",
+               .data = &ti_j721e_map_torrent,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, cdns_torrent_phy_of_match);
+
+static struct platform_driver cdns_torrent_phy_driver = {
+       .probe  = cdns_torrent_phy_probe,
+       .remove = cdns_torrent_phy_remove,
+       .driver = {
+               .name   = "cdns-torrent-phy",
+               .of_match_table = cdns_torrent_phy_of_match,
+       }
+};
+module_platform_driver(cdns_torrent_phy_driver);
+
+MODULE_AUTHOR("Cadence Design Systems, Inc.");
+MODULE_DESCRIPTION("Cadence Torrent PHY driver");
+MODULE_LICENSE("GPL v2");
index cb2ed3b2506858f852fe51eec0148b4d748ef154..cdbcc49f71152cf479ab1a35eabeb8b663469f10 100644 (file)
@@ -43,6 +43,8 @@
 #define PA0_RG_USB20_INTR_EN           BIT(5)
 
 #define U3P_USBPHYACR1         0x004
+#define PA1_RG_INTR_CAL                GENMASK(23, 19)
+#define PA1_RG_INTR_CAL_VAL(x) ((0x1f & (x)) << 19)
 #define PA1_RG_VRT_SEL                 GENMASK(14, 12)
 #define PA1_RG_VRT_SEL_VAL(x)  ((0x7 & (x)) << 12)
 #define PA1_RG_TERM_SEL                GENMASK(10, 8)
@@ -60,6 +62,8 @@
 #define U3P_USBPHYACR6         0x018
 #define PA6_RG_U2_BC11_SW_EN           BIT(23)
 #define PA6_RG_U2_OTG_VBUSCMP_EN       BIT(20)
+#define PA6_RG_U2_DISCTH               GENMASK(7, 4)
+#define PA6_RG_U2_DISCTH_VAL(x)        ((0xf & (x)) << 4)
 #define PA6_RG_U2_SQTH         GENMASK(3, 0)
 #define PA6_RG_U2_SQTH_VAL(x)  (0xf & (x))
 
@@ -294,20 +298,21 @@ struct mtk_phy_instance {
                struct u2phy_banks u2_banks;
                struct u3phy_banks u3_banks;
        };
-       struct clk *ref_clk;    /* reference clock of anolog phy */
+       struct clk *ref_clk;    /* reference clock of (digital) phy */
+       struct clk *da_ref_clk; /* reference clock of analog phy */
        u32 index;
        u8 type;
        int eye_src;
        int eye_vrt;
        int eye_term;
+       int intr;
+       int discth;
        bool bc12_en;
 };
 
 struct mtk_tphy {
        struct device *dev;
        void __iomem *sif_base; /* only shared sif */
-       /* deprecated, use @ref_clk instead in phy instance */
-       struct clk *u3phya_ref; /* reference clock of usb3 anolog phy */
        const struct mtk_phy_pdata *pdata;
        struct mtk_phy_instance **phys;
        int nphys;
@@ -850,9 +855,14 @@ static void phy_parse_property(struct mtk_tphy *tphy,
                                 &instance->eye_vrt);
        device_property_read_u32(dev, "mediatek,eye-term",
                                 &instance->eye_term);
-       dev_dbg(dev, "bc12:%d, src:%d, vrt:%d, term:%d\n",
+       device_property_read_u32(dev, "mediatek,intr",
+                                &instance->intr);
+       device_property_read_u32(dev, "mediatek,discth",
+                                &instance->discth);
+       dev_dbg(dev, "bc12:%d, src:%d, vrt:%d, term:%d, intr:%d, disc:%d\n",
                instance->bc12_en, instance->eye_src,
-               instance->eye_vrt, instance->eye_term);
+               instance->eye_vrt, instance->eye_term,
+               instance->intr, instance->discth);
 }
 
 static void u2_phy_props_set(struct mtk_tphy *tphy,
@@ -888,6 +898,20 @@ static void u2_phy_props_set(struct mtk_tphy *tphy,
                tmp |= PA1_RG_TERM_SEL_VAL(instance->eye_term);
                writel(tmp, com + U3P_USBPHYACR1);
        }
+
+       if (instance->intr) {
+               tmp = readl(com + U3P_USBPHYACR1);
+               tmp &= ~PA1_RG_INTR_CAL;
+               tmp |= PA1_RG_INTR_CAL_VAL(instance->intr);
+               writel(tmp, com + U3P_USBPHYACR1);
+       }
+
+       if (instance->discth) {
+               tmp = readl(com + U3P_USBPHYACR6);
+               tmp &= ~PA6_RG_U2_DISCTH;
+               tmp |= PA6_RG_U2_DISCTH_VAL(instance->discth);
+               writel(tmp, com + U3P_USBPHYACR6);
+       }
 }
 
 static int mtk_phy_init(struct phy *phy)
@@ -896,15 +920,16 @@ static int mtk_phy_init(struct phy *phy)
        struct mtk_tphy *tphy = dev_get_drvdata(phy->dev.parent);
        int ret;
 
-       ret = clk_prepare_enable(tphy->u3phya_ref);
+       ret = clk_prepare_enable(instance->ref_clk);
        if (ret) {
-               dev_err(tphy->dev, "failed to enable u3phya_ref\n");
+               dev_err(tphy->dev, "failed to enable ref_clk\n");
                return ret;
        }
 
-       ret = clk_prepare_enable(instance->ref_clk);
+       ret = clk_prepare_enable(instance->da_ref_clk);
        if (ret) {
-               dev_err(tphy->dev, "failed to enable ref_clk\n");
+               dev_err(tphy->dev, "failed to enable da_ref\n");
+               clk_disable_unprepare(instance->ref_clk);
                return ret;
        }
 
@@ -967,7 +992,7 @@ static int mtk_phy_exit(struct phy *phy)
                u2_phy_instance_exit(tphy, instance);
 
        clk_disable_unprepare(instance->ref_clk);
-       clk_disable_unprepare(tphy->u3phya_ref);
+       clk_disable_unprepare(instance->da_ref_clk);
        return 0;
 }
 
@@ -1102,11 +1127,6 @@ static int mtk_tphy_probe(struct platform_device *pdev)
                }
        }
 
-       /* it's deprecated, make it optional for backward compatibility */
-       tphy->u3phya_ref = devm_clk_get_optional(dev, "u3phya_ref");
-       if (IS_ERR(tphy->u3phya_ref))
-               return PTR_ERR(tphy->u3phya_ref);
-
        tphy->src_ref_clk = U3P_REF_CLK;
        tphy->src_coef = U3P_SLEW_RATE_COEF;
        /* update parameters of slew rate calibrate if exist */
@@ -1153,16 +1173,20 @@ static int mtk_tphy_probe(struct platform_device *pdev)
                phy_set_drvdata(phy, instance);
                port++;
 
-               /* if deprecated clock is provided, ignore instance's one */
-               if (tphy->u3phya_ref)
-                       continue;
-
-               instance->ref_clk = devm_clk_get(&phy->dev, "ref");
+               instance->ref_clk = devm_clk_get_optional(&phy->dev, "ref");
                if (IS_ERR(instance->ref_clk)) {
                        dev_err(dev, "failed to get ref_clk(id-%d)\n", port);
                        retval = PTR_ERR(instance->ref_clk);
                        goto put_child;
                }
+
+               instance->da_ref_clk =
+                       devm_clk_get_optional(&phy->dev, "da_ref");
+               if (IS_ERR(instance->da_ref_clk)) {
+                       dev_err(dev, "failed to get da_ref_clk(id-%d)\n", port);
+                       retval = PTR_ERR(instance->da_ref_clk);
+                       goto put_child;
+               }
        }
 
        provider = devm_of_phy_provider_register(dev, mtk_phy_xlate);
index e46824da29f6c9191ce18d118f3876a7cd5cd918..98674ed094d9c3c1b0de77f058ea146974460761 100644 (file)
@@ -91,3 +91,23 @@ config PHY_QCOM_USB_HSIC
        select GENERIC_PHY
        help
          Support for the USB HSIC ULPI compliant PHY on QCOM chipsets.
+
+config PHY_QCOM_USB_HS_28NM
+       tristate "Qualcomm 28nm High-Speed PHY"
+       depends on ARCH_QCOM || COMPILE_TEST
+       depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in
+       select GENERIC_PHY
+       help
+         Enable this to support the Qualcomm Synopsys DesignWare Core 28nm
+         High-Speed PHY driver. This driver supports the Hi-Speed PHY which
+         is usually paired with either the ChipIdea or Synopsys DWC3 USB
+         IPs on MSM SOCs.
+
+config PHY_QCOM_USB_SS
+       tristate "Qualcomm USB Super-Speed PHY driver"
+       depends on ARCH_QCOM || COMPILE_TEST
+       depends on EXTCON || !EXTCON # if EXTCON=m, this cannot be built-in
+       select GENERIC_PHY
+       help
+         Enable this to support the Super-Speed USB transceiver on various
+         Qualcomm chipsets.
index 283251d6a5d9b9e48ca00b5691cccb6915277596..1f14aeacbd70935618ee81137cf2f6ad727fec73 100644 (file)
@@ -10,3 +10,5 @@ obj-$(CONFIG_PHY_QCOM_UFS_14NM)               += phy-qcom-ufs-qmp-14nm.o
 obj-$(CONFIG_PHY_QCOM_UFS_20NM)                += phy-qcom-ufs-qmp-20nm.o
 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
index 7db2a94f7a995c94c896a9a3c25d6f9705906423..c190406246ab0817d49e5ce624befa2f0e4307ec 100644 (file)
@@ -121,6 +121,11 @@ enum qphy_reg_layout {
        QPHY_PCS_LFPS_RXTERM_IRQ_STATUS,
 };
 
+static const unsigned int msm8996_ufsphy_regs_layout[] = {
+       [QPHY_START_CTRL]               = 0x00,
+       [QPHY_PCS_READY_STATUS]         = 0x168,
+};
+
 static const unsigned int pciephy_regs_layout[] = {
        [QPHY_COM_SW_RESET]             = 0x400,
        [QPHY_COM_POWER_DOWN_CONTROL]   = 0x404,
@@ -160,6 +165,18 @@ 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[] = {
+       [QPHY_SW_RESET]                 = 0x00,
+       [QPHY_START_CTRL]               = 0x08,
+       [QPHY_PCS_STATUS]               = 0x174,
+};
+
+static const unsigned int sdm845_qhp_pciephy_regs_layout[] = {
+       [QPHY_SW_RESET]                 = 0x00,
+       [QPHY_START_CTRL]               = 0x08,
+       [QPHY_PCS_STATUS]               = 0x2ac,
+};
+
 static const unsigned int sdm845_ufsphy_regs_layout[] = {
        [QPHY_START_CTRL]               = 0x00,
        [QPHY_PCS_READY_STATUS]         = 0x160,
@@ -331,6 +348,75 @@ static const struct qmp_phy_init_tbl msm8998_pcie_pcs_tbl[] = {
        QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03),
 };
 
+static const struct qmp_phy_init_tbl msm8996_ufs_serdes_tbl[] = {
+       QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x0e),
+       QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0xd7),
+       QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30),
+       QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06),
+       QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+       QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a),
+       QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x05),
+       QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV, 0x0a),
+       QMP_PHY_INIT_CFG(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_EN, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_CTRL, 0x10),
+       QMP_PHY_INIT_CFG(QSERDES_COM_RESETSM_CNTRL, 0x20),
+       QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x54),
+       QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x82),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+       QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+       QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+       QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+       QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE0, 0x28),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE0, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xff),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE1, 0x98),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE1, 0x0b),
+       QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
+       QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE1, 0x28),
+       QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80),
+       QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6),
+       QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f),
+       QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
+};
+
+static const struct qmp_phy_init_tbl msm8996_ufs_tx_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45),
+       QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x02),
+};
+
+static const struct qmp_phy_init_tbl msm8996_ufs_rx_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_LVL, 0x24),
+       QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18),
+       QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B),
+       QMP_PHY_INIT_CFG(QSERDES_RX_RX_TERM_BW, 0x5b),
+       QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xff),
+       QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3f),
+       QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xff),
+       QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0f),
+       QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
+};
+
 static const struct qmp_phy_init_tbl msm8996_usb3_serdes_tbl[] = {
        QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x14),
        QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
@@ -481,6 +567,229 @@ static const struct qmp_phy_init_tbl ipq8074_pcie_pcs_tbl[] = {
        QMP_PHY_INIT_CFG_L(QPHY_START_CTRL, 0x3),
 };
 
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_serdes_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x007),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_CONFIG, 0x06),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP_EN, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_RESETSM_CNTRL, 0x20),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_MAP, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE2_MODE0, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE1_MODE0, 0xc9),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER1, 0xff),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_VCO_TUNE_TIMER2, 0x3f),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SVS_MODE_CLK_SEL, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORE_CLK_EN, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CORECLK_DIV_MODE0, 0x0a),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_EP_DIV, 0x19),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_ENABLE1, 0x90),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_DEC_START_MODE0, 0x82),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START3_MODE0, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START2_MODE0, 0xea),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_DIV_FRAC_START1_MODE0, 0xab),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP3_MODE0, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP2_MODE0, 0x0d),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_LOCK_CMP1_MODE0, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_HSCLK_SEL, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CP_CTRL_MODE0, 0x06),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_RCTRL_MODE0, 0x16),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_CCTRL_MODE0, 0x36),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CMN_MODE, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x33),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYS_CLK_CTRL, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_BUF_ENABLE, 0x06),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_BG_TIMER, 0x09),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_EN_CENTER, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER1, 0x40),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_PER2, 0x01),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER1, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_ADJ_PER2, 0x00),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE1, 0x7e),
+       QMP_PHY_INIT_CFG(QSERDES_V3_COM_SSC_STEP_SIZE2, 0x15),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_tx_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_V3_TX_RES_CODE_LANE_OFFSET_TX, 0x02),
+       QMP_PHY_INIT_CFG(QSERDES_V3_TX_RCV_DETECT_LVL_2, 0x12),
+       QMP_PHY_INIT_CFG(QSERDES_V3_TX_HIGHZ_DRVR_EN, 0x10),
+       QMP_PHY_INIT_CFG(QSERDES_V3_TX_LANE_MODE_1, 0x06),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_rx_tbl[] = {
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_CNTRL, 0x03),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_ENABLES, 0x10),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_SIGDET_DEGLITCH_CNTRL, 0x14),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0e),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL3, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQU_ADAPTOR_CNTRL4, 0x1a),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x4b),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_SO_GAIN_HALF, 0x04),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x71),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_00, 0x59),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_MODE_01, 0x59),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_RX_INTERFACE_MODE, 0x40),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_PI_CONTROLS, 0x71),
+       QMP_PHY_INIT_CFG(QSERDES_V3_RX_UCDR_FASTLOCK_COUNT_LOW, 0x40),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_tbl[] = {
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_ENDPOINT_REFCLK_DRIVE, 0x04),
+
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x40),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02),
+
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_OSC_DTCT_ACTIONS, 0x00),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x01),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_L1SS_WAKEUP_DLY_TIME_AUXCLK_LSB, 0x20),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK_MSB, 0x00),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_LP_WAKEUP_DLY_TIME_AUXCLK, 0x01),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_PLL_LOCK_CHK_DLY_TIME, 0x73),
+
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0xbb),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_SIGDET_CNTRL, 0x03),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_REFGEN_REQ_CONFIG1, 0x0d),
+
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG4, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qmp_pcie_pcs_misc_tbl[] = {
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_CONFIG2, 0x52),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG2, 0x10),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG4, 0x1a),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_OSC_DTCT_MODE2_CONFIG5, 0x06),
+       QMP_PHY_INIT_CFG(QPHY_V3_PCS_MISC_PCIE_INT_AUX_CLK_CONFIG1, 0x00),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_serdes_tbl[] = {
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL, 0x27),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_EN_CENTER, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER1, 0x31),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_PER2, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1, 0xde),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2, 0x07),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1, 0x4c),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1, 0x06),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN, 0x18),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_ENABLE1, 0xb0),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0, 0x8c),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0, 0x20),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1, 0x14),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1, 0x34),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE0, 0x06),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CP_CTRL_MODE1, 0x06),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0, 0x16),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1, 0x16),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0, 0x36),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1, 0x36),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_RESTRIM_CTRL2, 0x05),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_LOCK_CMP_EN, 0x42),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE0, 0x82),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DEC_START_MODE1, 0x68),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0, 0x55),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0, 0x55),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0, 0x03),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1, 0xab),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1, 0xaa),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1, 0x02),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0, 0x3f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1, 0x3f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VCO_TUNE_MAP, 0x10),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CLK_SELECT, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_HSCLK_SEL1, 0x30),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORE_CLK_EN, 0x73),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_CONFIG, 0x0c),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL, 0x15),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_CMN_MODE, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV1, 0x22),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_VREGCLK_DIV2, 0x00),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BGV_TRIM, 0x20),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_COM_BG_CTRL, 0x07),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_tx_tbl[] = {
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL0, 0x00),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_TAP_EN, 0x0d),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TX_BAND_MODE, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_LANE_MODE, 0x1a),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PARALLEL_RATE, 0x2f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE0, 0x09),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE1, 0x09),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CML_CTRL_MODE2, 0x1b),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2, 0x07),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0, 0x31),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1, 0x31),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2, 0x03),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE, 0x02),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CGA_THRESH_DFE, 0x00),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXENGINE_EN0, 0x12),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME, 0x25),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME, 0x00),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME, 0x05),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_GAIN, 0x26),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DFE_GAIN, 0x12),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_GAIN, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_OFFSET_GAIN, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PRE_GAIN, 0x09),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EQ_INTVAL, 0x15),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_EDAC_INITVAL, 0x28),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB0, 0x7f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_INITB1, 0x07),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RXEQ_CTRL, 0x70),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0, 0x8b),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1, 0x08),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2, 0x0a),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0, 0x03),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG, 0x0c),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_BAND, 0x02),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0, 0x5c),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1, 0x3e),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2, 0x3f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_ENABLES, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_CNTRL, 0xa0),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL, 0x08),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DCC_GAIN, 0x01),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_EN_SIGNAL, 0xc3),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL, 0x00),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0, 0xbc),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_TS0_TIMER, 0x7f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE, 0x15),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL1, 0x0c),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_DRVR_CTRL2, 0x0f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET, 0x04),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_VGA_INITVAL, 0x20),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_L0_RSM_START, 0x01),
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_rx_tbl[] = {
+};
+
+static const struct qmp_phy_init_tbl sdm845_qhp_pcie_pcs_tbl[] = {
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG, 0x3f),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG, 0x50),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB, 0x19),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB, 0x07),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB, 0x17),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB, 0x09),
+       QMP_PHY_INIT_CFG(PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5, 0x9f),
+};
+
 static const struct qmp_phy_init_tbl qmp_v3_usb3_serdes_tbl[] = {
        QMP_PHY_INIT_CFG(QSERDES_V3_COM_PLL_IVCO, 0x07),
        QMP_PHY_INIT_CFG(QSERDES_V3_COM_SYSCLK_EN_SEL, 0x14),
@@ -988,6 +1297,8 @@ struct qmp_phy_cfg {
        int rx_tbl_num;
        const struct qmp_phy_init_tbl *pcs_tbl;
        int pcs_tbl_num;
+       const struct qmp_phy_init_tbl *pcs_misc_tbl;
+       int pcs_misc_tbl_num;
 
        /* clock ids to be requested */
        const char * const *clk_list;
@@ -1122,10 +1433,18 @@ static const char * const msm8996_phy_clk_l[] = {
        "aux", "cfg_ahb", "ref",
 };
 
+static const char * const msm8996_ufs_phy_clk_l[] = {
+       "ref",
+};
+
 static const char * const qmp_v3_phy_clk_l[] = {
        "aux", "cfg_ahb", "ref", "com_aux",
 };
 
+static const char * const sdm845_pciephy_clk_l[] = {
+       "aux", "cfg_ahb", "ref", "refgen",
+};
+
 static const char * const sdm845_ufs_phy_clk_l[] = {
        "ref", "ref_aux",
 };
@@ -1139,6 +1458,10 @@ static const char * const msm8996_usb3phy_reset_l[] = {
        "phy", "common",
 };
 
+static const char * const sdm845_pciephy_reset_l[] = {
+       "phy",
+};
+
 /* list of regulators */
 static const char * const qmp_phy_vreg_l[] = {
        "vdda-phy", "vdda-pll",
@@ -1175,6 +1498,31 @@ static const struct qmp_phy_cfg msm8996_pciephy_cfg = {
        .pwrdn_delay_max        = POWER_DOWN_DELAY_US_MAX,
 };
 
+static const struct qmp_phy_cfg msm8996_ufs_cfg = {
+       .type                   = PHY_TYPE_UFS,
+       .nlanes                 = 1,
+
+       .serdes_tbl             = msm8996_ufs_serdes_tbl,
+       .serdes_tbl_num         = ARRAY_SIZE(msm8996_ufs_serdes_tbl),
+       .tx_tbl                 = msm8996_ufs_tx_tbl,
+       .tx_tbl_num             = ARRAY_SIZE(msm8996_ufs_tx_tbl),
+       .rx_tbl                 = msm8996_ufs_rx_tbl,
+       .rx_tbl_num             = ARRAY_SIZE(msm8996_ufs_rx_tbl),
+
+       .clk_list               = msm8996_ufs_phy_clk_l,
+       .num_clks               = ARRAY_SIZE(msm8996_ufs_phy_clk_l),
+
+       .vreg_list              = qmp_phy_vreg_l,
+       .num_vregs              = ARRAY_SIZE(qmp_phy_vreg_l),
+
+       .regs                   = msm8996_ufsphy_regs_layout,
+
+       .start_ctrl             = SERDES_START,
+       .pwrdn_ctrl             = SW_PWRDN,
+
+       .no_pcs_sw_reset        = true,
+};
+
 static const struct qmp_phy_cfg msm8996_usb3phy_cfg = {
        .type                   = PHY_TYPE_USB3,
        .nlanes                 = 1,
@@ -1234,6 +1582,64 @@ static const struct qmp_phy_cfg ipq8074_pciephy_cfg = {
        .pwrdn_delay_max        = 1005,         /* us */
 };
 
+static const struct qmp_phy_cfg sdm845_qmp_pciephy_cfg = {
+       .type = PHY_TYPE_PCIE,
+       .nlanes = 1,
+
+       .serdes_tbl             = sdm845_qmp_pcie_serdes_tbl,
+       .serdes_tbl_num         = ARRAY_SIZE(sdm845_qmp_pcie_serdes_tbl),
+       .tx_tbl                 = sdm845_qmp_pcie_tx_tbl,
+       .tx_tbl_num             = ARRAY_SIZE(sdm845_qmp_pcie_tx_tbl),
+       .rx_tbl                 = sdm845_qmp_pcie_rx_tbl,
+       .rx_tbl_num             = ARRAY_SIZE(sdm845_qmp_pcie_rx_tbl),
+       .pcs_tbl                = sdm845_qmp_pcie_pcs_tbl,
+       .pcs_tbl_num            = ARRAY_SIZE(sdm845_qmp_pcie_pcs_tbl),
+       .pcs_misc_tbl           = sdm845_qmp_pcie_pcs_misc_tbl,
+       .pcs_misc_tbl_num       = ARRAY_SIZE(sdm845_qmp_pcie_pcs_misc_tbl),
+       .clk_list               = sdm845_pciephy_clk_l,
+       .num_clks               = ARRAY_SIZE(sdm845_pciephy_clk_l),
+       .reset_list             = sdm845_pciephy_reset_l,
+       .num_resets             = ARRAY_SIZE(sdm845_pciephy_reset_l),
+       .vreg_list              = qmp_phy_vreg_l,
+       .num_vregs              = ARRAY_SIZE(qmp_phy_vreg_l),
+       .regs                   = sdm845_qmp_pciephy_regs_layout,
+
+       .start_ctrl             = PCS_START | SERDES_START,
+       .pwrdn_ctrl             = SW_PWRDN | REFCLK_DRV_DSBL,
+
+       .has_pwrdn_delay        = true,
+       .pwrdn_delay_min        = 995,          /* us */
+       .pwrdn_delay_max        = 1005,         /* us */
+};
+
+static const struct qmp_phy_cfg sdm845_qhp_pciephy_cfg = {
+       .type = PHY_TYPE_PCIE,
+       .nlanes = 1,
+
+       .serdes_tbl             = sdm845_qhp_pcie_serdes_tbl,
+       .serdes_tbl_num         = ARRAY_SIZE(sdm845_qhp_pcie_serdes_tbl),
+       .tx_tbl                 = sdm845_qhp_pcie_tx_tbl,
+       .tx_tbl_num             = ARRAY_SIZE(sdm845_qhp_pcie_tx_tbl),
+       .rx_tbl                 = sdm845_qhp_pcie_rx_tbl,
+       .rx_tbl_num             = ARRAY_SIZE(sdm845_qhp_pcie_rx_tbl),
+       .pcs_tbl                = sdm845_qhp_pcie_pcs_tbl,
+       .pcs_tbl_num            = ARRAY_SIZE(sdm845_qhp_pcie_pcs_tbl),
+       .clk_list               = sdm845_pciephy_clk_l,
+       .num_clks               = ARRAY_SIZE(sdm845_pciephy_clk_l),
+       .reset_list             = sdm845_pciephy_reset_l,
+       .num_resets             = ARRAY_SIZE(sdm845_pciephy_reset_l),
+       .vreg_list              = qmp_phy_vreg_l,
+       .num_vregs              = ARRAY_SIZE(qmp_phy_vreg_l),
+       .regs                   = sdm845_qhp_pciephy_regs_layout,
+
+       .start_ctrl             = PCS_START | SERDES_START,
+       .pwrdn_ctrl             = SW_PWRDN | REFCLK_DRV_DSBL,
+
+       .has_pwrdn_delay        = true,
+       .pwrdn_delay_min        = 995,          /* us */
+       .pwrdn_delay_max        = 1005,         /* us */
+};
+
 static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = {
        .type                   = PHY_TYPE_USB3,
        .nlanes                 = 1,
@@ -1563,6 +1969,7 @@ static int qcom_qmp_phy_enable(struct phy *phy)
        void __iomem *tx = qphy->tx;
        void __iomem *rx = qphy->rx;
        void __iomem *pcs = qphy->pcs;
+       void __iomem *pcs_misc = qphy->pcs_misc;
        void __iomem *dp_com = qmp->dp_com;
        void __iomem *status;
        unsigned int mask, val, ready;
@@ -1633,6 +2040,9 @@ static int qcom_qmp_phy_enable(struct phy *phy)
        if (ret)
                goto err_lane_rst;
 
+       qcom_qmp_phy_configure(pcs_misc, cfg->regs, cfg->pcs_misc_tbl,
+                              cfg->pcs_misc_tbl_num);
+
        /*
         * Pull out PHY from POWER DOWN state.
         * This is active low enable signal to power-down PHY.
@@ -1967,7 +2377,7 @@ static const struct phy_ops qcom_qmp_phy_gen_ops = {
        .owner          = THIS_MODULE,
 };
 
-static const struct phy_ops qcom_qmp_ufs_ops = {
+static const struct phy_ops qcom_qmp_pcie_ufs_ops = {
        .power_on       = qcom_qmp_phy_enable,
        .power_off      = qcom_qmp_phy_disable,
        .set_mode       = qcom_qmp_phy_set_mode,
@@ -2067,8 +2477,8 @@ int qcom_qmp_phy_create(struct device *dev, struct device_node *np, int id)
                }
        }
 
-       if (qmp->cfg->type == PHY_TYPE_UFS)
-               ops = &qcom_qmp_ufs_ops;
+       if (qmp->cfg->type == PHY_TYPE_UFS || qmp->cfg->type == PHY_TYPE_PCIE)
+               ops = &qcom_qmp_pcie_ufs_ops;
 
        generic_phy = devm_phy_create(dev, np, ops);
        if (IS_ERR(generic_phy)) {
@@ -2090,6 +2500,9 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
        {
                .compatible = "qcom,msm8996-qmp-pcie-phy",
                .data = &msm8996_pciephy_cfg,
+       }, {
+               .compatible = "qcom,msm8996-qmp-ufs-phy",
+               .data = &msm8996_ufs_cfg,
        }, {
                .compatible = "qcom,msm8996-qmp-usb3-phy",
                .data = &msm8996_usb3phy_cfg,
@@ -2102,6 +2515,12 @@ static const struct of_device_id qcom_qmp_phy_of_match_table[] = {
        }, {
                .compatible = "qcom,ipq8074-qmp-pcie-phy",
                .data = &ipq8074_pciephy_cfg,
+       }, {
+               .compatible = "qcom,sdm845-qhp-pcie-phy",
+               .data = &sdm845_qhp_pciephy_cfg,
+       }, {
+               .compatible = "qcom,sdm845-qmp-pcie-phy",
+               .data = &sdm845_qmp_pciephy_cfg,
        }, {
                .compatible = "qcom,sdm845-qmp-usb3-phy",
                .data = &qmp_v3_usb3phy_cfg,
index 90f793c2293d0b672f1028a0162683b84d03b4cc..dece0e67704ba241fc03d3fe5818fd7eb355cc6a 100644 (file)
 #define QPHY_V4_TX_MID_TERM_CTRL1                      0x1d8
 #define QPHY_V4_MULTI_LANE_CTRL1                       0x1e0
 
+/* PCIE GEN3 COM registers */
+#define PCIE_GEN3_QHP_COM_SSC_EN_CENTER                        0x14
+#define PCIE_GEN3_QHP_COM_SSC_PER1                     0x20
+#define PCIE_GEN3_QHP_COM_SSC_PER2                     0x24
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1               0x28
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2               0x2c
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE1_MODE1         0x34
+#define PCIE_GEN3_QHP_COM_SSC_STEP_SIZE2_MODE1         0x38
+#define PCIE_GEN3_QHP_COM_BIAS_EN_CKBUFLR_EN           0x54
+#define PCIE_GEN3_QHP_COM_CLK_ENABLE1                  0x58
+#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE0              0x6c
+#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE0              0x70
+#define PCIE_GEN3_QHP_COM_LOCK_CMP1_MODE1              0x78
+#define PCIE_GEN3_QHP_COM_LOCK_CMP2_MODE1              0x7c
+#define PCIE_GEN3_QHP_COM_BGV_TRIM                     0x98
+#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE0                        0xb4
+#define PCIE_GEN3_QHP_COM_CP_CTRL_MODE1                        0xb8
+#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE0              0xc0
+#define PCIE_GEN3_QHP_COM_PLL_RCTRL_MODE1              0xc4
+#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE0              0xcc
+#define PCIE_GEN3_QHP_COM_PLL_CCTRL_MODE1              0xd0
+#define PCIE_GEN3_QHP_COM_SYSCLK_EN_SEL                        0xdc
+#define PCIE_GEN3_QHP_COM_RESTRIM_CTRL2                        0xf0
+#define PCIE_GEN3_QHP_COM_LOCK_CMP_EN                  0xf8
+#define PCIE_GEN3_QHP_COM_DEC_START_MODE0              0x100
+#define PCIE_GEN3_QHP_COM_DEC_START_MODE1              0x108
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE0                0x11c
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE0                0x120
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE0                0x124
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START1_MODE1                0x128
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START2_MODE1                0x12c
+#define PCIE_GEN3_QHP_COM_DIV_FRAC_START3_MODE1                0x130
+#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE0                0x150
+#define PCIE_GEN3_QHP_COM_INTEGLOOP_GAIN0_MODE1                0x158
+#define PCIE_GEN3_QHP_COM_VCO_TUNE_MAP                 0x178
+#define PCIE_GEN3_QHP_COM_BG_CTRL                      0x1c8
+#define PCIE_GEN3_QHP_COM_CLK_SELECT                   0x1cc
+#define PCIE_GEN3_QHP_COM_HSCLK_SEL1                   0x1d0
+#define PCIE_GEN3_QHP_COM_CORECLK_DIV                  0x1e0
+#define PCIE_GEN3_QHP_COM_CORE_CLK_EN                  0x1e8
+#define PCIE_GEN3_QHP_COM_CMN_CONFIG                   0x1f0
+#define PCIE_GEN3_QHP_COM_SVS_MODE_CLK_SEL             0x1fc
+#define PCIE_GEN3_QHP_COM_CORECLK_DIV_MODE1            0x21c
+#define PCIE_GEN3_QHP_COM_CMN_MODE                     0x224
+#define PCIE_GEN3_QHP_COM_VREGCLK_DIV1                 0x228
+#define PCIE_GEN3_QHP_COM_VREGCLK_DIV2                 0x22c
+
+/* PCIE GEN3 QHP Lane registers */
+#define PCIE_GEN3_QHP_L0_DRVR_CTRL0                    0xc
+#define PCIE_GEN3_QHP_L0_DRVR_CTRL1                    0x10
+#define PCIE_GEN3_QHP_L0_DRVR_CTRL2                    0x14
+#define PCIE_GEN3_QHP_L0_DRVR_TAP_EN                   0x18
+#define PCIE_GEN3_QHP_L0_TX_BAND_MODE                  0x60
+#define PCIE_GEN3_QHP_L0_LANE_MODE                     0x64
+#define PCIE_GEN3_QHP_L0_PARALLEL_RATE                 0x7c
+#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE0                        0xc0
+#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE1                        0xc4
+#define PCIE_GEN3_QHP_L0_CML_CTRL_MODE2                        0xc8
+#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE1             0xd0
+#define PCIE_GEN3_QHP_L0_PREAMP_CTRL_MODE2             0xd4
+#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE0              0xd8
+#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE1              0xdc
+#define PCIE_GEN3_QHP_L0_MIXER_CTRL_MODE2              0xe0
+#define PCIE_GEN3_QHP_L0_CTLE_THRESH_DFE               0xfc
+#define PCIE_GEN3_QHP_L0_CGA_THRESH_DFE                        0x100
+#define PCIE_GEN3_QHP_L0_RXENGINE_EN0                  0x108
+#define PCIE_GEN3_QHP_L0_CTLE_TRAIN_TIME               0x114
+#define PCIE_GEN3_QHP_L0_CTLE_DFE_OVRLP_TIME           0x118
+#define PCIE_GEN3_QHP_L0_DFE_REFRESH_TIME              0x11c
+#define PCIE_GEN3_QHP_L0_DFE_ENABLE_TIME               0x120
+#define PCIE_GEN3_QHP_L0_VGA_GAIN                      0x124
+#define PCIE_GEN3_QHP_L0_DFE_GAIN                      0x128
+#define PCIE_GEN3_QHP_L0_EQ_GAIN                       0x130
+#define PCIE_GEN3_QHP_L0_OFFSET_GAIN                   0x134
+#define PCIE_GEN3_QHP_L0_PRE_GAIN                      0x138
+#define PCIE_GEN3_QHP_L0_VGA_INITVAL                   0x13c
+#define PCIE_GEN3_QHP_L0_EQ_INTVAL                     0x154
+#define PCIE_GEN3_QHP_L0_EDAC_INITVAL                  0x160
+#define PCIE_GEN3_QHP_L0_RXEQ_INITB0                   0x168
+#define PCIE_GEN3_QHP_L0_RXEQ_INITB1                   0x16c
+#define PCIE_GEN3_QHP_L0_RCVRDONE_THRESH1              0x178
+#define PCIE_GEN3_QHP_L0_RXEQ_CTRL                     0x180
+#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE0            0x184
+#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE1            0x188
+#define PCIE_GEN3_QHP_L0_UCDR_FO_GAIN_MODE2            0x18c
+#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE0            0x190
+#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE1            0x194
+#define PCIE_GEN3_QHP_L0_UCDR_SO_GAIN_MODE2            0x198
+#define PCIE_GEN3_QHP_L0_UCDR_SO_CONFIG                        0x19c
+#define PCIE_GEN3_QHP_L0_RX_BAND                       0x1a4
+#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE0           0x1c0
+#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE1           0x1c4
+#define PCIE_GEN3_QHP_L0_RX_RCVR_PATH1_MODE2           0x1c8
+#define PCIE_GEN3_QHP_L0_SIGDET_ENABLES                        0x230
+#define PCIE_GEN3_QHP_L0_SIGDET_CNTRL                  0x234
+#define PCIE_GEN3_QHP_L0_SIGDET_DEGLITCH_CNTRL         0x238
+#define PCIE_GEN3_QHP_L0_DCC_GAIN                      0x2a4
+#define PCIE_GEN3_QHP_L0_RSM_START                     0x2a8
+#define PCIE_GEN3_QHP_L0_RX_EN_SIGNAL                  0x2ac
+#define PCIE_GEN3_QHP_L0_PSM_RX_EN_CAL                 0x2b0
+#define PCIE_GEN3_QHP_L0_RX_MISC_CNTRL0                        0x2b8
+#define PCIE_GEN3_QHP_L0_TS0_TIMER                     0x2c0
+#define PCIE_GEN3_QHP_L0_DLL_HIGHDATARATE              0x2c4
+#define PCIE_GEN3_QHP_L0_RX_RESETCODE_OFFSET           0x2cc
+
+/* PCIE GEN3 PCS registers */
+#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M3P5DB         0x2c
+#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M3P5DB         0x40
+#define PCIE_GEN3_QHP_PHY_TXMGN_MAIN_V0_M6DB           0x54
+#define PCIE_GEN3_QHP_PHY_TXMGN_POST_V0_M6DB           0x68
+#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG           0x15c
+#define PCIE_GEN3_QHP_PHY_POWER_STATE_CONFIG5          0x16c
+#define PCIE_GEN3_QHP_PHY_PCS_TX_RX_CONFIG             0x174
+
 #endif
index bf94a52d308715955f5703365b5a776d51658081..3708d43b75085a29d1809e352a6bbda6b484e702 100644 (file)
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
- * Copyright (c) 2017, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2017, 2019, The Linux Foundation. All rights reserved.
  */
 
 #include <linux/clk.h>
 #define IMP_RES_OFFSET_MASK                    GENMASK(5, 0)
 #define IMP_RES_OFFSET_SHIFT                   0x0
 
+/* QUSB2PHY_PLL_BIAS_CONTROL_2 register bits */
+#define BIAS_CTRL2_RES_OFFSET_MASK             GENMASK(5, 0)
+#define BIAS_CTRL2_RES_OFFSET_SHIFT            0x0
+
+/* QUSB2PHY_CHG_CONTROL_2 register bits */
+#define CHG_CTRL2_OFFSET_MASK                  GENMASK(5, 4)
+#define CHG_CTRL2_OFFSET_SHIFT                 0x4
+
 /* QUSB2PHY_PORT_TUNE1 register bits */
 #define HSTX_TRIM_MASK                         GENMASK(7, 4)
 #define HSTX_TRIM_SHIFT                                0x4
 #define PREEMPHASIS_EN_MASK                    GENMASK(1, 0)
 #define PREEMPHASIS_EN_SHIFT                   0x0
 
+/* QUSB2PHY_PORT_TUNE2 register bits */
+#define HSDISC_TRIM_MASK                       GENMASK(1, 0)
+#define HSDISC_TRIM_SHIFT                      0x0
+
 #define QUSB2PHY_PLL_ANALOG_CONTROLS_TWO       0x04
 #define QUSB2PHY_PLL_CLOCK_INVERTERS           0x18c
 #define QUSB2PHY_PLL_CMODE                     0x2c
@@ -177,7 +189,7 @@ static const struct qusb2_phy_init_tbl msm8998_init_tbl[] = {
        QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_DIGITAL_TIMERS_TWO, 0x19),
 };
 
-static const unsigned int sdm845_regs_layout[] = {
+static const unsigned int qusb2_v2_regs_layout[] = {
        [QUSB2PHY_PLL_CORE_INPUT_OVERRIDE] = 0xa8,
        [QUSB2PHY_PLL_STATUS]           = 0x1a0,
        [QUSB2PHY_PORT_TUNE1]           = 0x240,
@@ -191,7 +203,7 @@ static const unsigned int sdm845_regs_layout[] = {
        [QUSB2PHY_INTR_CTRL]            = 0x230,
 };
 
-static const struct qusb2_phy_init_tbl sdm845_init_tbl[] = {
+static const struct qusb2_phy_init_tbl qusb2_v2_init_tbl[] = {
        QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_ANALOG_CONTROLS_TWO, 0x03),
        QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CLOCK_INVERTERS, 0x7c),
        QUSB2_PHY_INIT_CFG(QUSB2PHY_PLL_CMODE, 0x80),
@@ -258,10 +270,10 @@ static const struct qusb2_phy_cfg msm8998_phy_cfg = {
        .update_tune1_with_efuse = true,
 };
 
-static const struct qusb2_phy_cfg sdm845_phy_cfg = {
-       .tbl            = sdm845_init_tbl,
-       .tbl_num        = ARRAY_SIZE(sdm845_init_tbl),
-       .regs           = sdm845_regs_layout,
+static const struct qusb2_phy_cfg qusb2_v2_phy_cfg = {
+       .tbl            = qusb2_v2_init_tbl,
+       .tbl_num        = ARRAY_SIZE(qusb2_v2_init_tbl),
+       .regs           = qusb2_v2_regs_layout,
 
        .disable_ctrl   = (PWR_CTRL1_VREF_SUPPLY_TRIM | PWR_CTRL1_CLAMP_N_EN |
                           POWER_DOWN),
@@ -277,6 +289,34 @@ static const char * const qusb2_phy_vreg_names[] = {
 
 #define QUSB2_NUM_VREGS                ARRAY_SIZE(qusb2_phy_vreg_names)
 
+/* struct override_param - structure holding qusb2 v2 phy overriding param
+ * set override true if the  device tree property exists and read and assign
+ * to value
+ */
+struct override_param {
+       bool override;
+       u8 value;
+};
+
+/*struct override_params - structure holding qusb2 v2 phy overriding params
+ * @imp_res_offset: rescode offset to be updated in IMP_CTRL1 register
+ * @hstx_trim: HSTX_TRIM to be updated in TUNE1 register
+ * @preemphasis: Amplitude Pre-Emphasis to be updated in TUNE1 register
+ * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1
+ * @bias_ctrl: bias ctrl to be updated in BIAS_CONTROL_2 register
+ * @charge_ctrl: charge ctrl to be updated in CHG_CTRL2 register
+ * @hsdisc_trim: disconnect threshold to be updated in TUNE2 register
+ */
+struct override_params {
+       struct override_param imp_res_offset;
+       struct override_param hstx_trim;
+       struct override_param preemphasis;
+       struct override_param preemphasis_width;
+       struct override_param bias_ctrl;
+       struct override_param charge_ctrl;
+       struct override_param hsdisc_trim;
+};
+
 /**
  * struct qusb2_phy - structure holding qusb2 phy attributes
  *
@@ -292,14 +332,7 @@ static const char * const qusb2_phy_vreg_names[] = {
  * @tcsr: TCSR syscon register map
  * @cell: nvmem cell containing phy tuning value
  *
- * @override_imp_res_offset: PHY should use different rescode offset
- * @imp_res_offset_value: rescode offset to be updated in IMP_CTRL1 register
- * @override_hstx_trim: PHY should use different HSTX o/p current value
- * @hstx_trim_value: HSTX_TRIM value to be updated in TUNE1 register
- * @override_preemphasis: PHY should use different pre-amphasis amplitude
- * @preemphasis_level: Amplitude Pre-Emphasis to be updated in TUNE1 register
- * @override_preemphasis_width: PHY should use different pre-emphasis duration
- * @preemphasis_width: half/full-width Pre-Emphasis updated via TUNE1
+ * @overrides: pointer to structure for all overriding tuning params
  *
  * @cfg: phy config data
  * @has_se_clk_scheme: indicate if PHY has single-ended ref clock scheme
@@ -319,14 +352,7 @@ struct qusb2_phy {
        struct regmap *tcsr;
        struct nvmem_cell *cell;
 
-       bool override_imp_res_offset;
-       u8 imp_res_offset_value;
-       bool override_hstx_trim;
-       u8 hstx_trim_value;
-       bool override_preemphasis;
-       u8 preemphasis_level;
-       bool override_preemphasis_width;
-       u8 preemphasis_width;
+       struct override_params overrides;
 
        const struct qusb2_phy_cfg *cfg;
        bool has_se_clk_scheme;
@@ -394,24 +420,35 @@ void qcom_qusb2_phy_configure(void __iomem *base,
 static void qusb2_phy_override_phy_params(struct qusb2_phy *qphy)
 {
        const struct qusb2_phy_cfg *cfg = qphy->cfg;
+       struct override_params *or = &qphy->overrides;
 
-       if (qphy->override_imp_res_offset)
+       if (or->imp_res_offset.override)
                qusb2_write_mask(qphy->base, QUSB2PHY_IMP_CTRL1,
-                            qphy->imp_res_offset_value << IMP_RES_OFFSET_SHIFT,
+               or->imp_res_offset.value << IMP_RES_OFFSET_SHIFT,
                             IMP_RES_OFFSET_MASK);
 
-       if (qphy->override_hstx_trim)
+       if (or->bias_ctrl.override)
+               qusb2_write_mask(qphy->base, QUSB2PHY_PLL_BIAS_CONTROL_2,
+               or->bias_ctrl.value << BIAS_CTRL2_RES_OFFSET_SHIFT,
+                          BIAS_CTRL2_RES_OFFSET_MASK);
+
+       if (or->charge_ctrl.override)
+               qusb2_write_mask(qphy->base, QUSB2PHY_CHG_CTRL2,
+               or->charge_ctrl.value << CHG_CTRL2_OFFSET_SHIFT,
+                            CHG_CTRL2_OFFSET_MASK);
+
+       if (or->hstx_trim.override)
                qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
-                                qphy->hstx_trim_value << HSTX_TRIM_SHIFT,
+               or->hstx_trim.value << HSTX_TRIM_SHIFT,
                                 HSTX_TRIM_MASK);
 
-       if (qphy->override_preemphasis)
+       if (or->preemphasis.override)
                qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE1],
-                               qphy->preemphasis_level << PREEMPHASIS_EN_SHIFT,
+               or->preemphasis.value << PREEMPHASIS_EN_SHIFT,
                                PREEMPHASIS_EN_MASK);
 
-       if (qphy->override_preemphasis_width) {
-               if (qphy->preemphasis_width ==
+       if (or->preemphasis_width.override) {
+               if (or->preemphasis_width.value ==
                    QUSB2_V2_PREEMPHASIS_WIDTH_HALF_BIT)
                        qusb2_setbits(qphy->base,
                                      cfg->regs[QUSB2PHY_PORT_TUNE1],
@@ -421,6 +458,11 @@ static void qusb2_phy_override_phy_params(struct qusb2_phy *qphy)
                                      cfg->regs[QUSB2PHY_PORT_TUNE1],
                                      PREEMPH_WIDTH_HALF_BIT);
        }
+
+       if (or->hsdisc_trim.override)
+               qusb2_write_mask(qphy->base, cfg->regs[QUSB2PHY_PORT_TUNE2],
+               or->hsdisc_trim.value << HSDISC_TRIM_SHIFT,
+                                HSDISC_TRIM_MASK);
 }
 
 /*
@@ -774,8 +816,8 @@ static const struct of_device_id qusb2_phy_of_match_table[] = {
                .compatible     = "qcom,msm8998-qusb2-phy",
                .data           = &msm8998_phy_cfg,
        }, {
-               .compatible     = "qcom,sdm845-qusb2-phy",
-               .data           = &sdm845_phy_cfg,
+               .compatible     = "qcom,qusb2-v2-phy",
+               .data           = &qusb2_v2_phy_cfg,
        },
        { },
 };
@@ -796,10 +838,12 @@ static int qusb2_phy_probe(struct platform_device *pdev)
        int ret, i;
        int num;
        u32 value;
+       struct override_params *or;
 
        qphy = devm_kzalloc(dev, sizeof(*qphy), GFP_KERNEL);
        if (!qphy)
                return -ENOMEM;
+       or = &qphy->overrides;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        qphy->base = devm_ioremap_resource(dev, res);
@@ -864,26 +908,44 @@ static int qusb2_phy_probe(struct platform_device *pdev)
 
        if (!of_property_read_u32(dev->of_node, "qcom,imp-res-offset-value",
                                  &value)) {
-               qphy->imp_res_offset_value = (u8)value;
-               qphy->override_imp_res_offset = true;
+               or->imp_res_offset.value = (u8)value;
+               or->imp_res_offset.override = true;
+       }
+
+       if (!of_property_read_u32(dev->of_node, "qcom,bias-ctrl-value",
+                                 &value)) {
+               or->bias_ctrl.value = (u8)value;
+               or->bias_ctrl.override = true;
+       }
+
+       if (!of_property_read_u32(dev->of_node, "qcom,charge-ctrl-value",
+                                 &value)) {
+               or->charge_ctrl.value = (u8)value;
+               or->charge_ctrl.override = true;
        }
 
        if (!of_property_read_u32(dev->of_node, "qcom,hstx-trim-value",
                                  &value)) {
-               qphy->hstx_trim_value = (u8)value;
-               qphy->override_hstx_trim = true;
+               or->hstx_trim.value = (u8)value;
+               or->hstx_trim.override = true;
        }
 
        if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-level",
                                     &value)) {
-               qphy->preemphasis_level = (u8)value;
-               qphy->override_preemphasis = true;
+               or->preemphasis.value = (u8)value;
+               or->preemphasis.override = true;
        }
 
        if (!of_property_read_u32(dev->of_node, "qcom,preemphasis-width",
                                     &value)) {
-               qphy->preemphasis_width = (u8)value;
-               qphy->override_preemphasis_width = true;
+               or->preemphasis_width.value = (u8)value;
+               or->preemphasis_width.override = true;
+       }
+
+       if (!of_property_read_u32(dev->of_node, "qcom,hsdisc-trim-value",
+                                 &value)) {
+               or->hsdisc_trim.value = (u8)value;
+               or->hsdisc_trim.override = true;
        }
 
        pm_runtime_set_active(dev);
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c b/drivers/phy/qualcomm/phy-qcom-usb-hs-28nm.c
new file mode 100644 (file)
index 0000000..d998e65
--- /dev/null
@@ -0,0 +1,415 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2009-2018, Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, Linaro Limited
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_graph.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+/* PHY register and bit definitions */
+#define PHY_CTRL_COMMON0               0x078
+#define SIDDQ                          BIT(2)
+#define PHY_IRQ_CMD                    0x0d0
+#define PHY_INTR_MASK0                 0x0d4
+#define PHY_INTR_CLEAR0                        0x0dc
+#define DPDM_MASK                      0x1e
+#define DP_1_0                         BIT(4)
+#define DP_0_1                         BIT(3)
+#define DM_1_0                         BIT(2)
+#define DM_0_1                         BIT(1)
+
+enum hsphy_voltage {
+       VOL_NONE,
+       VOL_MIN,
+       VOL_MAX,
+       VOL_NUM,
+};
+
+enum hsphy_vreg {
+       VDD,
+       VDDA_1P8,
+       VDDA_3P3,
+       VREG_NUM,
+};
+
+struct hsphy_init_seq {
+       int offset;
+       int val;
+       int delay;
+};
+
+struct hsphy_data {
+       const struct hsphy_init_seq *init_seq;
+       unsigned int init_seq_num;
+};
+
+struct hsphy_priv {
+       void __iomem *base;
+       struct clk_bulk_data *clks;
+       int num_clks;
+       struct reset_control *phy_reset;
+       struct reset_control *por_reset;
+       struct regulator_bulk_data vregs[VREG_NUM];
+       const struct hsphy_data *data;
+       enum phy_mode mode;
+};
+
+static int qcom_snps_hsphy_set_mode(struct phy *phy, enum phy_mode mode,
+                                   int submode)
+{
+       struct hsphy_priv *priv = phy_get_drvdata(phy);
+
+       priv->mode = PHY_MODE_INVALID;
+
+       if (mode > 0)
+               priv->mode = mode;
+
+       return 0;
+}
+
+static void qcom_snps_hsphy_enable_hv_interrupts(struct hsphy_priv *priv)
+{
+       u32 val;
+
+       /* Clear any existing interrupts before enabling the interrupts */
+       val = readb(priv->base + PHY_INTR_CLEAR0);
+       val |= DPDM_MASK;
+       writeb(val, priv->base + PHY_INTR_CLEAR0);
+
+       writeb(0x0, priv->base + PHY_IRQ_CMD);
+       usleep_range(200, 220);
+       writeb(0x1, priv->base + PHY_IRQ_CMD);
+
+       /* Make sure the interrupts are cleared */
+       usleep_range(200, 220);
+
+       val = readb(priv->base + PHY_INTR_MASK0);
+       switch (priv->mode) {
+       case PHY_MODE_USB_HOST_HS:
+       case PHY_MODE_USB_HOST_FS:
+       case PHY_MODE_USB_DEVICE_HS:
+       case PHY_MODE_USB_DEVICE_FS:
+               val |= DP_1_0 | DM_0_1;
+               break;
+       case PHY_MODE_USB_HOST_LS:
+       case PHY_MODE_USB_DEVICE_LS:
+               val |= DP_0_1 | DM_1_0;
+               break;
+       default:
+               /* No device connected */
+               val |= DP_0_1 | DM_0_1;
+               break;
+       }
+       writeb(val, priv->base + PHY_INTR_MASK0);
+}
+
+static void qcom_snps_hsphy_disable_hv_interrupts(struct hsphy_priv *priv)
+{
+       u32 val;
+
+       val = readb(priv->base + PHY_INTR_MASK0);
+       val &= ~DPDM_MASK;
+       writeb(val, priv->base + PHY_INTR_MASK0);
+
+       /* Clear any pending interrupts */
+       val = readb(priv->base + PHY_INTR_CLEAR0);
+       val |= DPDM_MASK;
+       writeb(val, priv->base + PHY_INTR_CLEAR0);
+
+       writeb(0x0, priv->base + PHY_IRQ_CMD);
+       usleep_range(200, 220);
+
+       writeb(0x1, priv->base + PHY_IRQ_CMD);
+       usleep_range(200, 220);
+}
+
+static void qcom_snps_hsphy_enter_retention(struct hsphy_priv *priv)
+{
+       u32 val;
+
+       val = readb(priv->base + PHY_CTRL_COMMON0);
+       val |= SIDDQ;
+       writeb(val, priv->base + PHY_CTRL_COMMON0);
+}
+
+static void qcom_snps_hsphy_exit_retention(struct hsphy_priv *priv)
+{
+       u32 val;
+
+       val = readb(priv->base + PHY_CTRL_COMMON0);
+       val &= ~SIDDQ;
+       writeb(val, priv->base + PHY_CTRL_COMMON0);
+}
+
+static int qcom_snps_hsphy_power_on(struct phy *phy)
+{
+       struct hsphy_priv *priv = phy_get_drvdata(phy);
+       int ret;
+
+       ret = regulator_bulk_enable(VREG_NUM, priv->vregs);
+       if (ret)
+               return ret;
+       ret = clk_bulk_prepare_enable(priv->num_clks, priv->clks);
+       if (ret)
+               goto err_disable_regulator;
+       qcom_snps_hsphy_disable_hv_interrupts(priv);
+       qcom_snps_hsphy_exit_retention(priv);
+
+       return 0;
+
+err_disable_regulator:
+       regulator_bulk_disable(VREG_NUM, priv->vregs);
+
+       return ret;
+}
+
+static int qcom_snps_hsphy_power_off(struct phy *phy)
+{
+       struct hsphy_priv *priv = phy_get_drvdata(phy);
+
+       qcom_snps_hsphy_enter_retention(priv);
+       qcom_snps_hsphy_enable_hv_interrupts(priv);
+       clk_bulk_disable_unprepare(priv->num_clks, priv->clks);
+       regulator_bulk_disable(VREG_NUM, priv->vregs);
+
+       return 0;
+}
+
+static int qcom_snps_hsphy_reset(struct hsphy_priv *priv)
+{
+       int ret;
+
+       ret = reset_control_assert(priv->phy_reset);
+       if (ret)
+               return ret;
+
+       usleep_range(10, 15);
+
+       ret = reset_control_deassert(priv->phy_reset);
+       if (ret)
+               return ret;
+
+       usleep_range(80, 100);
+
+       return 0;
+}
+
+static void qcom_snps_hsphy_init_sequence(struct hsphy_priv *priv)
+{
+       const struct hsphy_data *data = priv->data;
+       const struct hsphy_init_seq *seq;
+       int i;
+
+       /* Device match data is optional. */
+       if (!data)
+               return;
+
+       seq = data->init_seq;
+
+       for (i = 0; i < data->init_seq_num; i++, seq++) {
+               writeb(seq->val, priv->base + seq->offset);
+               if (seq->delay)
+                       usleep_range(seq->delay, seq->delay + 10);
+       }
+}
+
+static int qcom_snps_hsphy_por_reset(struct hsphy_priv *priv)
+{
+       int ret;
+
+       ret = reset_control_assert(priv->por_reset);
+       if (ret)
+               return ret;
+
+       /*
+        * The Femto PHY is POR reset in the following scenarios.
+        *
+        * 1. After overriding the parameter registers.
+        * 2. Low power mode exit from PHY retention.
+        *
+        * Ensure that SIDDQ is cleared before bringing the PHY
+        * out of reset.
+        */
+       qcom_snps_hsphy_exit_retention(priv);
+
+       /*
+        * As per databook, 10 usec delay is required between
+        * PHY POR assert and de-assert.
+        */
+       usleep_range(10, 20);
+       ret = reset_control_deassert(priv->por_reset);
+       if (ret)
+               return ret;
+
+       /*
+        * As per databook, it takes 75 usec for PHY to stabilize
+        * after the reset.
+        */
+       usleep_range(80, 100);
+
+       return 0;
+}
+
+static int qcom_snps_hsphy_init(struct phy *phy)
+{
+       struct hsphy_priv *priv = phy_get_drvdata(phy);
+       int ret;
+
+       ret = qcom_snps_hsphy_reset(priv);
+       if (ret)
+               return ret;
+
+       qcom_snps_hsphy_init_sequence(priv);
+
+       ret = qcom_snps_hsphy_por_reset(priv);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static const struct phy_ops qcom_snps_hsphy_ops = {
+       .init = qcom_snps_hsphy_init,
+       .power_on = qcom_snps_hsphy_power_on,
+       .power_off = qcom_snps_hsphy_power_off,
+       .set_mode = qcom_snps_hsphy_set_mode,
+       .owner = THIS_MODULE,
+};
+
+static const char * const qcom_snps_hsphy_clks[] = {
+       "ref",
+       "ahb",
+       "sleep",
+};
+
+static int qcom_snps_hsphy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy_provider *provider;
+       struct hsphy_priv *priv;
+       struct phy *phy;
+       int ret;
+       int i;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       priv->num_clks = ARRAY_SIZE(qcom_snps_hsphy_clks);
+       priv->clks = devm_kcalloc(dev, priv->num_clks, sizeof(*priv->clks),
+                                 GFP_KERNEL);
+       if (!priv->clks)
+               return -ENOMEM;
+
+       for (i = 0; i < priv->num_clks; i++)
+               priv->clks[i].id = qcom_snps_hsphy_clks[i];
+
+       ret = devm_clk_bulk_get(dev, priv->num_clks, priv->clks);
+       if (ret)
+               return ret;
+
+       priv->phy_reset = devm_reset_control_get_exclusive(dev, "phy");
+       if (IS_ERR(priv->phy_reset))
+               return PTR_ERR(priv->phy_reset);
+
+       priv->por_reset = devm_reset_control_get_exclusive(dev, "por");
+       if (IS_ERR(priv->por_reset))
+               return PTR_ERR(priv->por_reset);
+
+       priv->vregs[VDD].supply = "vdd";
+       priv->vregs[VDDA_1P8].supply = "vdda1p8";
+       priv->vregs[VDDA_3P3].supply = "vdda3p3";
+
+       ret = devm_regulator_bulk_get(dev, VREG_NUM, priv->vregs);
+       if (ret)
+               return ret;
+
+       /* Get device match data */
+       priv->data = device_get_match_data(dev);
+
+       phy = devm_phy_create(dev, dev->of_node, &qcom_snps_hsphy_ops);
+       if (IS_ERR(phy))
+               return PTR_ERR(phy);
+
+       phy_set_drvdata(phy, priv);
+
+       provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(provider))
+               return PTR_ERR(provider);
+
+       ret = regulator_set_load(priv->vregs[VDDA_1P8].consumer, 19000);
+       if (ret < 0)
+               return ret;
+
+       ret = regulator_set_load(priv->vregs[VDDA_3P3].consumer, 16000);
+       if (ret < 0)
+               goto unset_1p8_load;
+
+       return 0;
+
+unset_1p8_load:
+       regulator_set_load(priv->vregs[VDDA_1P8].consumer, 0);
+
+       return ret;
+}
+
+/*
+ * The macro is used to define an initialization sequence.  Each tuple
+ * is meant to program 'value' into phy register at 'offset' with 'delay'
+ * in us followed.
+ */
+#define HSPHY_INIT_CFG(o, v, d)        { .offset = o, .val = v, .delay = d, }
+
+static const struct hsphy_init_seq init_seq_femtophy[] = {
+       HSPHY_INIT_CFG(0xc0, 0x01, 0),
+       HSPHY_INIT_CFG(0xe8, 0x0d, 0),
+       HSPHY_INIT_CFG(0x74, 0x12, 0),
+       HSPHY_INIT_CFG(0x98, 0x63, 0),
+       HSPHY_INIT_CFG(0x9c, 0x03, 0),
+       HSPHY_INIT_CFG(0xa0, 0x1d, 0),
+       HSPHY_INIT_CFG(0xa4, 0x03, 0),
+       HSPHY_INIT_CFG(0x8c, 0x23, 0),
+       HSPHY_INIT_CFG(0x78, 0x08, 0),
+       HSPHY_INIT_CFG(0x7c, 0xdc, 0),
+       HSPHY_INIT_CFG(0x90, 0xe0, 20),
+       HSPHY_INIT_CFG(0x74, 0x10, 0),
+       HSPHY_INIT_CFG(0x90, 0x60, 0),
+};
+
+static const struct hsphy_data hsphy_data_femtophy = {
+       .init_seq = init_seq_femtophy,
+       .init_seq_num = ARRAY_SIZE(init_seq_femtophy),
+};
+
+static const struct of_device_id qcom_snps_hsphy_match[] = {
+       { .compatible = "qcom,usb-hs-28nm-femtophy", .data = &hsphy_data_femtophy, },
+       { },
+};
+MODULE_DEVICE_TABLE(of, qcom_snps_hsphy_match);
+
+static struct platform_driver qcom_snps_hsphy_driver = {
+       .probe = qcom_snps_hsphy_probe,
+       .driver = {
+               .name = "qcom,usb-hs-28nm-phy",
+               .of_match_table = qcom_snps_hsphy_match,
+       },
+};
+module_platform_driver(qcom_snps_hsphy_driver);
+
+MODULE_DESCRIPTION("Qualcomm 28nm Hi-Speed USB PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/qualcomm/phy-qcom-usb-ss.c b/drivers/phy/qualcomm/phy-qcom-usb-ss.c
new file mode 100644 (file)
index 0000000..a3a6d3c
--- /dev/null
@@ -0,0 +1,246 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2012-2014,2017 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2018-2020, Linaro Limited
+ */
+
+#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/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+
+#define PHY_CTRL0                      0x6C
+#define PHY_CTRL1                      0x70
+#define PHY_CTRL2                      0x74
+#define PHY_CTRL4                      0x7C
+
+/* PHY_CTRL bits */
+#define REF_PHY_EN                     BIT(0)
+#define LANE0_PWR_ON                   BIT(2)
+#define SWI_PCS_CLK_SEL                        BIT(4)
+#define TST_PWR_DOWN                   BIT(4)
+#define PHY_RESET                      BIT(7)
+
+#define NUM_BULK_CLKS                  3
+#define NUM_BULK_REGS                  2
+
+struct ssphy_priv {
+       void __iomem *base;
+       struct device *dev;
+       struct reset_control *reset_com;
+       struct reset_control *reset_phy;
+       struct regulator_bulk_data regs[NUM_BULK_REGS];
+       struct clk_bulk_data clks[NUM_BULK_CLKS];
+       enum phy_mode mode;
+};
+
+static inline void qcom_ssphy_updatel(void __iomem *addr, u32 mask, u32 val)
+{
+       writel((readl(addr) & ~mask) | val, addr);
+}
+
+static int qcom_ssphy_do_reset(struct ssphy_priv *priv)
+{
+       int ret;
+
+       if (!priv->reset_com) {
+               qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET,
+                                  PHY_RESET);
+               usleep_range(10, 20);
+               qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET, 0);
+       } else {
+               ret = reset_control_assert(priv->reset_com);
+               if (ret) {
+                       dev_err(priv->dev, "Failed to assert reset com\n");
+                       return ret;
+               }
+
+               ret = reset_control_assert(priv->reset_phy);
+               if (ret) {
+                       dev_err(priv->dev, "Failed to assert reset phy\n");
+                       return ret;
+               }
+
+               usleep_range(10, 20);
+
+               ret = reset_control_deassert(priv->reset_com);
+               if (ret) {
+                       dev_err(priv->dev, "Failed to deassert reset com\n");
+                       return ret;
+               }
+
+               ret = reset_control_deassert(priv->reset_phy);
+               if (ret) {
+                       dev_err(priv->dev, "Failed to deassert reset phy\n");
+                       return ret;
+               }
+       }
+
+       return 0;
+}
+
+static int qcom_ssphy_power_on(struct phy *phy)
+{
+       struct ssphy_priv *priv = phy_get_drvdata(phy);
+       int ret;
+
+       ret = regulator_bulk_enable(NUM_BULK_REGS, priv->regs);
+       if (ret)
+               return ret;
+
+       ret = clk_bulk_prepare_enable(NUM_BULK_CLKS, priv->clks);
+       if (ret)
+               goto err_disable_regulator;
+
+       ret = qcom_ssphy_do_reset(priv);
+       if (ret)
+               goto err_disable_clock;
+
+       writeb(SWI_PCS_CLK_SEL, priv->base + PHY_CTRL0);
+       qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, LANE0_PWR_ON);
+       qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, REF_PHY_EN);
+       qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, 0);
+
+       return 0;
+err_disable_clock:
+       clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
+err_disable_regulator:
+       regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
+
+       return ret;
+}
+
+static int qcom_ssphy_power_off(struct phy *phy)
+{
+       struct ssphy_priv *priv = phy_get_drvdata(phy);
+
+       qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, 0);
+       qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, 0);
+       qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, TST_PWR_DOWN);
+
+       clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
+       regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
+
+       return 0;
+}
+
+static int qcom_ssphy_init_clock(struct ssphy_priv *priv)
+{
+       priv->clks[0].id = "ref";
+       priv->clks[1].id = "ahb";
+       priv->clks[2].id = "pipe";
+
+       return devm_clk_bulk_get(priv->dev, NUM_BULK_CLKS, priv->clks);
+}
+
+static int qcom_ssphy_init_regulator(struct ssphy_priv *priv)
+{
+       int ret;
+
+       priv->regs[0].supply = "vdd";
+       priv->regs[1].supply = "vdda1p8";
+       ret = devm_regulator_bulk_get(priv->dev, NUM_BULK_REGS, priv->regs);
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(priv->dev, "Failed to get regulators\n");
+               return ret;
+       }
+
+       return ret;
+}
+
+static int qcom_ssphy_init_reset(struct ssphy_priv *priv)
+{
+       priv->reset_com = devm_reset_control_get_optional_exclusive(priv->dev, "com");
+       if (IS_ERR(priv->reset_com)) {
+               dev_err(priv->dev, "Failed to get reset control com\n");
+               return PTR_ERR(priv->reset_com);
+       }
+
+       if (priv->reset_com) {
+               /* if reset_com is present, reset_phy is no longer optional */
+               priv->reset_phy = devm_reset_control_get_exclusive(priv->dev, "phy");
+               if (IS_ERR(priv->reset_phy)) {
+                       dev_err(priv->dev, "Failed to get reset control phy\n");
+                       return PTR_ERR(priv->reset_phy);
+               }
+       }
+
+       return 0;
+}
+
+static const struct phy_ops qcom_ssphy_ops = {
+       .power_off = qcom_ssphy_power_off,
+       .power_on = qcom_ssphy_power_on,
+       .owner = THIS_MODULE,
+};
+
+static int qcom_ssphy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy_provider *provider;
+       struct ssphy_priv *priv;
+       struct phy *phy;
+       int ret;
+
+       priv = devm_kzalloc(dev, sizeof(struct ssphy_priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->dev = dev;
+       priv->mode = PHY_MODE_INVALID;
+
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       ret = qcom_ssphy_init_clock(priv);
+       if (ret)
+               return ret;
+
+       ret = qcom_ssphy_init_reset(priv);
+       if (ret)
+               return ret;
+
+       ret = qcom_ssphy_init_regulator(priv);
+       if (ret)
+               return ret;
+
+       phy = devm_phy_create(dev, dev->of_node, &qcom_ssphy_ops);
+       if (IS_ERR(phy)) {
+               dev_err(dev, "Failed to create the SS phy\n");
+               return PTR_ERR(phy);
+       }
+
+       phy_set_drvdata(phy, priv);
+
+       provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id qcom_ssphy_match[] = {
+       { .compatible = "qcom,usb-ss-28nm-phy", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, qcom_ssphy_match);
+
+static struct platform_driver qcom_ssphy_driver = {
+       .probe          = qcom_ssphy_probe,
+       .driver = {
+               .name   = "qcom-usb-ssphy",
+               .of_match_table = qcom_ssphy_match,
+       },
+};
+module_platform_driver(qcom_ssphy_driver);
+
+MODULE_DESCRIPTION("Qualcomm SuperSpeed USB PHY driver");
+MODULE_LICENSE("GPL v2");
index 680cc0c8825c801c5f6f80b7a7e7b316534f4d67..a84e9f027fc4692b17ccee5ace4285e0ede991be 100644 (file)
@@ -763,7 +763,7 @@ static void rockchip_chg_detect_work(struct work_struct *work)
                /* put the controller in normal mode */
                property_enable(base, &rphy->phy_cfg->chg_det.opmode, true);
                rockchip_usb2phy_otg_sm_work(&rport->otg_sm_work.work);
-               dev_info(&rport->phy->dev, "charger = %s\n",
+               dev_dbg(&rport->phy->dev, "charger = %s\n",
                         chg_to_string(rphy->chg_type));
                return;
        default:
index 93ffbd2940fa4c9da7c115f67266cf6325852e74..e4adab375c73706cdc510327939a25e381209822 100644 (file)
 #include <linux/resource.h>
 
 /* PHY */
+#define PCL_PHY_CLKCTRL                0x0000
+#define PORT_SEL_MASK          GENMASK(11, 9)
+#define PORT_SEL_1             FIELD_PREP(PORT_SEL_MASK, 1)
+
 #define PCL_PHY_TEST_I         0x2000
 #define PCL_PHY_TEST_O         0x2004
 #define TESTI_DAT_MASK         GENMASK(13, 6)
 struct uniphier_pciephy_priv {
        void __iomem *base;
        struct device *dev;
-       struct clk *clk;
-       struct reset_control *rst;
+       struct clk *clk, *clk_gio;
+       struct reset_control *rst, *rst_gio;
        const struct uniphier_pciephy_soc_data *data;
 };
 
 struct uniphier_pciephy_soc_data {
-       bool has_syscon;
+       bool is_legacy;
+       void (*set_phymode)(struct regmap *regmap);
 };
 
 static void uniphier_pciephy_testio_write(struct uniphier_pciephy_priv *priv,
@@ -111,16 +116,35 @@ static void uniphier_pciephy_deassert(struct uniphier_pciephy_priv *priv)
 static int uniphier_pciephy_init(struct phy *phy)
 {
        struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
+       u32 val;
        int ret;
 
        ret = clk_prepare_enable(priv->clk);
        if (ret)
                return ret;
 
-       ret = reset_control_deassert(priv->rst);
+       ret = clk_prepare_enable(priv->clk_gio);
        if (ret)
                goto out_clk_disable;
 
+       ret = reset_control_deassert(priv->rst);
+       if (ret)
+               goto out_clk_gio_disable;
+
+       ret = reset_control_deassert(priv->rst_gio);
+       if (ret)
+               goto out_rst_assert;
+
+       /* support only 1 port */
+       val = readl(priv->base + PCL_PHY_CLKCTRL);
+       val &= ~PORT_SEL_MASK;
+       val |= PORT_SEL_1;
+       writel(val, priv->base + PCL_PHY_CLKCTRL);
+
+       /* legacy controller doesn't have phy_reset and parameters */
+       if (priv->data->is_legacy)
+               return 0;
+
        uniphier_pciephy_set_param(priv, PCL_PHY_R00,
                                   RX_EQ_ADJ_EN, RX_EQ_ADJ_EN);
        uniphier_pciephy_set_param(priv, PCL_PHY_R06, RX_EQ_ADJ,
@@ -134,6 +158,10 @@ static int uniphier_pciephy_init(struct phy *phy)
 
        return 0;
 
+out_rst_assert:
+       reset_control_assert(priv->rst);
+out_clk_gio_disable:
+       clk_disable_unprepare(priv->clk_gio);
 out_clk_disable:
        clk_disable_unprepare(priv->clk);
 
@@ -144,8 +172,11 @@ static int uniphier_pciephy_exit(struct phy *phy)
 {
        struct uniphier_pciephy_priv *priv = phy_get_drvdata(phy);
 
-       uniphier_pciephy_assert(priv);
+       if (!priv->data->is_legacy)
+               uniphier_pciephy_assert(priv);
+       reset_control_assert(priv->rst_gio);
        reset_control_assert(priv->rst);
+       clk_disable_unprepare(priv->clk_gio);
        clk_disable_unprepare(priv->clk);
 
        return 0;
@@ -163,7 +194,6 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
        struct phy_provider *phy_provider;
        struct device *dev = &pdev->dev;
        struct regmap *regmap;
-       struct resource *res;
        struct phy *phy;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -176,18 +206,36 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
 
        priv->dev = dev;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->base = devm_ioremap_resource(dev, res);
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(priv->base))
                return PTR_ERR(priv->base);
 
-       priv->clk = devm_clk_get(dev, NULL);
-       if (IS_ERR(priv->clk))
-               return PTR_ERR(priv->clk);
-
-       priv->rst = devm_reset_control_get_shared(dev, NULL);
-       if (IS_ERR(priv->rst))
-               return PTR_ERR(priv->rst);
+       if (priv->data->is_legacy) {
+               priv->clk_gio = devm_clk_get(dev, "gio");
+               if (IS_ERR(priv->clk_gio))
+                       return PTR_ERR(priv->clk_gio);
+
+               priv->rst_gio =
+                       devm_reset_control_get_shared(dev, "gio");
+               if (IS_ERR(priv->rst_gio))
+                       return PTR_ERR(priv->rst_gio);
+
+               priv->clk = devm_clk_get(dev, "link");
+               if (IS_ERR(priv->clk))
+                       return PTR_ERR(priv->clk);
+
+               priv->rst = devm_reset_control_get_shared(dev, "link");
+               if (IS_ERR(priv->rst))
+                       return PTR_ERR(priv->rst);
+       } else {
+               priv->clk = devm_clk_get(dev, NULL);
+               if (IS_ERR(priv->clk))
+                       return PTR_ERR(priv->clk);
+
+               priv->rst = devm_reset_control_get_shared(dev, NULL);
+               if (IS_ERR(priv->rst))
+                       return PTR_ERR(priv->rst);
+       }
 
        phy = devm_phy_create(dev, dev->of_node, &uniphier_pciephy_ops);
        if (IS_ERR(phy))
@@ -195,9 +243,8 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
 
        regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
                                                 "socionext,syscon");
-       if (!IS_ERR(regmap) && priv->data->has_syscon)
-               regmap_update_bits(regmap, SG_USBPCIESEL,
-                                  SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
+       if (!IS_ERR(regmap) && priv->data->set_phymode)
+               priv->data->set_phymode(regmap);
 
        phy_set_drvdata(phy, priv);
        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -205,15 +252,30 @@ static int uniphier_pciephy_probe(struct platform_device *pdev)
        return PTR_ERR_OR_ZERO(phy_provider);
 }
 
+static void uniphier_pciephy_ld20_setmode(struct regmap *regmap)
+{
+       regmap_update_bits(regmap, SG_USBPCIESEL,
+                          SG_USBPCIESEL_PCIE, SG_USBPCIESEL_PCIE);
+}
+
+static const struct uniphier_pciephy_soc_data uniphier_pro5_data = {
+       .is_legacy = true,
+};
+
 static const struct uniphier_pciephy_soc_data uniphier_ld20_data = {
-       .has_syscon = true,
+       .is_legacy = false,
+       .set_phymode = uniphier_pciephy_ld20_setmode,
 };
 
 static const struct uniphier_pciephy_soc_data uniphier_pxs3_data = {
-       .has_syscon = false,
+       .is_legacy = false,
 };
 
 static const struct of_device_id uniphier_pciephy_match[] = {
+       {
+               .compatible = "socionext,uniphier-pro5-pcie-phy",
+               .data = &uniphier_pro5_data,
+       },
        {
                .compatible = "socionext,uniphier-ld20-pcie-phy",
                .data = &uniphier_ld20_data,
index 50f379fc4e06c4ab727b8a8a8aa9b8dbebd44cca..a9bc74121f38a4daf0635d23d8279066d0ec2c14 100644 (file)
 
 #define PHY_F(regno, msb, lsb) { (regno), (msb), (lsb) }
 
+#define RX_CHK_SYNC    PHY_F(0, 5, 5)  /* RX sync mode */
+#define RX_SYNC_SEL    PHY_F(1, 1, 0)  /* RX sync length */
 #define LS_SLEW                PHY_F(10, 6, 6) /* LS mode slew rate */
 #define FS_LS_DRV      PHY_F(10, 5, 5) /* FS/LS slew rate */
 
-#define MAX_PHY_PARAMS 2
+#define MAX_PHY_PARAMS 4
 
 struct uniphier_u3hsphy_param {
        struct {
@@ -66,13 +68,14 @@ struct uniphier_u3hsphy_trim_param {
 struct uniphier_u3hsphy_priv {
        struct device *dev;
        void __iomem *base;
-       struct clk *clk, *clk_parent, *clk_ext;
-       struct reset_control *rst, *rst_parent;
+       struct clk *clk, *clk_parent, *clk_ext, *clk_parent_gio;
+       struct reset_control *rst, *rst_parent, *rst_parent_gio;
        struct regulator *vbus;
        const struct uniphier_u3hsphy_soc_data *data;
 };
 
 struct uniphier_u3hsphy_soc_data {
+       bool is_legacy;
        int nparams;
        const struct uniphier_u3hsphy_param param[MAX_PHY_PARAMS];
        u32 config0;
@@ -256,11 +259,20 @@ static int uniphier_u3hsphy_init(struct phy *phy)
        if (ret)
                return ret;
 
-       ret = reset_control_deassert(priv->rst_parent);
+       ret = clk_prepare_enable(priv->clk_parent_gio);
        if (ret)
                goto out_clk_disable;
 
-       if (!priv->data->config0 && !priv->data->config1)
+       ret = reset_control_deassert(priv->rst_parent);
+       if (ret)
+               goto out_clk_gio_disable;
+
+       ret = reset_control_deassert(priv->rst_parent_gio);
+       if (ret)
+               goto out_rst_assert;
+
+       if ((priv->data->is_legacy)
+           || (!priv->data->config0 && !priv->data->config1))
                return 0;
 
        config0 = priv->data->config0;
@@ -280,6 +292,8 @@ static int uniphier_u3hsphy_init(struct phy *phy)
 
 out_rst_assert:
        reset_control_assert(priv->rst_parent);
+out_clk_gio_disable:
+       clk_disable_unprepare(priv->clk_parent_gio);
 out_clk_disable:
        clk_disable_unprepare(priv->clk_parent);
 
@@ -290,7 +304,9 @@ static int uniphier_u3hsphy_exit(struct phy *phy)
 {
        struct uniphier_u3hsphy_priv *priv = phy_get_drvdata(phy);
 
+       reset_control_assert(priv->rst_parent_gio);
        reset_control_assert(priv->rst_parent);
+       clk_disable_unprepare(priv->clk_parent_gio);
        clk_disable_unprepare(priv->clk_parent);
 
        return 0;
@@ -309,7 +325,6 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct uniphier_u3hsphy_priv *priv;
        struct phy_provider *phy_provider;
-       struct resource *res;
        struct phy *phy;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -322,27 +337,38 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
                    priv->data->nparams > MAX_PHY_PARAMS))
                return -EINVAL;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->base = devm_ioremap_resource(dev, res);
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(priv->base))
                return PTR_ERR(priv->base);
 
-       priv->clk = devm_clk_get(dev, "phy");
-       if (IS_ERR(priv->clk))
-               return PTR_ERR(priv->clk);
+       if (!priv->data->is_legacy) {
+               priv->clk = devm_clk_get(dev, "phy");
+               if (IS_ERR(priv->clk))
+                       return PTR_ERR(priv->clk);
+
+               priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
+               if (IS_ERR(priv->clk_ext))
+                       return PTR_ERR(priv->clk_ext);
+
+               priv->rst = devm_reset_control_get_shared(dev, "phy");
+               if (IS_ERR(priv->rst))
+                       return PTR_ERR(priv->rst);
+
+       } else {
+               priv->clk_parent_gio = devm_clk_get(dev, "gio");
+               if (IS_ERR(priv->clk_parent_gio))
+                       return PTR_ERR(priv->clk_parent_gio);
+
+               priv->rst_parent_gio =
+                       devm_reset_control_get_shared(dev, "gio");
+               if (IS_ERR(priv->rst_parent_gio))
+                       return PTR_ERR(priv->rst_parent_gio);
+       }
 
        priv->clk_parent = devm_clk_get(dev, "link");
        if (IS_ERR(priv->clk_parent))
                return PTR_ERR(priv->clk_parent);
 
-       priv->clk_ext = devm_clk_get_optional(dev, "phy-ext");
-       if (IS_ERR(priv->clk_ext))
-               return PTR_ERR(priv->clk_ext);
-
-       priv->rst = devm_reset_control_get_shared(dev, "phy");
-       if (IS_ERR(priv->rst))
-               return PTR_ERR(priv->rst);
-
        priv->rst_parent = devm_reset_control_get_shared(dev, "link");
        if (IS_ERR(priv->rst_parent))
                return PTR_ERR(priv->rst_parent);
@@ -364,13 +390,26 @@ static int uniphier_u3hsphy_probe(struct platform_device *pdev)
        return PTR_ERR_OR_ZERO(phy_provider);
 }
 
-static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {
+static const struct uniphier_u3hsphy_soc_data uniphier_pro5_data = {
+       .is_legacy = true,
        .nparams = 0,
 };
 
-static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
+static const struct uniphier_u3hsphy_soc_data uniphier_pxs2_data = {
+       .is_legacy = false,
        .nparams = 2,
        .param = {
+               { RX_CHK_SYNC, 1 },
+               { RX_SYNC_SEL, 1 },
+       },
+};
+
+static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
+       .is_legacy = false,
+       .nparams = 4,
+       .param = {
+               { RX_CHK_SYNC, 1 },
+               { RX_SYNC_SEL, 1 },
                { LS_SLEW, 1 },
                { FS_LS_DRV, 1 },
        },
@@ -380,13 +419,22 @@ static const struct uniphier_u3hsphy_soc_data uniphier_ld20_data = {
 };
 
 static const struct uniphier_u3hsphy_soc_data uniphier_pxs3_data = {
-       .nparams = 0,
+       .is_legacy = false,
+       .nparams = 2,
+       .param = {
+               { RX_CHK_SYNC, 1 },
+               { RX_SYNC_SEL, 1 },
+       },
        .trim_func = uniphier_u3hsphy_trim_ld20,
        .config0 = 0x92316680,
        .config1 = 0x00000106,
 };
 
 static const struct of_device_id uniphier_u3hsphy_match[] = {
+       {
+               .compatible = "socionext,uniphier-pro5-usb3-hsphy",
+               .data = &uniphier_pro5_data,
+       },
        {
                .compatible = "socionext,uniphier-pxs2-usb3-hsphy",
                .data = &uniphier_pxs2_data,
index ec231e40ef2ace3bc324d6566bd6594250c813ad..6700645bcbe6bfd6abeeb4432618b5a6c2596055 100644 (file)
@@ -215,7 +215,6 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        struct uniphier_u3ssphy_priv *priv;
        struct phy_provider *phy_provider;
-       struct resource *res;
        struct phy *phy;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
@@ -228,8 +227,7 @@ static int uniphier_u3ssphy_probe(struct platform_device *pdev)
                    priv->data->nparams > MAX_PHY_PARAMS))
                return -EINVAL;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       priv->base = devm_ioremap_resource(dev, res);
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
        if (IS_ERR(priv->base))
                return PTR_ERR(priv->base);
 
@@ -314,6 +312,10 @@ static const struct of_device_id uniphier_u3ssphy_match[] = {
                .compatible = "socionext,uniphier-pro4-usb3-ssphy",
                .data = &uniphier_pro4_data,
        },
+       {
+               .compatible = "socionext,uniphier-pro5-usb3-ssphy",
+               .data = &uniphier_pro4_data,
+       },
        {
                .compatible = "socionext,uniphier-pxs2-usb3-ssphy",
                .data = &uniphier_pxs2_data,
index f9817c3ae85f02773c88899345ac352c24de178d..a208aca4ba7b376bfc350bc8f082dca93b21ac3b 100644 (file)
@@ -2,6 +2,8 @@
 config PHY_TEGRA_XUSB
        tristate "NVIDIA Tegra XUSB pad controller driver"
        depends on ARCH_TEGRA
+       select USB_CONN_GPIO
+       select USB_PHY
        help
          Choose this option if you have an NVIDIA Tegra SoC.
 
index 320dd389f34d8a400baa34c00d4e4a0ca55fecd7..89b84067cb4c32e90a77a0907d1dae3c8e0b1415 100644 (file)
@@ -6,4 +6,5 @@ phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_124_SOC) += xusb-tegra124.o
 phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_132_SOC) += xusb-tegra124.o
 phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_210_SOC) += xusb-tegra210.o
 phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_186_SOC) += xusb-tegra186.o
+phy-tegra-xusb-$(CONFIG_ARCH_TEGRA_194_SOC) += xusb-tegra186.o
 obj-$(CONFIG_PHY_TEGRA194_P2U) += phy-tegra194-p2u.o
index 98d84920c676c5df0b1e5709ec73f021697f1f12..db56c7fbe60be01d23f1a4f89f30acef6c0db8a7 100644 (file)
@@ -1422,6 +1422,8 @@ tegra124_usb2_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra124_usb2_port_ops = {
+       .release = tegra_xusb_usb2_port_release,
+       .remove = tegra_xusb_usb2_port_remove,
        .enable = tegra124_usb2_port_enable,
        .disable = tegra124_usb2_port_disable,
        .map = tegra124_usb2_port_map,
@@ -1443,6 +1445,7 @@ tegra124_ulpi_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra124_ulpi_port_ops = {
+       .release = tegra_xusb_ulpi_port_release,
        .enable = tegra124_ulpi_port_enable,
        .disable = tegra124_ulpi_port_disable,
        .map = tegra124_ulpi_port_map,
@@ -1464,6 +1467,7 @@ tegra124_hsic_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra124_hsic_port_ops = {
+       .release = tegra_xusb_hsic_port_release,
        .enable = tegra124_hsic_port_enable,
        .disable = tegra124_hsic_port_disable,
        .map = tegra124_hsic_port_map,
@@ -1647,6 +1651,8 @@ tegra124_usb3_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra124_usb3_port_ops = {
+       .release = tegra_xusb_usb3_port_release,
+       .remove = tegra_xusb_usb3_port_remove,
        .enable = tegra124_usb3_port_enable,
        .disable = tegra124_usb3_port_disable,
        .map = tegra124_usb3_port_map,
index 84c27394c181db5ec976d439f083d5ca4bdb6e11..5d64f69b39a9558f8dde13c2f247c432601766dc 100644 (file)
 #define  SSPX_ELPG_CLAMP_EN(x)                 BIT(0 + (x) * 3)
 #define  SSPX_ELPG_CLAMP_EN_EARLY(x)           BIT(1 + (x) * 3)
 #define  SSPX_ELPG_VCORE_DOWN(x)               BIT(2 + (x) * 3)
+#define XUSB_PADCTL_SS_PORT_CFG                        0x2c
+#define   PORTX_SPEED_SUPPORT_SHIFT(x)         ((x) * 4)
+#define   PORTX_SPEED_SUPPORT_MASK             (0x3)
+#define     PORT_SPEED_SUPPORT_GEN1            (0x0)
 
 #define XUSB_PADCTL_USB2_OTG_PADX_CTL0(x)      (0x88 + (x) * 0x40)
 #define  HS_CURR_LEVEL(x)                      ((x) & 0x3f)
@@ -301,6 +305,97 @@ static void tegra_phy_xusb_utmi_pad_power_down(struct phy *phy)
        tegra186_utmi_bias_pad_power_off(padctl);
 }
 
+static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
+                                              bool status)
+{
+       u32 value;
+
+       dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
+
+       value = padctl_readl(padctl, USB2_VBUS_ID);
+
+       if (status) {
+               value |= VBUS_OVERRIDE;
+               value &= ~ID_OVERRIDE(~0);
+               value |= ID_OVERRIDE_FLOATING;
+       } else {
+               value &= ~VBUS_OVERRIDE;
+       }
+
+       padctl_writel(padctl, value, USB2_VBUS_ID);
+
+       return 0;
+}
+
+static int tegra186_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl,
+                                           bool status)
+{
+       u32 value;
+
+       dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear");
+
+       value = padctl_readl(padctl, USB2_VBUS_ID);
+
+       if (status) {
+               if (value & VBUS_OVERRIDE) {
+                       value &= ~VBUS_OVERRIDE;
+                       padctl_writel(padctl, value, USB2_VBUS_ID);
+                       usleep_range(1000, 2000);
+
+                       value = padctl_readl(padctl, USB2_VBUS_ID);
+               }
+
+               value &= ~ID_OVERRIDE(~0);
+               value |= ID_OVERRIDE_GROUNDED;
+       } else {
+               value &= ~ID_OVERRIDE(~0);
+               value |= ID_OVERRIDE_FLOATING;
+       }
+
+       padctl_writel(padctl, value, USB2_VBUS_ID);
+
+       return 0;
+}
+
+static int tegra186_utmi_phy_set_mode(struct phy *phy, enum phy_mode mode,
+                                     int submode)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+       struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl,
+                                                               lane->index);
+       int err = 0;
+
+       mutex_lock(&padctl->lock);
+
+       dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode);
+
+       if (mode == PHY_MODE_USB_OTG) {
+               if (submode == USB_ROLE_HOST) {
+                       tegra186_xusb_padctl_id_override(padctl, true);
+
+                       err = regulator_enable(port->supply);
+               } else if (submode == USB_ROLE_DEVICE) {
+                       tegra186_xusb_padctl_vbus_override(padctl, true);
+               } else if (submode == USB_ROLE_NONE) {
+                       /*
+                        * When port is peripheral only or role transitions to
+                        * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not
+                        * enabled.
+                        */
+                       if (regulator_is_enabled(port->supply))
+                               regulator_disable(port->supply);
+
+                       tegra186_xusb_padctl_id_override(padctl, false);
+                       tegra186_xusb_padctl_vbus_override(padctl, false);
+               }
+       }
+
+       mutex_unlock(&padctl->lock);
+
+       return err;
+}
+
 static int tegra186_utmi_phy_power_on(struct phy *phy)
 {
        struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
@@ -439,6 +534,7 @@ static const struct phy_ops utmi_phy_ops = {
        .exit = tegra186_utmi_phy_exit,
        .power_on = tegra186_utmi_phy_power_on,
        .power_off = tegra186_utmi_phy_power_off,
+       .set_mode = tegra186_utmi_phy_set_mode,
        .owner = THIS_MODULE,
 };
 
@@ -503,19 +599,6 @@ static const char * const tegra186_usb2_functions[] = {
        "xusb",
 };
 
-static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = {
-       TEGRA186_LANE("usb2-0", 0,  0, 0, usb2),
-       TEGRA186_LANE("usb2-1", 0,  0, 0, usb2),
-       TEGRA186_LANE("usb2-2", 0,  0, 0, usb2),
-};
-
-static const struct tegra_xusb_pad_soc tegra186_usb2_pad = {
-       .name = "usb2",
-       .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes),
-       .lanes = tegra186_usb2_lanes,
-       .ops = &tegra186_usb2_pad_ops,
-};
-
 static int tegra186_usb2_port_enable(struct tegra_xusb_port *port)
 {
        return 0;
@@ -532,6 +615,8 @@ tegra186_usb2_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra186_usb2_port_ops = {
+       .release = tegra_xusb_usb2_port_release,
+       .remove = tegra_xusb_usb2_port_remove,
        .enable = tegra186_usb2_port_enable,
        .disable = tegra186_usb2_port_disable,
        .map = tegra186_usb2_port_map,
@@ -591,6 +676,8 @@ tegra186_usb3_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra186_usb3_port_ops = {
+       .release = tegra_xusb_usb3_port_release,
+       .remove = tegra_xusb_usb3_port_remove,
        .enable = tegra186_usb3_port_enable,
        .disable = tegra186_usb3_port_disable,
        .map = tegra186_usb3_port_map,
@@ -635,6 +722,15 @@ static int tegra186_usb3_phy_power_on(struct phy *phy)
 
        padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CAP);
 
+       if (padctl->soc->supports_gen2 && port->disable_gen2) {
+               value = padctl_readl(padctl, XUSB_PADCTL_SS_PORT_CFG);
+               value &= ~(PORTX_SPEED_SUPPORT_MASK <<
+                       PORTX_SPEED_SUPPORT_SHIFT(index));
+               value |= (PORT_SPEED_SUPPORT_GEN1 <<
+                       PORTX_SPEED_SUPPORT_SHIFT(index));
+               padctl_writel(padctl, value, XUSB_PADCTL_SS_PORT_CFG);
+       }
+
        value = padctl_readl(padctl, XUSB_PADCTL_ELPG_PROGRAM_1);
        value &= ~SSPX_ELPG_VCORE_DOWN(index);
        padctl_writel(padctl, value, XUSB_PADCTL_ELPG_PROGRAM_1);
@@ -765,27 +861,6 @@ static const char * const tegra186_usb3_functions[] = {
        "xusb",
 };
 
-static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = {
-       TEGRA186_LANE("usb3-0", 0,  0, 0, usb3),
-       TEGRA186_LANE("usb3-1", 0,  0, 0, usb3),
-       TEGRA186_LANE("usb3-2", 0,  0, 0, usb3),
-};
-
-static const struct tegra_xusb_pad_soc tegra186_usb3_pad = {
-       .name = "usb3",
-       .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes),
-       .lanes = tegra186_usb3_lanes,
-       .ops = &tegra186_usb3_pad_ops,
-};
-
-static const struct tegra_xusb_pad_soc * const tegra186_pads[] = {
-       &tegra186_usb2_pad,
-       &tegra186_usb3_pad,
-#if 0 /* TODO implement */
-       &tegra186_hsic_pad,
-#endif
-};
-
 static int
 tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
 {
@@ -802,7 +877,9 @@ tegra186_xusb_read_fuse_calibration(struct tegra186_xusb_padctl *padctl)
 
        err = tegra_fuse_readl(TEGRA_FUSE_SKU_CALIB_0, &value);
        if (err) {
-               dev_err(dev, "failed to read calibration fuse: %d\n", err);
+               if (err != -EPROBE_DEFER)
+                       dev_err(dev, "failed to read calibration fuse: %d\n",
+                               err);
                return err;
        }
 
@@ -857,34 +934,13 @@ static void tegra186_xusb_padctl_remove(struct tegra_xusb_padctl *padctl)
 {
 }
 
-static int tegra186_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
-                                              bool status)
-{
-       u32 value;
-
-       dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
-
-       value = padctl_readl(padctl, USB2_VBUS_ID);
-
-       if (status) {
-               value |= VBUS_OVERRIDE;
-               value &= ~ID_OVERRIDE(~0);
-               value |= ID_OVERRIDE_FLOATING;
-       } else {
-               value &= ~VBUS_OVERRIDE;
-       }
-
-       padctl_writel(padctl, value, USB2_VBUS_ID);
-
-       return 0;
-}
-
 static const struct tegra_xusb_padctl_ops tegra186_xusb_padctl_ops = {
        .probe = tegra186_xusb_padctl_probe,
        .remove = tegra186_xusb_padctl_remove,
        .vbus_override = tegra186_xusb_padctl_vbus_override,
 };
 
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_186_SOC)
 static const char * const tegra186_xusb_padctl_supply_names[] = {
        "avdd-pll-erefeut",
        "avdd-usb",
@@ -892,6 +948,40 @@ static const char * const tegra186_xusb_padctl_supply_names[] = {
        "vddio-hsic",
 };
 
+static const struct tegra_xusb_lane_soc tegra186_usb2_lanes[] = {
+       TEGRA186_LANE("usb2-0", 0,  0, 0, usb2),
+       TEGRA186_LANE("usb2-1", 0,  0, 0, usb2),
+       TEGRA186_LANE("usb2-2", 0,  0, 0, usb2),
+};
+
+static const struct tegra_xusb_pad_soc tegra186_usb2_pad = {
+       .name = "usb2",
+       .num_lanes = ARRAY_SIZE(tegra186_usb2_lanes),
+       .lanes = tegra186_usb2_lanes,
+       .ops = &tegra186_usb2_pad_ops,
+};
+
+static const struct tegra_xusb_lane_soc tegra186_usb3_lanes[] = {
+       TEGRA186_LANE("usb3-0", 0,  0, 0, usb3),
+       TEGRA186_LANE("usb3-1", 0,  0, 0, usb3),
+       TEGRA186_LANE("usb3-2", 0,  0, 0, usb3),
+};
+
+static const struct tegra_xusb_pad_soc tegra186_usb3_pad = {
+       .name = "usb3",
+       .num_lanes = ARRAY_SIZE(tegra186_usb3_lanes),
+       .lanes = tegra186_usb3_lanes,
+       .ops = &tegra186_usb3_pad_ops,
+};
+
+static const struct tegra_xusb_pad_soc * const tegra186_pads[] = {
+       &tegra186_usb2_pad,
+       &tegra186_usb3_pad,
+#if 0 /* TODO implement */
+       &tegra186_hsic_pad,
+#endif
+};
+
 const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
        .num_pads = ARRAY_SIZE(tegra186_pads),
        .pads = tegra186_pads,
@@ -916,6 +1006,67 @@ const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc = {
        .num_supplies = ARRAY_SIZE(tegra186_xusb_padctl_supply_names),
 };
 EXPORT_SYMBOL_GPL(tegra186_xusb_padctl_soc);
+#endif
+
+#if IS_ENABLED(CONFIG_ARCH_TEGRA_194_SOC)
+static const char * const tegra194_xusb_padctl_supply_names[] = {
+       "avdd-usb",
+       "vclamp-usb",
+};
+
+static const struct tegra_xusb_lane_soc tegra194_usb2_lanes[] = {
+       TEGRA186_LANE("usb2-0", 0,  0, 0, usb2),
+       TEGRA186_LANE("usb2-1", 0,  0, 0, usb2),
+       TEGRA186_LANE("usb2-2", 0,  0, 0, usb2),
+       TEGRA186_LANE("usb2-3", 0,  0, 0, usb2),
+};
+
+static const struct tegra_xusb_pad_soc tegra194_usb2_pad = {
+       .name = "usb2",
+       .num_lanes = ARRAY_SIZE(tegra194_usb2_lanes),
+       .lanes = tegra194_usb2_lanes,
+       .ops = &tegra186_usb2_pad_ops,
+};
+
+static const struct tegra_xusb_lane_soc tegra194_usb3_lanes[] = {
+       TEGRA186_LANE("usb3-0", 0,  0, 0, usb3),
+       TEGRA186_LANE("usb3-1", 0,  0, 0, usb3),
+       TEGRA186_LANE("usb3-2", 0,  0, 0, usb3),
+       TEGRA186_LANE("usb3-3", 0,  0, 0, usb3),
+};
+
+static const struct tegra_xusb_pad_soc tegra194_usb3_pad = {
+       .name = "usb3",
+       .num_lanes = ARRAY_SIZE(tegra194_usb3_lanes),
+       .lanes = tegra194_usb3_lanes,
+       .ops = &tegra186_usb3_pad_ops,
+};
+
+static const struct tegra_xusb_pad_soc * const tegra194_pads[] = {
+       &tegra194_usb2_pad,
+       &tegra194_usb3_pad,
+};
+
+const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc = {
+       .num_pads = ARRAY_SIZE(tegra194_pads),
+       .pads = tegra194_pads,
+       .ports = {
+               .usb2 = {
+                       .ops = &tegra186_usb2_port_ops,
+                       .count = 4,
+               },
+               .usb3 = {
+                       .ops = &tegra186_usb3_port_ops,
+                       .count = 4,
+               },
+       },
+       .ops = &tegra186_xusb_padctl_ops,
+       .supply_names = tegra194_xusb_padctl_supply_names,
+       .num_supplies = ARRAY_SIZE(tegra194_xusb_padctl_supply_names),
+       .supports_gen2 = true,
+};
+EXPORT_SYMBOL_GPL(tegra194_xusb_padctl_soc);
+#endif
 
 MODULE_AUTHOR("JC Kuo <jckuo@nvidia.com>");
 MODULE_DESCRIPTION("NVIDIA Tegra186 XUSB Pad Controller driver");
index 394913bb2f2082e9478da4256ee255ea8e833ed0..66bd4613835b243c8c4049efb72897e9937f65fc 100644 (file)
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT 18
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK 0xf
 #define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING 8
+#define XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED 0
 
 struct tegra210_xusb_fuse_calibration {
        u32 hs_curr_level[4];
@@ -935,6 +936,103 @@ static int tegra210_usb2_phy_exit(struct phy *phy)
        return tegra210_xusb_padctl_disable(lane->pad->padctl);
 }
 
+static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
+                                             bool status)
+{
+       u32 value;
+
+       dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
+
+       if (status) {
+               value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+               value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
+                          XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
+               value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
+                        XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
+       } else {
+               value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+       }
+
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
+
+       return 0;
+}
+
+static int tegra210_xusb_padctl_id_override(struct tegra_xusb_padctl *padctl,
+                                           bool status)
+{
+       u32 value;
+
+       dev_dbg(padctl->dev, "%s id override\n", status ? "set" : "clear");
+
+       value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
+
+       if (status) {
+               if (value & XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON) {
+                       value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
+                       padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
+                       usleep_range(1000, 2000);
+
+                       value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
+               }
+
+               value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
+                          XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
+               value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_GROUNDED <<
+                        XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
+       } else {
+               value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
+                          XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
+               value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
+                        XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
+       }
+
+       padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
+
+       return 0;
+}
+
+static int tegra210_usb2_phy_set_mode(struct phy *phy, enum phy_mode mode,
+                                     int submode)
+{
+       struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
+       struct tegra_xusb_padctl *padctl = lane->pad->padctl;
+       struct tegra_xusb_usb2_port *port = tegra_xusb_find_usb2_port(padctl,
+                                                               lane->index);
+       int err = 0;
+
+       mutex_lock(&padctl->lock);
+
+       dev_dbg(&port->base.dev, "%s: mode %d", __func__, mode);
+
+       if (mode == PHY_MODE_USB_OTG) {
+               if (submode == USB_ROLE_HOST) {
+                       tegra210_xusb_padctl_id_override(padctl, true);
+
+                       err = regulator_enable(port->supply);
+               } else if (submode == USB_ROLE_DEVICE) {
+                       tegra210_xusb_padctl_vbus_override(padctl, true);
+               } else if (submode == USB_ROLE_NONE) {
+                       /*
+                        * When port is peripheral only or role transitions to
+                        * USB_ROLE_NONE from USB_ROLE_DEVICE, regulator is not
+                        * be enabled.
+                        */
+                       if (regulator_is_enabled(port->supply))
+                               regulator_disable(port->supply);
+
+                       tegra210_xusb_padctl_id_override(padctl, false);
+                       tegra210_xusb_padctl_vbus_override(padctl, false);
+               }
+       }
+
+       mutex_unlock(&padctl->lock);
+
+       return err;
+}
+
 static int tegra210_usb2_phy_power_on(struct phy *phy)
 {
        struct tegra_xusb_lane *lane = phy_get_drvdata(phy);
@@ -1048,9 +1146,11 @@ static int tegra210_usb2_phy_power_on(struct phy *phy)
        padctl_writel(padctl, value,
                      XUSB_PADCTL_USB2_BATTERY_CHRG_OTGPADX_CTL1(index));
 
-       err = regulator_enable(port->supply);
-       if (err)
-               return err;
+       if (port->supply && port->mode == USB_DR_MODE_HOST) {
+               err = regulator_enable(port->supply);
+               if (err)
+                       return err;
+       }
 
        mutex_lock(&padctl->lock);
 
@@ -1164,6 +1264,7 @@ static const struct phy_ops tegra210_usb2_phy_ops = {
        .exit = tegra210_usb2_phy_exit,
        .power_on = tegra210_usb2_phy_power_on,
        .power_off = tegra210_usb2_phy_power_off,
+       .set_mode = tegra210_usb2_phy_set_mode,
        .owner = THIS_MODULE,
 };
 
@@ -1852,6 +1953,8 @@ tegra210_usb2_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra210_usb2_port_ops = {
+       .release = tegra_xusb_usb2_port_release,
+       .remove = tegra_xusb_usb2_port_remove,
        .enable = tegra210_usb2_port_enable,
        .disable = tegra210_usb2_port_disable,
        .map = tegra210_usb2_port_map,
@@ -1873,6 +1976,7 @@ tegra210_hsic_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra210_hsic_port_ops = {
+       .release = tegra_xusb_hsic_port_release,
        .enable = tegra210_hsic_port_enable,
        .disable = tegra210_hsic_port_disable,
        .map = tegra210_hsic_port_map,
@@ -2018,35 +2122,13 @@ tegra210_usb3_port_map(struct tegra_xusb_port *port)
 }
 
 static const struct tegra_xusb_port_ops tegra210_usb3_port_ops = {
+       .release = tegra_xusb_usb3_port_release,
+       .remove = tegra_xusb_usb3_port_remove,
        .enable = tegra210_usb3_port_enable,
        .disable = tegra210_usb3_port_disable,
        .map = tegra210_usb3_port_map,
 };
 
-static int tegra210_xusb_padctl_vbus_override(struct tegra_xusb_padctl *padctl,
-                                             bool status)
-{
-       u32 value;
-
-       dev_dbg(padctl->dev, "%s vbus override\n", status ? "set" : "clear");
-
-       value = padctl_readl(padctl, XUSB_PADCTL_USB2_VBUS_ID);
-
-       if (status) {
-               value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
-               value &= ~(XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_MASK <<
-                          XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT);
-               value |= XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_FLOATING <<
-                        XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_SHIFT;
-       } else {
-               value &= ~XUSB_PADCTL_USB2_VBUS_ID_OVERRIDE_VBUS_ON;
-       }
-
-       padctl_writel(padctl, value, XUSB_PADCTL_USB2_VBUS_ID);
-
-       return 0;
-}
-
 static int tegra210_utmi_port_reset(struct phy *phy)
 {
        struct tegra_xusb_padctl *padctl;
index f98ec3922c0289d1984efd183eab38c55de4dbe1..de4a46fe176308d97b684e7051bfe7677a7ae6fe 100644 (file)
@@ -65,6 +65,12 @@ static const struct of_device_id tegra_xusb_padctl_of_match[] = {
                .compatible = "nvidia,tegra186-xusb-padctl",
                .data = &tegra186_xusb_padctl_soc,
        },
+#endif
+#if defined(CONFIG_ARCH_TEGRA_194_SOC)
+       {
+               .compatible = "nvidia,tegra194-xusb-padctl",
+               .data = &tegra194_xusb_padctl_soc,
+       },
 #endif
        { }
 };
@@ -501,6 +507,10 @@ tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl, unsigned int index)
 
 static void tegra_xusb_port_release(struct device *dev)
 {
+       struct tegra_xusb_port *port = to_tegra_xusb_port(dev);
+
+       if (port->ops->release)
+               port->ops->release(port);
 }
 
 static struct device_type tegra_xusb_port_type = {
@@ -541,6 +551,16 @@ unregister:
 
 static void tegra_xusb_port_unregister(struct tegra_xusb_port *port)
 {
+       if (!IS_ERR_OR_NULL(port->usb_role_sw)) {
+               of_platform_depopulate(&port->dev);
+               usb_role_switch_unregister(port->usb_role_sw);
+               cancel_work_sync(&port->usb_phy_work);
+               usb_remove_phy(&port->usb_phy);
+       }
+
+       if (port->ops->remove)
+               port->ops->remove(port);
+
        device_unregister(&port->dev);
 }
 
@@ -551,11 +571,146 @@ static const char *const modes[] = {
        [USB_DR_MODE_OTG] = "otg",
 };
 
+static const char * const usb_roles[] = {
+       [USB_ROLE_NONE]         = "none",
+       [USB_ROLE_HOST]         = "host",
+       [USB_ROLE_DEVICE]       = "device",
+};
+
+static enum usb_phy_events to_usb_phy_event(enum usb_role role)
+{
+       switch (role) {
+       case USB_ROLE_DEVICE:
+               return USB_EVENT_VBUS;
+
+       case USB_ROLE_HOST:
+               return USB_EVENT_ID;
+
+       default:
+               return USB_EVENT_NONE;
+       }
+}
+
+static void tegra_xusb_usb_phy_work(struct work_struct *work)
+{
+       struct tegra_xusb_port *port = container_of(work,
+                                                   struct tegra_xusb_port,
+                                                   usb_phy_work);
+       enum usb_role role = usb_role_switch_get_role(port->usb_role_sw);
+
+       usb_phy_set_event(&port->usb_phy, to_usb_phy_event(role));
+
+       dev_dbg(&port->dev, "%s(): calling notifier for role %s\n", __func__,
+               usb_roles[role]);
+
+       atomic_notifier_call_chain(&port->usb_phy.notifier, 0, &port->usb_phy);
+}
+
+static int tegra_xusb_role_sw_set(struct usb_role_switch *sw,
+                                 enum usb_role role)
+{
+       struct tegra_xusb_port *port = usb_role_switch_get_drvdata(sw);
+
+       dev_dbg(&port->dev, "%s(): role %s\n", __func__, usb_roles[role]);
+
+       schedule_work(&port->usb_phy_work);
+
+       return 0;
+}
+
+static int tegra_xusb_set_peripheral(struct usb_otg *otg,
+                                    struct usb_gadget *gadget)
+{
+       struct tegra_xusb_port *port = container_of(otg->usb_phy,
+                                                   struct tegra_xusb_port,
+                                                   usb_phy);
+
+       if (gadget != NULL)
+               schedule_work(&port->usb_phy_work);
+
+       return 0;
+}
+
+static int tegra_xusb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+       struct tegra_xusb_port *port = container_of(otg->usb_phy,
+                                                   struct tegra_xusb_port,
+                                                   usb_phy);
+
+       if (host != NULL)
+               schedule_work(&port->usb_phy_work);
+
+       return 0;
+}
+
+
+static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port)
+{
+       struct tegra_xusb_lane *lane;
+       struct usb_role_switch_desc role_sx_desc = {
+               .fwnode = dev_fwnode(&port->dev),
+               .set = tegra_xusb_role_sw_set,
+       };
+       int err = 0;
+
+       /*
+        * USB role switch driver needs parent driver owner info. This is a
+        * suboptimal solution. TODO: Need to revisit this in a follow-up patch
+        * where an optimal solution is possible with changes to USB role
+        * switch driver.
+        */
+       port->dev.driver = devm_kzalloc(&port->dev,
+                                       sizeof(struct device_driver),
+                                       GFP_KERNEL);
+       port->dev.driver->owner  = THIS_MODULE;
+
+       port->usb_role_sw = usb_role_switch_register(&port->dev,
+                                                    &role_sx_desc);
+       if (IS_ERR(port->usb_role_sw)) {
+               err = PTR_ERR(port->usb_role_sw);
+               dev_err(&port->dev, "failed to register USB role switch: %d",
+                       err);
+               return err;
+       }
+
+       INIT_WORK(&port->usb_phy_work, tegra_xusb_usb_phy_work);
+       usb_role_switch_set_drvdata(port->usb_role_sw, port);
+
+       port->usb_phy.otg = devm_kzalloc(&port->dev, sizeof(struct usb_otg),
+                                        GFP_KERNEL);
+       if (!port->usb_phy.otg)
+               return -ENOMEM;
+
+       lane = tegra_xusb_find_lane(port->padctl, "usb2", port->index);
+
+       /*
+        * Assign phy dev to usb-phy dev. Host/device drivers can use phy
+        * reference to retrieve usb-phy details.
+        */
+       port->usb_phy.dev = &lane->pad->lanes[port->index]->dev;
+       port->usb_phy.dev->driver = port->padctl->dev->driver;
+       port->usb_phy.otg->usb_phy = &port->usb_phy;
+       port->usb_phy.otg->set_peripheral = tegra_xusb_set_peripheral;
+       port->usb_phy.otg->set_host = tegra_xusb_set_host;
+
+       err = usb_add_phy_dev(&port->usb_phy);
+       if (err < 0) {
+               dev_err(&port->dev, "Failed to add USB PHY: %d\n", err);
+               return err;
+       }
+
+       /* populate connector entry */
+       of_platform_populate(port->dev.of_node, NULL, NULL, &port->dev);
+
+       return err;
+}
+
 static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
 {
        struct tegra_xusb_port *port = &usb2->base;
        struct device_node *np = port->dev.of_node;
        const char *mode;
+       int err;
 
        usb2->internal = of_property_read_bool(np, "nvidia,internal");
 
@@ -572,7 +727,21 @@ static int tegra_xusb_usb2_port_parse_dt(struct tegra_xusb_usb2_port *usb2)
                usb2->mode = USB_DR_MODE_HOST;
        }
 
-       usb2->supply = devm_regulator_get(&port->dev, "vbus");
+       /* usb-role-switch property is mandatory for OTG/Peripheral modes */
+       if (usb2->mode == USB_DR_MODE_PERIPHERAL ||
+           usb2->mode == USB_DR_MODE_OTG) {
+               if (of_property_read_bool(np, "usb-role-switch")) {
+                       err = tegra_xusb_setup_usb_role_switch(port);
+                       if (err < 0)
+                               return err;
+               } else {
+                       dev_err(&port->dev, "usb-role-switch not found for %s mode",
+                               modes[usb2->mode]);
+                       return -EINVAL;
+               }
+       }
+
+       usb2->supply = regulator_get(&port->dev, "vbus");
        return PTR_ERR_OR_ZERO(usb2->supply);
 }
 
@@ -591,7 +760,7 @@ static int tegra_xusb_add_usb2_port(struct tegra_xusb_padctl *padctl,
        if (!np || !of_device_is_available(np))
                goto out;
 
-       usb2 = devm_kzalloc(padctl->dev, sizeof(*usb2), GFP_KERNEL);
+       usb2 = kzalloc(sizeof(*usb2), GFP_KERNEL);
        if (!usb2) {
                err = -ENOMEM;
                goto out;
@@ -622,6 +791,20 @@ out:
        return err;
 }
 
+void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port)
+{
+       struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port);
+
+       kfree(usb2);
+}
+
+void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port)
+{
+       struct tegra_xusb_usb2_port *usb2 = to_usb2_port(port);
+
+       regulator_put(usb2->supply);
+}
+
 static int tegra_xusb_ulpi_port_parse_dt(struct tegra_xusb_ulpi_port *ulpi)
 {
        struct tegra_xusb_port *port = &ulpi->base;
@@ -643,7 +826,7 @@ static int tegra_xusb_add_ulpi_port(struct tegra_xusb_padctl *padctl,
        if (!np || !of_device_is_available(np))
                goto out;
 
-       ulpi = devm_kzalloc(padctl->dev, sizeof(*ulpi), GFP_KERNEL);
+       ulpi = kzalloc(sizeof(*ulpi), GFP_KERNEL);
        if (!ulpi) {
                err = -ENOMEM;
                goto out;
@@ -674,6 +857,13 @@ out:
        return err;
 }
 
+void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port)
+{
+       struct tegra_xusb_ulpi_port *ulpi = to_ulpi_port(port);
+
+       kfree(ulpi);
+}
+
 static int tegra_xusb_hsic_port_parse_dt(struct tegra_xusb_hsic_port *hsic)
 {
        /* XXX */
@@ -691,7 +881,7 @@ static int tegra_xusb_add_hsic_port(struct tegra_xusb_padctl *padctl,
        if (!np || !of_device_is_available(np))
                goto out;
 
-       hsic = devm_kzalloc(padctl->dev, sizeof(*hsic), GFP_KERNEL);
+       hsic = kzalloc(sizeof(*hsic), GFP_KERNEL);
        if (!hsic) {
                err = -ENOMEM;
                goto out;
@@ -722,10 +912,18 @@ out:
        return err;
 }
 
+void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port)
+{
+       struct tegra_xusb_hsic_port *hsic = to_hsic_port(port);
+
+       kfree(hsic);
+}
+
 static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
 {
        struct tegra_xusb_port *port = &usb3->base;
        struct device_node *np = port->dev.of_node;
+       enum usb_device_speed maximum_speed;
        u32 value;
        int err;
 
@@ -739,7 +937,17 @@ static int tegra_xusb_usb3_port_parse_dt(struct tegra_xusb_usb3_port *usb3)
 
        usb3->internal = of_property_read_bool(np, "nvidia,internal");
 
-       usb3->supply = devm_regulator_get(&port->dev, "vbus");
+       if (device_property_present(&port->dev, "maximum-speed")) {
+               maximum_speed =  usb_get_maximum_speed(&port->dev);
+               if (maximum_speed == USB_SPEED_SUPER)
+                       usb3->disable_gen2 = true;
+               else if (maximum_speed == USB_SPEED_SUPER_PLUS)
+                       usb3->disable_gen2 = false;
+               else
+                       return -EINVAL;
+       }
+
+       usb3->supply = regulator_get(&port->dev, "vbus");
        return PTR_ERR_OR_ZERO(usb3->supply);
 }
 
@@ -759,7 +967,7 @@ static int tegra_xusb_add_usb3_port(struct tegra_xusb_padctl *padctl,
        if (!np || !of_device_is_available(np))
                goto out;
 
-       usb3 = devm_kzalloc(padctl->dev, sizeof(*usb3), GFP_KERNEL);
+       usb3 = kzalloc(sizeof(*usb3), GFP_KERNEL);
        if (!usb3) {
                err = -ENOMEM;
                goto out;
@@ -790,6 +998,20 @@ out:
        return err;
 }
 
+void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port)
+{
+       struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
+
+       kfree(usb3);
+}
+
+void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port)
+{
+       struct tegra_xusb_usb3_port *usb3 = to_usb3_port(port);
+
+       regulator_put(usb3->supply);
+}
+
 static void __tegra_xusb_remove_ports(struct tegra_xusb_padctl *padctl)
 {
        struct tegra_xusb_port *port, *tmp;
@@ -1001,7 +1223,13 @@ static int tegra_xusb_padctl_probe(struct platform_device *pdev)
 
        err = tegra_xusb_setup_ports(padctl);
        if (err) {
-               dev_err(&pdev->dev, "failed to setup XUSB ports: %d\n", err);
+               const char *level = KERN_ERR;
+
+               if (err == -EPROBE_DEFER)
+                       level = KERN_DEBUG;
+
+               dev_printk(level, &pdev->dev,
+                          dev_fmt("failed to setup XUSB ports: %d\n"), err);
                goto remove_pads;
        }
 
@@ -1143,6 +1371,27 @@ int tegra_phy_xusb_utmi_port_reset(struct phy *phy)
 }
 EXPORT_SYMBOL_GPL(tegra_phy_xusb_utmi_port_reset);
 
+int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
+                                   unsigned int port)
+{
+       struct tegra_xusb_usb2_port *usb2;
+       struct tegra_xusb_usb3_port *usb3;
+       int i;
+
+       usb2 = tegra_xusb_find_usb2_port(padctl, port);
+       if (!usb2)
+               return -EINVAL;
+
+       for (i = 0; i < padctl->soc->ports.usb3.count; i++) {
+               usb3 = tegra_xusb_find_usb3_port(padctl, i);
+               if (usb3 && usb3->port == usb2->base.index)
+                       return usb3->base.index;
+       }
+
+       return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(tegra_xusb_padctl_get_usb3_companion);
+
 MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
 MODULE_DESCRIPTION("Tegra XUSB Pad Controller driver");
 MODULE_LICENSE("GPL v2");
index da94fcce630752acb3d89f15175fea4b959efc59..ea35af747066e702992cd24be1ce3a73da2f101e 100644 (file)
@@ -12,6 +12,7 @@
 #include <linux/workqueue.h>
 
 #include <linux/usb/otg.h>
+#include <linux/usb/role.h>
 
 /* legacy entry points for backwards-compatibility */
 int tegra_xusb_padctl_legacy_probe(struct platform_device *pdev);
@@ -266,9 +267,18 @@ struct tegra_xusb_port {
        struct list_head list;
        struct device dev;
 
+       struct usb_role_switch *usb_role_sw;
+       struct work_struct usb_phy_work;
+       struct usb_phy usb_phy;
+
        const struct tegra_xusb_port_ops *ops;
 };
 
+static inline struct tegra_xusb_port *to_tegra_xusb_port(struct device *dev)
+{
+       return container_of(dev, struct tegra_xusb_port, dev);
+}
+
 struct tegra_xusb_lane_map {
        unsigned int port;
        const char *type;
@@ -303,6 +313,8 @@ to_usb2_port(struct tegra_xusb_port *port)
 struct tegra_xusb_usb2_port *
 tegra_xusb_find_usb2_port(struct tegra_xusb_padctl *padctl,
                          unsigned int index);
+void tegra_xusb_usb2_port_release(struct tegra_xusb_port *port);
+void tegra_xusb_usb2_port_remove(struct tegra_xusb_port *port);
 
 struct tegra_xusb_ulpi_port {
        struct tegra_xusb_port base;
@@ -317,6 +329,8 @@ to_ulpi_port(struct tegra_xusb_port *port)
        return container_of(port, struct tegra_xusb_ulpi_port, base);
 }
 
+void tegra_xusb_ulpi_port_release(struct tegra_xusb_port *port);
+
 struct tegra_xusb_hsic_port {
        struct tegra_xusb_port base;
 };
@@ -327,12 +341,15 @@ to_hsic_port(struct tegra_xusb_port *port)
        return container_of(port, struct tegra_xusb_hsic_port, base);
 }
 
+void tegra_xusb_hsic_port_release(struct tegra_xusb_port *port);
+
 struct tegra_xusb_usb3_port {
        struct tegra_xusb_port base;
        struct regulator *supply;
        bool context_saved;
        unsigned int port;
        bool internal;
+       bool disable_gen2;
 
        u32 tap1;
        u32 amp;
@@ -349,8 +366,12 @@ to_usb3_port(struct tegra_xusb_port *port)
 struct tegra_xusb_usb3_port *
 tegra_xusb_find_usb3_port(struct tegra_xusb_padctl *padctl,
                          unsigned int index);
+void tegra_xusb_usb3_port_release(struct tegra_xusb_port *port);
+void tegra_xusb_usb3_port_remove(struct tegra_xusb_port *port);
 
 struct tegra_xusb_port_ops {
+       void (*release)(struct tegra_xusb_port *port);
+       void (*remove)(struct tegra_xusb_port *port);
        int (*enable)(struct tegra_xusb_port *port);
        void (*disable)(struct tegra_xusb_port *port);
        struct tegra_xusb_lane *(*map)(struct tegra_xusb_port *port);
@@ -392,6 +413,7 @@ struct tegra_xusb_padctl_soc {
 
        const char * const *supply_names;
        unsigned int num_supplies;
+       bool supports_gen2;
        bool need_fake_usb3_port;
 };
 
@@ -448,5 +470,8 @@ extern const struct tegra_xusb_padctl_soc tegra210_xusb_padctl_soc;
 #if defined(CONFIG_ARCH_TEGRA_186_SOC)
 extern const struct tegra_xusb_padctl_soc tegra186_xusb_padctl_soc;
 #endif
+#if defined(CONFIG_ARCH_TEGRA_194_SOC)
+extern const struct tegra_xusb_padctl_soc tegra194_xusb_padctl_soc;
+#endif
 
 #endif /* __PHY_TEGRA_XUSB_H */
index 1c536fc03c83c83e29dc57ae622d9220b3b9c92e..7edd5c3bc5361f300758c5d2741022fe306fda32 100644 (file)
@@ -170,6 +170,21 @@ struct phy_gmii_sel_soc_data phy_gmii_sel_soc_dm814 = {
        .regfields = phy_gmii_sel_fields_am33xx,
 };
 
+static const
+struct reg_field phy_gmii_sel_fields_am654[][PHY_GMII_SEL_LAST] = {
+       {
+               [PHY_GMII_SEL_PORT_MODE] = REG_FIELD(0x4040, 0, 1),
+               [PHY_GMII_SEL_RGMII_ID_MODE] = REG_FIELD((~0), 0, 0),
+               [PHY_GMII_SEL_RMII_IO_CLK_EN] = REG_FIELD((~0), 0, 0),
+       },
+};
+
+static const
+struct phy_gmii_sel_soc_data phy_gmii_sel_soc_am654 = {
+       .num_ports = 1,
+       .regfields = phy_gmii_sel_fields_am654,
+};
+
 static const struct of_device_id phy_gmii_sel_id_table[] = {
        {
                .compatible     = "ti,am3352-phy-gmii-sel",
@@ -187,6 +202,10 @@ static const struct of_device_id phy_gmii_sel_id_table[] = {
                .compatible     = "ti,dm814-phy-gmii-sel",
                .data           = &phy_gmii_sel_soc_dm814,
        },
+       {
+               .compatible     = "ti,am654-phy-gmii-sel",
+               .data           = &phy_gmii_sel_soc_am654,
+       },
        {}
 };
 MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table);
index b7980c856898e7382a6562c7c1b97ed17f549505..68c1b93ac5d9ea90eccd3a25ea7b18cf15242ee9 100644 (file)
@@ -147,10 +147,10 @@ static ssize_t boot_acl_show(struct device *dev, struct device_attribute *attr,
 
        for (ret = 0, i = 0; i < tb->nboot_acl; i++) {
                if (!uuid_is_null(&uuids[i]))
-                       ret += snprintf(buf + ret, PAGE_SIZE - ret, "%pUb",
+                       ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%pUb",
                                        &uuids[i]);
 
-               ret += snprintf(buf + ret, PAGE_SIZE - ret, "%s",
+               ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%s",
                               i < tb->nboot_acl - 1 ? "," : "\n");
        }
 
index 921d164b3f35dd3c7cae3fb814d0e2fcab44a850..b451a5aa90b5003256a956186d0867ddf04759db 100644 (file)
@@ -247,7 +247,7 @@ struct tb_drom_entry_header {
 
 struct tb_drom_entry_generic {
        struct tb_drom_entry_header header;
-       u8 data[0];
+       u8 data[];
 } __packed;
 
 struct tb_drom_entry_port {
index 13e88109742e6ff641bca422f1a926d7a76562f7..fbbe32ca1e6906889564a52fe7f8b61a460ca143 100644 (file)
@@ -114,7 +114,7 @@ struct icm_notification {
 struct ep_name_entry {
        u8 len;
        u8 type;
-       u8 data[0];
+       u8 data[];
 };
 
 #define EP_NAME_INTEL_VSS      0x10
index b341fc60c4ba45c472a627f550dd68b272ee05d2..3d084cec136f552a1048df38be78b6c0e6dfb573 100644 (file)
@@ -259,6 +259,7 @@ int usb4_switch_setup(struct tb_switch *sw)
 /**
  * usb4_switch_read_uid() - Read UID from USB4 router
  * @sw: USB4 router
+ * @uid: UID is stored here
  *
  * Reads 64-bit UID from USB4 router config space.
  */
@@ -296,6 +297,9 @@ static int usb4_switch_drom_read_block(struct tb_switch *sw,
 /**
  * usb4_switch_drom_read() - Read arbitrary bytes from USB4 router DROM
  * @sw: USB4 router
+ * @address: Byte address inside DROM to start reading
+ * @buf: Buffer where the DROM content is stored
+ * @size: Number of bytes to read from DROM
  *
  * Uses USB4 router operations to read router DROM. For devices this
  * should always work but for hosts it may return %-EOPNOTSUPP in which
index 635cf0466b5921c103bcf1b1c5bd657cf32e7e71..e9fed9a8873771e326d7bc0e36a89ea66cad655a 100644 (file)
@@ -350,7 +350,7 @@ struct l1_code {
        u8 string_header[E4_L1_STRING_HEADER];
        u8 page_number_to_block_index[E4_MAX_PAGE_NUMBER];
        struct block_index page_header[E4_NO_SWAPPAGE_HEADERS];
-       u8 code[0];
+       u8 code[];
 } __packed;
 
 /* structures describing a block within a DSP page */
index d3bdc4cc47aa8d7519a2b7c43acd07eb297fc079..d96658e2e209996b171a362dc5cd4564536eb5c7 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /******************************************************************************
  *  usbatm.h - Generic USB xDSL driver core
  *
@@ -164,7 +164,7 @@ struct usbatm_data {
        unsigned char *cell_buf;        /* holds partial rx cell */
        unsigned int buf_usage;
 
-       struct urb *urbs[0];
+       struct urb *urbs[];
 };
 
 static inline void *to_usbatm_driver_data(struct usb_interface *intf)
index 3b181d4c7a03827c571c71d367efa6840db376dd..6b6b04a3fe0fa781ff1dda12be58a06558909a10 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * c67x00-hcd.h: Cypress C67X00 USB HCD
  *
index 7ce10928b0376feba3c90ba3dfdae67e511c24e9..a4456d0fffa908b734ad81fde2ae2e3e37e66cef 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * c67x00.h: Cypress C67X00 USB register and field definitions
  *
index b0a29efe7d31706a37c2bc4bbbf66bfc0c718b32..deeea618ba33beccc840169a2ae85d1f6a8445d8 100644 (file)
@@ -201,4 +201,4 @@ MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
 
 MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
 MODULE_LICENSE("GPL v2");
-MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
+MODULE_DESCRIPTION("Cadence USBSS PCI wrapper");
index c6a79ca1585892e142a428bc68837aad9d5d2d52..5685ba11480bd1ab5126a5ccf89c3499bd86238e 100644 (file)
@@ -52,8 +52,8 @@ enum modestrap_mode { USBSS_MODESTRAP_MODE_NONE,
 struct cdns_ti {
        struct device *dev;
        void __iomem *usbss;
-       int usb2_only:1;
-       int vbus_divider:1;
+       unsigned usb2_only:1;
+       unsigned vbus_divider:1;
        struct clk *usb2_refclk;
        struct clk *lpm_clk;
 };
index c2123ef8d8a3320f11f1fa3b7b0363dc3886cd86..4aafba20f4506ba8a2ab1a607c2655676c80122c 100644 (file)
@@ -330,9 +330,9 @@ exit:
  *
  * Returns role
  */
-static enum usb_role cdns3_role_get(struct device *dev)
+static enum usb_role cdns3_role_get(struct usb_role_switch *sw)
 {
-       struct cdns3 *cdns = dev_get_drvdata(dev);
+       struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
 
        return cdns->role;
 }
@@ -346,9 +346,9 @@ static enum usb_role cdns3_role_get(struct device *dev)
  * - Role switch for dual-role devices
  * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
  */
-static int cdns3_role_set(struct device *dev, enum usb_role role)
+static int cdns3_role_set(struct usb_role_switch *sw, enum usb_role role)
 {
-       struct cdns3 *cdns = dev_get_drvdata(dev);
+       struct cdns3 *cdns = usb_role_switch_get_drvdata(sw);
        int ret = 0;
 
        pm_runtime_get_sync(cdns->dev);
@@ -423,12 +423,6 @@ pm_put:
        return ret;
 }
 
-static const struct usb_role_switch_desc cdns3_switch_desc = {
-       .set = cdns3_role_set,
-       .get = cdns3_role_get,
-       .allow_userspace_control = true,
-};
-
 /**
  * cdns3_probe - probe for cdns3 core device
  * @pdev: Pointer to cdns3 core platform device
@@ -437,6 +431,7 @@ static const struct usb_role_switch_desc cdns3_switch_desc = {
  */
 static int cdns3_probe(struct platform_device *pdev)
 {
+       struct usb_role_switch_desc sw_desc = { };
        struct device *dev = &pdev->dev;
        struct resource *res;
        struct cdns3 *cdns;
@@ -529,7 +524,12 @@ static int cdns3_probe(struct platform_device *pdev)
        if (ret)
                goto err3;
 
-       cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc);
+       sw_desc.set = cdns3_role_set;
+       sw_desc.get = cdns3_role_get;
+       sw_desc.allow_userspace_control = true;
+       sw_desc.driver_data = cdns;
+
+       cdns->role_sw = usb_role_switch_register(dev, &sw_desc);
        if (IS_ERR(cdns->role_sw)) {
                ret = PTR_ERR(cdns->role_sw);
                dev_warn(dev, "Unable to register Role Switch\n");
index 3574dbb093667771334060aa0c4d9488ecf66575..372460ea4df9afacd43be9236e5fa6b06176623a 100644 (file)
@@ -1380,7 +1380,7 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
                                  struct cdns3_request *priv_req)
 {
        struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
-       struct cdns3_trb *trb = priv_req->trb;
+       struct cdns3_trb *trb;
        int current_index = 0;
        int handled = 0;
        int doorbell;
index f003a7801872acd277c3f45972a9039d5f89a8f8..52765b098b9e17f69ac96b4b9d0c5582d7f1ec9c 100644 (file)
@@ -1199,7 +1199,7 @@ struct cdns3_aligned_buf {
        void                    *buf;
        dma_addr_t              dma;
        u32                     size;
-       int                     in_use:1;
+       unsigned                in_use:1;
        struct list_head        list;
 };
 
@@ -1308,8 +1308,8 @@ struct cdns3_device {
        unsigned                        u2_allowed:1;
        unsigned                        is_selfpowered:1;
        unsigned                        setup_pending:1;
-       int                             hw_configured_flag:1;
-       int                             wake_up_flag:1;
+       unsigned                        hw_configured_flag:1;
+       unsigned                        wake_up_flag:1;
        unsigned                        status_completion_no_call:1;
        unsigned                        using_streams:1;
        int                             out_mem_is_allocated;
index 98da99510be7f11e5972bd5723fe1d2925a80189..b1540ce9326435d98ef07759a8350c7d58523dae 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * bits.h - register bits of the ChipIdea USB IP core
  *
index d49d5e1235d03645db205a83820f8ee7be4dcd8b..644ecaef17ee2ac9f4080862a508053e21502003 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * ci.h - common structures, functions, and macros of the ChipIdea driver
  *
index de2aac9a2868647ad5463dae8651bd1fffbb7445..c2051aeba13f68cb5144a3c803120e83269bb54b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+/* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Copyright 2012 Freescale Semiconductor, Inc.
  */
index 52139c2a99240ef3f3f997cf8ac26ea03e2bba19..ae0bdc0364643cc5888761a2faf6ad6baec52264 100644 (file)
@@ -600,9 +600,9 @@ static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
        return NOTIFY_DONE;
 }
 
-static enum usb_role ci_usb_role_switch_get(struct device *dev)
+static enum usb_role ci_usb_role_switch_get(struct usb_role_switch *sw)
 {
-       struct ci_hdrc *ci = dev_get_drvdata(dev);
+       struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
        enum usb_role role;
        unsigned long flags;
 
@@ -613,9 +613,10 @@ static enum usb_role ci_usb_role_switch_get(struct device *dev)
        return role;
 }
 
-static int ci_usb_role_switch_set(struct device *dev, enum usb_role role)
+static int ci_usb_role_switch_set(struct usb_role_switch *sw,
+                                 enum usb_role role)
 {
-       struct ci_hdrc *ci = dev_get_drvdata(dev);
+       struct ci_hdrc *ci = usb_role_switch_get_drvdata(sw);
        struct ci_hdrc_cable *cable = NULL;
        enum usb_role current_role = ci_role_to_usb_role(ci);
        enum ci_role ci_role = usb_role_to_ci_role(role);
@@ -1118,6 +1119,7 @@ static int ci_hdrc_probe(struct platform_device *pdev)
        }
 
        if (ci_role_switch.fwnode) {
+               ci_role_switch.driver_data = ci;
                ci->role_switch = usb_role_switch_register(dev,
                                        &ci_role_switch);
                if (IS_ERR(ci->role_switch)) {
index fbfb02e05c97946f6b3b5622dcfa9da36ebdb2fd..be63924ea82efff1ae8e122ae8d3f1a6c67fee91 100644 (file)
@@ -170,6 +170,13 @@ static void ci_handle_id_switch(struct ci_hdrc *ci)
                dev_dbg(ci->dev, "switching from %s to %s\n",
                        ci_role(ci)->name, ci->roles[role]->name);
 
+               if (ci->vbus_active && ci->role == CI_ROLE_GADGET)
+                       /*
+                        * vbus disconnect event is lost due to role
+                        * switch occurs during system suspend.
+                        */
+                       usb_gadget_vbus_disconnect(&ci->gadget);
+
                ci_role_stop(ci);
 
                if (role == CI_ROLE_GADGET &&
index 4f8b8179ec96dc594c65d01b0513d17230730304..5e7a6e571dd2f448f77c8b6eabd4555a62915f6b 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (C) 2013-2014 Freescale Semiconductor, Inc.
  *
index 2b49d29bf2fb0ff4e93400c7d4c0bb6a9287df82..1f5c5ae0e71e782d76ab66390e465affaf8a1bd5 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (C) 2014 Freescale Semiconductor, Inc.
  *
index 4c4ac30db4987b2d18a5619638d8f7e6c3c7f6ea..921bcf14dc06154ad67649292889565605390a73 100644 (file)
@@ -1532,7 +1532,7 @@ static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active)
        struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
 
        if (is_active) {
-               pm_runtime_get_sync(&_gadget->dev);
+               pm_runtime_get_sync(ci->dev);
                hw_device_reset(ci);
                spin_lock_irq(&ci->lock);
                if (ci->driver) {
@@ -1552,7 +1552,7 @@ static void ci_hdrc_gadget_connect(struct usb_gadget *_gadget, int is_active)
                        ci->platdata->notify_event(ci,
                        CI_HDRC_CONTROLLER_STOPPED_EVENT);
                _gadget_stop_activity(&ci->gadget);
-               pm_runtime_put_sync(&_gadget->dev);
+               pm_runtime_put_sync(ci->dev);
                usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
        }
 }
@@ -1637,12 +1637,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
        if (ci_otg_is_fsm_mode(ci) || ci->role == CI_ROLE_HOST)
                return 0;
 
-       pm_runtime_get_sync(&ci->gadget.dev);
+       pm_runtime_get_sync(ci->dev);
        if (is_on)
                hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
        else
                hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
-       pm_runtime_put_sync(&ci->gadget.dev);
+       pm_runtime_put_sync(ci->dev);
 
        return 0;
 }
@@ -1840,7 +1840,7 @@ static int ci_udc_stop(struct usb_gadget *gadget)
                        CI_HDRC_CONTROLLER_STOPPED_EVENT);
                _gadget_stop_activity(&ci->gadget);
                spin_lock_irqsave(&ci->lock, flags);
-               pm_runtime_put(&ci->gadget.dev);
+               pm_runtime_put(ci->dev);
        }
 
        spin_unlock_irqrestore(&ci->lock, flags);
@@ -1971,9 +1971,6 @@ static int udc_start(struct ci_hdrc *ci)
        if (retval)
                goto destroy_eps;
 
-       pm_runtime_no_callbacks(&ci->gadget.dev);
-       pm_runtime_enable(&ci->gadget.dev);
-
        return retval;
 
 destroy_eps:
index e023735d94b7a7594d174c45ff4a74b63cccb7db..ebb11b625bb8fa84e0518436e835b647d0234333 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * udc.h - ChipIdea UDC structures
  *
index 47f09a6ce7bda47a28d2b4390bce144548e49d6e..84d6f7df09a4ef453dd0ece34aaaafd32ab584bb 100644 (file)
@@ -923,16 +923,16 @@ static int set_serial_info(struct tty_struct *tty, struct serial_struct *ss)
 
        mutex_lock(&acm->port.mutex);
 
-       if ((ss->close_delay != old_close_delay) ||
-            (ss->closing_wait != old_closing_wait)) {
-               if (!capable(CAP_SYS_ADMIN))
+       if (!capable(CAP_SYS_ADMIN)) {
+               if ((ss->close_delay != old_close_delay) ||
+                   (ss->closing_wait != old_closing_wait))
                        retval = -EPERM;
-               else {
-                       acm->port.close_delay  = close_delay;
-                       acm->port.closing_wait = closing_wait;
-               }
-       } else
-               retval = -EOPNOTSUPP;
+               else
+                       retval = -EOPNOTSUPP;
+       } else {
+               acm->port.close_delay  = close_delay;
+               acm->port.closing_wait = closing_wait;
+       }
 
        mutex_unlock(&acm->port.mutex);
        return retval;
index 2b27d232d7a75f29af95867ab710fcbb5b8160a9..f81606c6a35b03b92b7d0da2976c2e7a3800dfa7 100644 (file)
@@ -261,9 +261,19 @@ static int usb_probe_device(struct device *dev)
         */
        if (!udriver->supports_autosuspend)
                error = usb_autoresume_device(udev);
+       if (error)
+               return error;
 
-       if (!error)
-               error = udriver->probe(udev);
+       if (udriver->generic_subclass)
+               error = usb_generic_driver_probe(udev);
+       if (error)
+               return error;
+
+       error = udriver->probe(udev);
+       if (error == -ENODEV && udriver != &usb_generic_driver) {
+               udev->use_generic_driver = 1;
+               return -EPROBE_DEFER;
+       }
        return error;
 }
 
@@ -273,7 +283,10 @@ static int usb_unbind_device(struct device *dev)
        struct usb_device *udev = to_usb_device(dev);
        struct usb_device_driver *udriver = to_usb_device_driver(dev->driver);
 
-       udriver->disconnect(udev);
+       if (udriver->disconnect)
+               udriver->disconnect(udev);
+       if (udriver->generic_subclass)
+               usb_generic_driver_disconnect(udev);
        if (!udriver->supports_autosuspend)
                usb_autosuspend_device(udev);
        return 0;
@@ -790,17 +803,42 @@ const struct usb_device_id *usb_match_id(struct usb_interface *interface,
 }
 EXPORT_SYMBOL_GPL(usb_match_id);
 
+const struct usb_device_id *usb_device_match_id(struct usb_device *udev,
+                               const struct usb_device_id *id)
+{
+       if (!id)
+               return NULL;
+
+       for (; id->idVendor || id->idProduct ; id++) {
+               if (usb_match_device(udev, id))
+                       return id;
+       }
+
+       return NULL;
+}
+
 static int usb_device_match(struct device *dev, struct device_driver *drv)
 {
        /* devices and interfaces are handled separately */
        if (is_usb_device(dev)) {
+               struct usb_device *udev;
+               struct usb_device_driver *udrv;
 
                /* interface drivers never match devices */
                if (!is_usb_device_driver(drv))
                        return 0;
 
-               /* TODO: Add real matching code */
-               return 1;
+               udev = to_usb_device(dev);
+               udrv = to_usb_device_driver(drv);
+
+               if (udrv->id_table &&
+                   usb_device_match_id(udev, udrv->id_table) != NULL) {
+                       return 1;
+               }
+
+               if (udrv->match)
+                       return udrv->match(udev);
+               return 0;
 
        } else if (is_usb_interface(dev)) {
                struct usb_interface *intf;
@@ -1149,7 +1187,10 @@ static int usb_suspend_device(struct usb_device *udev, pm_message_t msg)
                udev->do_remote_wakeup = 0;
                udriver = &usb_generic_driver;
        }
-       status = udriver->suspend(udev, msg);
+       if (udriver->suspend)
+               status = udriver->suspend(udev, msg);
+       if (status == 0 && udriver->generic_subclass)
+               status = usb_generic_driver_suspend(udev, msg);
 
  done:
        dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
@@ -1181,7 +1222,10 @@ static int usb_resume_device(struct usb_device *udev, pm_message_t msg)
                udev->reset_resume = 1;
 
        udriver = to_usb_device_driver(udev->dev.driver);
-       status = udriver->resume(udev, msg);
+       if (udriver->generic_subclass)
+               status = usb_generic_driver_resume(udev, msg);
+       if (status == 0 && udriver->resume)
+               status = udriver->resume(udev, msg);
 
  done:
        dev_vdbg(&udev->dev, "%s: status %d\n", __func__, status);
index 38f8b3e317628a8aab57fd022a69e7ecf4e862bf..4626227a6dd223c037907dc999412486d8b581bd 100644 (file)
@@ -195,7 +195,38 @@ int usb_choose_configuration(struct usb_device *udev)
 }
 EXPORT_SYMBOL_GPL(usb_choose_configuration);
 
-static int generic_probe(struct usb_device *udev)
+static int __check_usb_generic(struct device_driver *drv, void *data)
+{
+       struct usb_device *udev = data;
+       struct usb_device_driver *udrv;
+
+       if (!is_usb_device_driver(drv))
+               return 0;
+       udrv = to_usb_device_driver(drv);
+       if (udrv == &usb_generic_driver)
+               return 0;
+       if (!udrv->id_table)
+               return 0;
+
+       return usb_device_match_id(udev, udrv->id_table) != NULL;
+}
+
+static bool usb_generic_driver_match(struct usb_device *udev)
+{
+       if (udev->use_generic_driver)
+               return true;
+
+       /*
+        * If any other driver wants the device, leave the device to this other
+        * driver.
+        */
+       if (bus_for_each_drv(&usb_bus_type, NULL, udev, __check_usb_generic))
+               return false;
+
+       return true;
+}
+
+int usb_generic_driver_probe(struct usb_device *udev)
 {
        int err, c;
 
@@ -222,7 +253,7 @@ static int generic_probe(struct usb_device *udev)
        return 0;
 }
 
-static void generic_disconnect(struct usb_device *udev)
+void usb_generic_driver_disconnect(struct usb_device *udev)
 {
        usb_notify_remove_device(udev);
 
@@ -234,7 +265,7 @@ static void generic_disconnect(struct usb_device *udev)
 
 #ifdef CONFIG_PM
 
-static int generic_suspend(struct usb_device *udev, pm_message_t msg)
+int usb_generic_driver_suspend(struct usb_device *udev, pm_message_t msg)
 {
        int rc;
 
@@ -262,7 +293,7 @@ static int generic_suspend(struct usb_device *udev, pm_message_t msg)
        return rc;
 }
 
-static int generic_resume(struct usb_device *udev, pm_message_t msg)
+int usb_generic_driver_resume(struct usb_device *udev, pm_message_t msg)
 {
        int rc;
 
@@ -285,11 +316,12 @@ static int generic_resume(struct usb_device *udev, pm_message_t msg)
 
 struct usb_device_driver usb_generic_driver = {
        .name = "usb",
-       .probe = generic_probe,
-       .disconnect = generic_disconnect,
+       .match = usb_generic_driver_match,
+       .probe = usb_generic_driver_probe,
+       .disconnect = usb_generic_driver_disconnect,
 #ifdef CONFIG_PM
-       .suspend = generic_suspend,
-       .resume = generic_resume,
+       .suspend = usb_generic_driver_suspend,
+       .resume = usb_generic_driver_resume,
 #endif
        .supports_autosuspend = 1,
 };
index 5adf489428aad5dd422121678fc86a3b2da8c71e..d5f834f16993258a4ad715224e54543510c51b0c 100644 (file)
@@ -5,6 +5,7 @@
  * Released under the GPLv2 only.
  */
 
+#include <linux/acpi.h>
 #include <linux/pci.h> /* for scatterlist macros */
 #include <linux/usb.h>
 #include <linux/module.h>
@@ -1941,6 +1942,7 @@ free_interfaces:
                        intf->dev.of_node = usb_of_get_interface_node(dev,
                                        configuration, ifnum);
                }
+               ACPI_COMPANION_SET(&intf->dev, ACPI_COMPANION(&dev->dev));
                intf->dev.driver = NULL;
                intf->dev.bus = &usb_bus_type;
                intf->dev.type = &usb_if_device_type;
index f19694e69f5c3eda9f58ba317a81b43096d8228d..9f4320b9d7fc00af99ee55b94b6917038f791992 100644 (file)
@@ -849,7 +849,7 @@ static struct attribute *dev_string_attrs[] = {
 static umode_t dev_string_attrs_are_visible(struct kobject *kobj,
                struct attribute *a, int n)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct usb_device *udev = to_usb_device(dev);
 
        if (a == &dev_attr_manufacturer.attr) {
@@ -883,7 +883,7 @@ read_descriptors(struct file *filp, struct kobject *kobj,
                struct bin_attribute *attr,
                char *buf, loff_t off, size_t count)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct usb_device *udev = to_usb_device(dev);
        size_t nleft = count;
        size_t srclen, n;
@@ -1233,7 +1233,7 @@ static struct attribute *intf_assoc_attrs[] = {
 static umode_t intf_assoc_attrs_are_visible(struct kobject *kobj,
                struct attribute *a, int n)
 {
-       struct device *dev = container_of(kobj, struct device, kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct usb_interface *intf = to_usb_interface(dev);
 
        if (intf->intf_assoc == NULL)
index 9043d7242d67ee40001a3b978e09097928373787..50b2fc7fcc0e32576d4527ee08bb861ad97537b5 100644 (file)
@@ -86,7 +86,7 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
 {
        enum usb_port_connect_type connect_type = USB_PORT_CONNECT_TYPE_UNKNOWN;
        struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-       union acpi_object *upc;
+       union acpi_object *upc = NULL;
        acpi_status status;
 
        /*
@@ -98,11 +98,12 @@ static enum usb_port_connect_type usb_acpi_get_connect_type(acpi_handle handle,
         * no connectable, the port would be not used.
         */
        status = acpi_evaluate_object(handle, "_UPC", NULL, &buffer);
+       if (ACPI_FAILURE(status))
+               goto out;
+
        upc = buffer.pointer;
-       if (!upc || (upc->type != ACPI_TYPE_PACKAGE)
-               || upc->package.count != 4) {
+       if (!upc || (upc->type != ACPI_TYPE_PACKAGE) || upc->package.count != 4)
                goto out;
-       }
 
        if (upc->package.elements[0].integer.value)
                if (pld->user_visible)
@@ -186,7 +187,7 @@ usb_acpi_find_companion_for_port(struct usb_port *port_dev)
 
        handle = adev->handle;
        status = acpi_get_physical_device_location(handle, &pld);
-       if (!ACPI_FAILURE(status) && pld) {
+       if (ACPI_SUCCESS(status) && pld) {
                port_dev->location = USB_ACPI_LOCATION_VALID
                        | pld->group_token << 8 | pld->group_position;
                port_dev->connect_type = usb_acpi_get_connect_type(handle, pld);
index 3ad0ee57e859fb00990bcf733112c553b25c65b1..64ed4023a8c8823fb624cb94d9941324ecd4e460 100644 (file)
@@ -50,6 +50,12 @@ extern void usb_release_bos_descriptor(struct usb_device *dev);
 extern char *usb_cache_string(struct usb_device *udev, int index);
 extern int usb_set_configuration(struct usb_device *dev, int configuration);
 extern int usb_choose_configuration(struct usb_device *udev);
+extern int usb_generic_driver_probe(struct usb_device *udev);
+extern void usb_generic_driver_disconnect(struct usb_device *udev);
+extern int usb_generic_driver_suspend(struct usb_device *udev,
+               pm_message_t msg);
+extern int usb_generic_driver_resume(struct usb_device *udev,
+               pm_message_t msg);
 
 static inline unsigned usb_get_max_power(struct usb_device *udev,
                struct usb_host_config *c)
@@ -66,6 +72,8 @@ extern int usb_match_one_id_intf(struct usb_device *dev,
                                 const struct usb_device_id *id);
 extern int usb_match_device(struct usb_device *dev,
                            const struct usb_device_id *id);
+extern const struct usb_device_id *usb_device_match_id(struct usb_device *udev,
+                               const struct usb_device_id *id);
 extern void usb_forced_unbind_intf(struct usb_interface *intf);
 extern void usb_unbind_and_rebind_marked_interfaces(struct usb_device *udev);
 
index 968e03b89d04d5d89b75d468925537305a25f143..99b0bdfe0012baa7bec0119f76e6e30c7d06372a 100644 (file)
@@ -411,6 +411,10 @@ enum dwc2_ep0_state {
  *                     register.
  *                     0 - Deactivate the transceiver (default)
  *                     1 - Activate the transceiver
+ * @activate_stm_id_vb_detection: Activate external ID pin and Vbus level
+ *                     detection using GGPIO register.
+ *                     0 - Deactivate the external level detection (default)
+ *                     1 - Activate the external level detection
  * @g_dma:              Enables gadget dma usage (default: autodetect).
  * @g_dma_desc:         Enables gadget descriptor DMA (default: autodetect).
  * @g_rx_fifo_size:    The periodic rx fifo size for the device, in
@@ -481,6 +485,7 @@ struct dwc2_core_params {
        bool service_interval;
        u8 hird_threshold;
        bool activate_stm_fs_transceiver;
+       bool activate_stm_id_vb_detection;
        bool ipg_isoc_en;
        u16 max_packet_count;
        u32 max_transfer_size;
@@ -874,6 +879,8 @@ struct dwc2_hregs_backup {
  *                      removed once all SoCs support usb transceiver.
  * @supplies:           Definition of USB power supplies
  * @vbus_supply:        Regulator supplying vbus.
+ * @usb33d:            Optional 3.3v regulator used on some stm32 devices to
+ *                     supply ID and VBUS detection hardware.
  * @lock:              Spinlock that protects all the driver data structures
  * @priv:              Stores a pointer to the struct usb_hcd
  * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth
@@ -1061,6 +1068,7 @@ struct dwc2_hsotg {
        struct dwc2_hsotg_plat *plat;
        struct regulator_bulk_data supplies[DWC2_NUM_SUPPLIES];
        struct regulator *vbus_supply;
+       struct regulator *usb33d;
 
        spinlock_t lock;
        void *priv;
index 92ed32ec160769783a14729a9aa31575057c22f3..12b98b4662872dcc76fb6f46d6741d0b3e5425f4 100644 (file)
@@ -1646,7 +1646,8 @@ static int dwc2_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
 
        switch (ctrl->bRequestType & USB_RECIP_MASK) {
        case USB_RECIP_DEVICE:
-               status = 1 << USB_DEVICE_SELF_POWERED;
+               status = hsotg->gadget.is_selfpowered <<
+                        USB_DEVICE_SELF_POWERED;
                status |= hsotg->remote_wakeup_allowed <<
                          USB_DEVICE_REMOTE_WAKEUP;
                reply = cpu_to_le16(status);
@@ -4527,6 +4528,26 @@ static int dwc2_hsotg_gadget_getframe(struct usb_gadget *gadget)
        return dwc2_hsotg_read_frameno(to_hsotg(gadget));
 }
 
+/**
+ * dwc2_hsotg_set_selfpowered - set if device is self/bus powered
+ * @gadget: The usb gadget state
+ * @is_selfpowered: Whether the device is self-powered
+ *
+ * Set if the device is self or bus powered.
+ */
+static int dwc2_hsotg_set_selfpowered(struct usb_gadget *gadget,
+                                     int is_selfpowered)
+{
+       struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+       unsigned long flags;
+
+       spin_lock_irqsave(&hsotg->lock, flags);
+       gadget->is_selfpowered = !!is_selfpowered;
+       spin_unlock_irqrestore(&hsotg->lock, flags);
+
+       return 0;
+}
+
 /**
  * dwc2_hsotg_pullup - connect/disconnect the USB PHY
  * @gadget: The usb gadget state
@@ -4618,6 +4639,7 @@ static int dwc2_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
 
 static const struct usb_gadget_ops dwc2_hsotg_gadget_ops = {
        .get_frame      = dwc2_hsotg_gadget_getframe,
+       .set_selfpowered        = dwc2_hsotg_set_selfpowered,
        .udc_start              = dwc2_hsotg_udc_start,
        .udc_stop               = dwc2_hsotg_udc_stop,
        .pullup                 = dwc2_hsotg_pullup,
index 8ca6d12a6f5701318d9665ad950d3be82f657e6e..1224fa9df604b4760077b2d063f057d9bbe7b425 100644 (file)
@@ -199,7 +199,7 @@ struct dwc2_hcd_urb {
        u32 flags;
        u16 interval;
        struct dwc2_hcd_pipe_info pipe_info;
-       struct dwc2_hcd_iso_packet_desc iso_descs[0];
+       struct dwc2_hcd_iso_packet_desc iso_descs[];
 };
 
 /* Phases for control transfers */
index 510e87ec0be84e6fe9fb98d5a529e8f228eb1d5d..c4027bbcedec49ccb314da05daced62c414eede3 100644 (file)
 #define GOTGCTL_HSTSETHNPEN            BIT(10)
 #define GOTGCTL_HNPREQ                 BIT(9)
 #define GOTGCTL_HSTNEGSCS              BIT(8)
+#define GOTGCTL_BVALOVAL               BIT(7)
+#define GOTGCTL_BVALOEN                        BIT(6)
+#define GOTGCTL_AVALOVAL               BIT(5)
+#define GOTGCTL_AVALOEN                        BIT(4)
+#define GOTGCTL_VBVALOVAL              BIT(3)
+#define GOTGCTL_VBVALOEN               BIT(2)
 #define GOTGCTL_SESREQ                 BIT(1)
 #define GOTGCTL_SESREQSCS              BIT(0)
 
 #define GPVNDCTL                       HSOTG_REG(0x0034)
 #define GGPIO                          HSOTG_REG(0x0038)
 #define GGPIO_STM32_OTG_GCCFG_PWRDWN   BIT(16)
+#define GGPIO_STM32_OTG_GCCFG_VBDEN    BIT(21)
+#define GGPIO_STM32_OTG_GCCFG_IDEN     BIT(22)
 
 #define GUID                           HSOTG_REG(0x003c)
 #define GSNPSID                                HSOTG_REG(0x0040)
index 31e090ac9f1ecb47ee16aa961c77ed2aeac8468f..8ccc83f7eb3fcfe1b4b511734a8ff90e1e54b0eb 100644 (file)
@@ -163,6 +163,35 @@ static void dwc2_set_stm32f7_hsotg_params(struct dwc2_hsotg *hsotg)
        p->host_perio_tx_fifo_size = 256;
 }
 
+static void dwc2_set_stm32mp15_fsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+       p->speed = DWC2_SPEED_PARAM_FULL;
+       p->host_rx_fifo_size = 128;
+       p->host_nperio_tx_fifo_size = 96;
+       p->host_perio_tx_fifo_size = 96;
+       p->max_packet_count = 256;
+       p->phy_type = DWC2_PHY_TYPE_PARAM_FS;
+       p->i2c_enable = false;
+       p->activate_stm_fs_transceiver = true;
+       p->activate_stm_id_vb_detection = true;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
+static void dwc2_set_stm32mp15_hsotg_params(struct dwc2_hsotg *hsotg)
+{
+       struct dwc2_core_params *p = &hsotg->params;
+
+       p->otg_cap = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE;
+       p->activate_stm_id_vb_detection = true;
+       p->host_rx_fifo_size = 440;
+       p->host_nperio_tx_fifo_size = 256;
+       p->host_perio_tx_fifo_size = 256;
+       p->power_down = DWC2_POWER_DOWN_PARAM_NONE;
+}
+
 const struct of_device_id dwc2_of_match_table[] = {
        { .compatible = "brcm,bcm2835-usb", .data = dwc2_set_bcm_params },
        { .compatible = "hisilicon,hi6220-usb", .data = dwc2_set_his_params  },
@@ -186,6 +215,10 @@ const struct of_device_id dwc2_of_match_table[] = {
        { .compatible = "st,stm32f4x9-hsotg" },
        { .compatible = "st,stm32f7-hsotg",
          .data = dwc2_set_stm32f7_hsotg_params },
+       { .compatible = "st,stm32mp15-fsotg",
+         .data = dwc2_set_stm32mp15_fsotg_params },
+       { .compatible = "st,stm32mp15-hsotg",
+         .data = dwc2_set_stm32mp15_hsotg_params },
        {},
 };
 MODULE_DEVICE_TABLE(of, dwc2_of_match_table);
index 3c6ce09a6db569103ed830c57c4212f88428c512..69972750e161252e676e0f3b749194cf2bc31bf5 100644 (file)
@@ -285,7 +285,9 @@ static int dwc2_lowlevel_hw_init(struct dwc2_hsotg *hsotg)
        ret = devm_regulator_bulk_get(hsotg->dev, ARRAY_SIZE(hsotg->supplies),
                                      hsotg->supplies);
        if (ret) {
-               dev_err(hsotg->dev, "failed to request supplies: %d\n", ret);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(hsotg->dev, "failed to request supplies: %d\n",
+                               ret);
                return ret;
        }
        return 0;
@@ -312,6 +314,9 @@ static int dwc2_driver_remove(struct platform_device *dev)
        if (hsotg->gadget_enabled)
                dwc2_hsotg_remove(hsotg);
 
+       if (hsotg->params.activate_stm_id_vb_detection)
+               regulator_disable(hsotg->usb33d);
+
        if (hsotg->ll_hw_enabled)
                dwc2_lowlevel_hw_disable(hsotg);
 
@@ -392,8 +397,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
                return retval;
        }
 
-       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
-       hsotg->regs = devm_ioremap_resource(&dev->dev, res);
+       hsotg->regs = devm_platform_get_and_ioremap_resource(dev, 0, &res);
        if (IS_ERR(hsotg->regs))
                return PTR_ERR(hsotg->regs);
 
@@ -464,10 +468,35 @@ static int dwc2_driver_probe(struct platform_device *dev)
        if (retval)
                goto error;
 
+       if (hsotg->params.activate_stm_id_vb_detection) {
+               u32 ggpio;
+
+               hsotg->usb33d = devm_regulator_get(hsotg->dev, "usb33d");
+               if (IS_ERR(hsotg->usb33d)) {
+                       retval = PTR_ERR(hsotg->usb33d);
+                       if (retval != -EPROBE_DEFER)
+                               dev_err(hsotg->dev,
+                                       "failed to request usb33d supply: %d\n",
+                                       retval);
+                       goto error;
+               }
+               retval = regulator_enable(hsotg->usb33d);
+               if (retval) {
+                       dev_err(hsotg->dev,
+                               "failed to enable usb33d supply: %d\n", retval);
+                       goto error;
+               }
+
+               ggpio = dwc2_readl(hsotg, GGPIO);
+               ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
+               ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
+               dwc2_writel(hsotg, ggpio, GGPIO);
+       }
+
        if (hsotg->dr_mode != USB_DR_MODE_HOST) {
                retval = dwc2_gadget_init(hsotg);
                if (retval)
-                       goto error;
+                       goto error_init;
                hsotg->gadget_enabled = 1;
        }
 
@@ -493,7 +522,7 @@ static int dwc2_driver_probe(struct platform_device *dev)
                if (retval) {
                        if (hsotg->gadget_enabled)
                                dwc2_hsotg_remove(hsotg);
-                       goto error;
+                       goto error_init;
                }
                hsotg->hcd_enabled = 1;
        }
@@ -509,6 +538,9 @@ static int dwc2_driver_probe(struct platform_device *dev)
 
        return 0;
 
+error_init:
+       if (hsotg->params.activate_stm_id_vb_detection)
+               regulator_disable(hsotg->usb33d);
 error:
        dwc2_lowlevel_hw_disable(hsotg);
        return retval;
@@ -523,6 +555,37 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
        if (is_device_mode)
                dwc2_hsotg_suspend(dwc2);
 
+       if (dwc2->params.activate_stm_id_vb_detection) {
+               unsigned long flags;
+               u32 ggpio, gotgctl;
+
+               /*
+                * Need to force the mode to the current mode to avoid Mode
+                * Mismatch Interrupt when ID detection will be disabled.
+                */
+               dwc2_force_mode(dwc2, !is_device_mode);
+
+               spin_lock_irqsave(&dwc2->lock, flags);
+               gotgctl = dwc2_readl(dwc2, GOTGCTL);
+               /* bypass debounce filter, enable overrides */
+               gotgctl |= GOTGCTL_DBNCE_FLTR_BYPASS;
+               gotgctl |= GOTGCTL_BVALOEN | GOTGCTL_AVALOEN;
+               /* Force A / B session if needed */
+               if (gotgctl & GOTGCTL_ASESVLD)
+                       gotgctl |= GOTGCTL_AVALOVAL;
+               if (gotgctl & GOTGCTL_BSESVLD)
+                       gotgctl |= GOTGCTL_BVALOVAL;
+               dwc2_writel(dwc2, gotgctl, GOTGCTL);
+               spin_unlock_irqrestore(&dwc2->lock, flags);
+
+               ggpio = dwc2_readl(dwc2, GGPIO);
+               ggpio &= ~GGPIO_STM32_OTG_GCCFG_IDEN;
+               ggpio &= ~GGPIO_STM32_OTG_GCCFG_VBDEN;
+               dwc2_writel(dwc2, ggpio, GGPIO);
+
+               regulator_disable(dwc2->usb33d);
+       }
+
        if (dwc2->ll_hw_enabled &&
            (is_device_mode || dwc2_host_can_poweroff_phy(dwc2))) {
                ret = __dwc2_lowlevel_hw_disable(dwc2);
@@ -544,6 +607,34 @@ static int __maybe_unused dwc2_resume(struct device *dev)
        }
        dwc2->phy_off_for_suspend = false;
 
+       if (dwc2->params.activate_stm_id_vb_detection) {
+               unsigned long flags;
+               u32 ggpio, gotgctl;
+
+               ret = regulator_enable(dwc2->usb33d);
+               if (ret)
+                       return ret;
+
+               ggpio = dwc2_readl(dwc2, GGPIO);
+               ggpio |= GGPIO_STM32_OTG_GCCFG_IDEN;
+               ggpio |= GGPIO_STM32_OTG_GCCFG_VBDEN;
+               dwc2_writel(dwc2, ggpio, GGPIO);
+
+               /* ID/VBUS detection startup time */
+               usleep_range(5000, 7000);
+
+               spin_lock_irqsave(&dwc2->lock, flags);
+               gotgctl = dwc2_readl(dwc2, GOTGCTL);
+               gotgctl &= ~GOTGCTL_DBNCE_FLTR_BYPASS;
+               gotgctl &= ~(GOTGCTL_BVALOEN | GOTGCTL_AVALOEN |
+                            GOTGCTL_BVALOVAL | GOTGCTL_AVALOVAL);
+               dwc2_writel(dwc2, gotgctl, GOTGCTL);
+               spin_unlock_irqrestore(&dwc2->lock, flags);
+       }
+
+       /* Need to restore FORCEDEVMODE/FORCEHOSTMODE */
+       dwc2_force_dr_mode(dwc2);
+
        if (dwc2_is_device_mode(dwc2))
                ret = dwc2_hsotg_resume(dwc2);
 
index 1d85c42b9c674230ddab70e4d346a03abe2e60ff..edc17155cb2be9d1dc19f6a109fc3b90b1abcf57 100644 (file)
@@ -289,12 +289,6 @@ done:
        return 0;
 }
 
-static const struct clk_bulk_data dwc3_core_clks[] = {
-       { .id = "ref" },
-       { .id = "bus_early" },
-       { .id = "suspend" },
-};
-
 /*
  * dwc3_frame_length_adjustment - Adjusts frame length if required
  * @dwc3: Pointer to our controller context structure
@@ -1029,6 +1023,9 @@ static int dwc3_core_init(struct dwc3 *dwc)
                if (dwc->dis_tx_ipgap_linecheck_quirk)
                        reg |= DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS;
 
+               if (dwc->parkmode_disable_ss_quirk)
+                       reg |= DWC3_GUCTL1_PARKMODE_DISABLE_SS;
+
                dwc3_writel(dwc->regs, DWC3_GUCTL1, reg);
        }
 
@@ -1342,6 +1339,8 @@ static void dwc3_get_properties(struct dwc3 *dwc)
                                "snps,dis-del-phy-power-chg-quirk");
        dwc->dis_tx_ipgap_linecheck_quirk = device_property_read_bool(dev,
                                "snps,dis-tx-ipgap-linecheck-quirk");
+       dwc->parkmode_disable_ss_quirk = device_property_read_bool(dev,
+                               "snps,parkmode-disable-ss-quirk");
 
        dwc->tx_de_emphasis_quirk = device_property_read_bool(dev,
                                "snps,tx_de_emphasis_quirk");
@@ -1441,11 +1440,6 @@ static int dwc3_probe(struct platform_device *pdev)
        if (!dwc)
                return -ENOMEM;
 
-       dwc->clks = devm_kmemdup(dev, dwc3_core_clks, sizeof(dwc3_core_clks),
-                                GFP_KERNEL);
-       if (!dwc->clks)
-               return -ENOMEM;
-
        dwc->dev = dev;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1476,22 +1470,23 @@ static int dwc3_probe(struct platform_device *pdev)
 
        dwc3_get_properties(dwc);
 
-       dwc->reset = devm_reset_control_get_optional_shared(dev, NULL);
+       dwc->reset = devm_reset_control_array_get(dev, true, true);
        if (IS_ERR(dwc->reset))
                return PTR_ERR(dwc->reset);
 
        if (dev->of_node) {
-               dwc->num_clks = ARRAY_SIZE(dwc3_core_clks);
-
-               ret = devm_clk_bulk_get(dev, dwc->num_clks, dwc->clks);
+               ret = devm_clk_bulk_get_all(dev, &dwc->clks);
                if (ret == -EPROBE_DEFER)
                        return ret;
                /*
                 * Clocks are optional, but new DT platforms should support all
                 * clocks as required by the DT-binding.
                 */
-               if (ret)
+               if (ret < 0)
                        dwc->num_clks = 0;
+               else
+                       dwc->num_clks = ret;
+
        }
 
        ret = reset_control_deassert(dwc->reset);
@@ -1637,6 +1632,8 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg)
 
        switch (dwc->current_dr_role) {
        case DWC3_GCTL_PRTCAP_DEVICE:
+               if (pm_runtime_suspended(dwc->dev))
+                       break;
                spin_lock_irqsave(&dwc->lock, flags);
                dwc3_gadget_suspend(dwc);
                spin_unlock_irqrestore(&dwc->lock, flags);
index 77c4a9abe3652679993bfb92527dc666083f4226..6846eb0cba135236768d559277b9970731b09dc8 100644 (file)
@@ -25,6 +25,7 @@
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
+#include <linux/usb/role.h>
 #include <linux/ulpi/interface.h>
 
 #include <linux/phy/phy.h>
 #define DWC3_GUCTL_HSTINAUTORETRY      BIT(14)
 
 /* Global User Control 1 Register */
+#define DWC3_GUCTL1_PARKMODE_DISABLE_SS        BIT(17)
 #define DWC3_GUCTL1_TX_IPGAP_LINECHECK_DIS     BIT(28)
 #define DWC3_GUCTL1_DEV_L1_EXIT_BY_HW  BIT(24)
 
@@ -953,6 +955,9 @@ struct dwc3_scratchpad_array {
  * @hsphy_mode: UTMI phy mode, one of following:
  *             - USBPHY_INTERFACE_MODE_UTMI
  *             - USBPHY_INTERFACE_MODE_UTMIW
+ * @role_sw: usb_role_switch handle
+ * @role_switch_default_mode: default operation mode of controller while
+ *                     usb role is USB_ROLE_NONE.
  * @usb2_phy: pointer to USB2 PHY
  * @usb3_phy: pointer to USB3 PHY
  * @usb2_generic_phy: pointer to USB2 PHY
@@ -1024,6 +1029,8 @@ struct dwc3_scratchpad_array {
  *                     change quirk.
  * @dis_tx_ipgap_linecheck_quirk: set if we disable u2mac linestate
  *                     check during HS transmit.
+ * @parkmode_disable_ss_quirk: set if we need to disable all SuperSpeed
+ *                     instances in park mode.
  * @tx_de_emphasis_quirk: set if we enable Tx de-emphasis quirk
  * @tx_de_emphasis: Tx de-emphasis value
  *     0       - -6dB de-emphasis
@@ -1086,6 +1093,8 @@ struct dwc3 {
        struct extcon_dev       *edev;
        struct notifier_block   edev_nb;
        enum usb_phy_interface  hsphy_mode;
+       struct usb_role_switch  *role_sw;
+       enum usb_dr_mode        role_switch_default_mode;
 
        u32                     fladj;
        u32                     irq_gadget;
@@ -1215,6 +1224,7 @@ struct dwc3 {
        unsigned                dis_u2_freeclk_exists_quirk:1;
        unsigned                dis_del_phy_power_chg_quirk:1;
        unsigned                dis_tx_ipgap_linecheck_quirk:1;
+       unsigned                parkmode_disable_ss_quirk:1;
 
        unsigned                tx_de_emphasis_quirk:1;
        unsigned                tx_de_emphasis:2;
index c946d64142ad77979908b9f54847790c13baa341..7db1ffc92bbd58258856dc38a952516f4159db7b 100644 (file)
@@ -476,6 +476,94 @@ static struct extcon_dev *dwc3_get_extcon(struct dwc3 *dwc)
        return edev;
 }
 
+#if IS_ENABLED(CONFIG_USB_ROLE_SWITCH)
+#define ROLE_SWITCH 1
+static int dwc3_usb_role_switch_set(struct usb_role_switch *sw,
+                                   enum usb_role role)
+{
+       struct dwc3 *dwc = usb_role_switch_get_drvdata(sw);
+       u32 mode;
+
+       switch (role) {
+       case USB_ROLE_HOST:
+               mode = DWC3_GCTL_PRTCAP_HOST;
+               break;
+       case USB_ROLE_DEVICE:
+               mode = DWC3_GCTL_PRTCAP_DEVICE;
+               break;
+       default:
+               if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
+                       mode = DWC3_GCTL_PRTCAP_HOST;
+               else
+                       mode = DWC3_GCTL_PRTCAP_DEVICE;
+               break;
+       }
+
+       dwc3_set_mode(dwc, mode);
+       return 0;
+}
+
+static enum usb_role dwc3_usb_role_switch_get(struct usb_role_switch *sw)
+{
+       struct dwc3 *dwc = usb_role_switch_get_drvdata(sw);
+       unsigned long flags;
+       enum usb_role role;
+
+       spin_lock_irqsave(&dwc->lock, flags);
+       switch (dwc->current_dr_role) {
+       case DWC3_GCTL_PRTCAP_HOST:
+               role = USB_ROLE_HOST;
+               break;
+       case DWC3_GCTL_PRTCAP_DEVICE:
+               role = USB_ROLE_DEVICE;
+               break;
+       case DWC3_GCTL_PRTCAP_OTG:
+               role = dwc->current_otg_role;
+               break;
+       default:
+               if (dwc->role_switch_default_mode == USB_DR_MODE_HOST)
+                       role = USB_ROLE_HOST;
+               else
+                       role = USB_ROLE_DEVICE;
+               break;
+       }
+       spin_unlock_irqrestore(&dwc->lock, flags);
+       return role;
+}
+
+static int dwc3_setup_role_switch(struct dwc3 *dwc)
+{
+       struct usb_role_switch_desc dwc3_role_switch = {NULL};
+       const char *str;
+       u32 mode;
+       int ret;
+
+       ret = device_property_read_string(dwc->dev, "role-switch-default-mode",
+                                         &str);
+       if (ret >= 0  && !strncmp(str, "host", strlen("host"))) {
+               dwc->role_switch_default_mode = USB_DR_MODE_HOST;
+               mode = DWC3_GCTL_PRTCAP_HOST;
+       } else {
+               dwc->role_switch_default_mode = USB_DR_MODE_PERIPHERAL;
+               mode = DWC3_GCTL_PRTCAP_DEVICE;
+       }
+
+       dwc3_role_switch.fwnode = dev_fwnode(dwc->dev);
+       dwc3_role_switch.set = dwc3_usb_role_switch_set;
+       dwc3_role_switch.get = dwc3_usb_role_switch_get;
+       dwc3_role_switch.driver_data = dwc;
+       dwc->role_sw = usb_role_switch_register(dwc->dev, &dwc3_role_switch);
+       if (IS_ERR(dwc->role_sw))
+               return PTR_ERR(dwc->role_sw);
+
+       dwc3_set_mode(dwc, mode);
+       return 0;
+}
+#else
+#define ROLE_SWITCH 0
+#define dwc3_setup_role_switch(x) 0
+#endif
+
 int dwc3_drd_init(struct dwc3 *dwc)
 {
        int ret, irq;
@@ -484,7 +572,12 @@ int dwc3_drd_init(struct dwc3 *dwc)
        if (IS_ERR(dwc->edev))
                return PTR_ERR(dwc->edev);
 
-       if (dwc->edev) {
+       if (ROLE_SWITCH &&
+           device_property_read_bool(dwc->dev, "usb-role-switch")) {
+               ret = dwc3_setup_role_switch(dwc);
+               if (ret < 0)
+                       return ret;
+       } else if (dwc->edev) {
                dwc->edev_nb.notifier_call = dwc3_drd_notifier;
                ret = extcon_register_notifier(dwc->edev, EXTCON_USB_HOST,
                                               &dwc->edev_nb);
@@ -531,6 +624,9 @@ void dwc3_drd_exit(struct dwc3 *dwc)
 {
        unsigned long flags;
 
+       if (dwc->role_sw)
+               usb_role_switch_unregister(dwc->role_sw);
+
        if (dwc->edev)
                extcon_unregister_notifier(dwc->edev, EXTCON_USB_HOST,
                                           &dwc->edev_nb);
index 90bb022737da8ef4e540ee1a713ed3ac3acdd8e2..48b68b6f0dc82186368028334ac1d6ec32622912 100644 (file)
@@ -162,6 +162,12 @@ static const struct dwc3_exynos_driverdata exynos5250_drvdata = {
        .suspend_clk_idx = -1,
 };
 
+static const struct dwc3_exynos_driverdata exynos5420_drvdata = {
+       .clk_names = { "usbdrd30", "usbdrd30_susp_clk"},
+       .num_clks = 2,
+       .suspend_clk_idx = 1,
+};
+
 static const struct dwc3_exynos_driverdata exynos5433_drvdata = {
        .clk_names = { "aclk", "susp_clk", "pipe_pclk", "phyclk" },
        .num_clks = 4,
@@ -178,6 +184,9 @@ static const struct of_device_id exynos_dwc3_match[] = {
        {
                .compatible = "samsung,exynos5250-dwusb3",
                .data = &exynos5250_drvdata,
+       }, {
+               .compatible = "samsung,exynos5420-dwusb3",
+               .data = &exynos5420_drvdata,
        }, {
                .compatible = "samsung,exynos5433-dwusb3",
                .data = &exynos5433_drvdata,
index 8a3ec1a951feb3173e9a6b73f81d3cc8db79a464..b81d085bc534c8e7fa660116547dc0aad01d95aa 100644 (file)
@@ -107,10 +107,37 @@ static const char *phy_names[PHY_COUNT] = {
        "usb2-phy0", "usb2-phy1", "usb3-phy0",
 };
 
+static struct clk_bulk_data meson_g12a_clocks[] = {
+       { .id = NULL },
+};
+
+static struct clk_bulk_data meson_a1_clocks[] = {
+       { .id = "usb_ctrl" },
+       { .id = "usb_bus" },
+       { .id = "xtal_usb_ctrl" },
+};
+
+struct dwc3_meson_g12a_drvdata {
+       bool otg_switch_supported;
+       struct clk_bulk_data *clks;
+       int num_clks;
+};
+
+static struct dwc3_meson_g12a_drvdata g12a_drvdata = {
+       .otg_switch_supported = true,
+       .clks = meson_g12a_clocks,
+       .num_clks = ARRAY_SIZE(meson_g12a_clocks),
+};
+
+static struct dwc3_meson_g12a_drvdata a1_drvdata = {
+       .otg_switch_supported = false,
+       .clks = meson_a1_clocks,
+       .num_clks = ARRAY_SIZE(meson_a1_clocks),
+};
+
 struct dwc3_meson_g12a {
        struct device           *dev;
        struct regmap           *regmap;
-       struct clk              *clk;
        struct reset_control    *reset;
        struct phy              *phys[PHY_COUNT];
        enum usb_dr_mode        otg_mode;
@@ -120,6 +147,7 @@ struct dwc3_meson_g12a {
        struct regulator        *vbus;
        struct usb_role_switch_desc switch_desc;
        struct usb_role_switch  *role_switch;
+       const struct dwc3_meson_g12a_drvdata *drvdata;
 };
 
 static void dwc3_meson_g12a_usb2_set_mode(struct dwc3_meson_g12a *priv,
@@ -151,7 +179,7 @@ static int dwc3_meson_g12a_usb2_init(struct dwc3_meson_g12a *priv)
                                   U2P_R0_POWER_ON_RESET,
                                   U2P_R0_POWER_ON_RESET);
 
-               if (i == USB2_OTG_PHY) {
+               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,
@@ -295,7 +323,7 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
 {
        int ret;
 
-       if (!priv->phys[USB2_OTG_PHY])
+       if (!priv->drvdata->otg_switch_supported || !priv->phys[USB2_OTG_PHY])
                return -EINVAL;
 
        if (mode == PHY_MODE_USB_HOST)
@@ -321,9 +349,10 @@ static int dwc3_meson_g12a_otg_mode_set(struct dwc3_meson_g12a *priv,
        return 0;
 }
 
-static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role)
+static int dwc3_meson_g12a_role_set(struct usb_role_switch *sw,
+                                   enum usb_role role)
 {
-       struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+       struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
        enum phy_mode mode;
 
        if (role == USB_ROLE_NONE)
@@ -338,9 +367,9 @@ static int dwc3_meson_g12a_role_set(struct device *dev, enum usb_role role)
        return dwc3_meson_g12a_otg_mode_set(priv, mode);
 }
 
-static enum usb_role dwc3_meson_g12a_role_get(struct device *dev)
+static enum usb_role dwc3_meson_g12a_role_get(struct usb_role_switch *sw)
 {
-       struct dwc3_meson_g12a *priv = dev_get_drvdata(dev);
+       struct dwc3_meson_g12a *priv = usb_role_switch_get_drvdata(sw);
 
        return priv->otg_phy_mode == PHY_MODE_USB_HOST ?
                USB_ROLE_HOST : USB_ROLE_DEVICE;
@@ -380,14 +409,61 @@ static struct device *dwc3_meson_g12_find_child(struct device *dev,
        return &pdev->dev;
 }
 
+static int dwc3_meson_g12a_otg_init(struct platform_device *pdev,
+                                   struct dwc3_meson_g12a *priv)
+{
+       enum phy_mode otg_id;
+       int ret, irq;
+       struct device *dev = &pdev->dev;
+
+       if (!priv->drvdata->otg_switch_supported)
+               return 0;
+
+       if (priv->otg_mode == USB_DR_MODE_OTG) {
+               /* Ack irq before registering */
+               regmap_update_bits(priv->regmap, USB_R5,
+                                  USB_R5_ID_DIG_IRQ, 0);
+
+               irq = platform_get_irq(pdev, 0);
+               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                                               dwc3_meson_g12a_irq_thread,
+                                               IRQF_ONESHOT, pdev->name, priv);
+               if (ret)
+                       return ret;
+       }
+
+       /* Setup OTG mode corresponding to the ID pin */
+       if (priv->otg_mode == USB_DR_MODE_OTG) {
+               otg_id = dwc3_meson_g12a_get_id(priv);
+               if (otg_id != priv->otg_phy_mode) {
+                       if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
+                               dev_warn(dev, "Failed to switch OTG mode\n");
+               }
+       }
+
+       /* Setup role switcher */
+       priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
+                                                               "snps,dwc3");
+       priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
+       priv->switch_desc.allow_userspace_control = true;
+       priv->switch_desc.set = dwc3_meson_g12a_role_set;
+       priv->switch_desc.get = dwc3_meson_g12a_role_get;
+       priv->switch_desc.driver_data = priv;
+
+       priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
+       if (IS_ERR(priv->role_switch))
+               dev_warn(dev, "Unable to register Role Switch\n");
+
+       return 0;
+}
+
 static int dwc3_meson_g12a_probe(struct platform_device *pdev)
 {
        struct dwc3_meson_g12a  *priv;
        struct device           *dev = &pdev->dev;
        struct device_node      *np = dev->of_node;
        void __iomem *base;
-       enum phy_mode otg_id;
-       int ret, i, irq;
+       int ret, i;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -409,17 +485,18 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
                priv->vbus = NULL;
        }
 
-       priv->clk = devm_clk_get(dev, NULL);
-       if (IS_ERR(priv->clk))
-               return PTR_ERR(priv->clk);
+       priv->drvdata = of_device_get_match_data(&pdev->dev);
 
-       ret = clk_prepare_enable(priv->clk);
+       ret = devm_clk_bulk_get(dev,
+                               priv->drvdata->num_clks,
+                               priv->drvdata->clks);
        if (ret)
                return ret;
 
-       devm_add_action_or_reset(dev,
-                                (void(*)(void *))clk_disable_unprepare,
-                                priv->clk);
+       ret = clk_bulk_prepare_enable(priv->drvdata->num_clks,
+                                     priv->drvdata->clks);
+       if (ret)
+               return ret;
 
        platform_set_drvdata(pdev, priv);
        priv->dev = dev;
@@ -433,41 +510,28 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
 
        ret = reset_control_reset(priv->reset);
        if (ret)
-               return ret;
+               goto err_disable_clks;
 
        ret = dwc3_meson_g12a_get_phys(priv);
        if (ret)
-               return ret;
+               goto err_disable_clks;
 
        if (priv->vbus) {
                ret = regulator_enable(priv->vbus);
                if (ret)
-                       return ret;
+                       goto err_disable_clks;
        }
 
        /* Get dr_mode */
        priv->otg_mode = usb_get_dr_mode(dev);
 
-       if (priv->otg_mode == USB_DR_MODE_OTG) {
-               /* Ack irq before registering */
-               regmap_update_bits(priv->regmap, USB_R5,
-                                  USB_R5_ID_DIG_IRQ, 0);
-
-               irq = platform_get_irq(pdev, 0);
-               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
-                                               dwc3_meson_g12a_irq_thread,
-                                               IRQF_ONESHOT, pdev->name, priv);
-               if (ret)
-                       return ret;
-       }
-
        dwc3_meson_g12a_usb_init(priv);
 
        /* Init PHYs */
        for (i = 0 ; i < PHY_COUNT ; ++i) {
                ret = phy_init(priv->phys[i]);
                if (ret)
-                       return ret;
+                       goto err_disable_clks;
        }
 
        /* Set PHY Power */
@@ -478,31 +542,12 @@ static int dwc3_meson_g12a_probe(struct platform_device *pdev)
        }
 
        ret = of_platform_populate(np, NULL, NULL, dev);
-       if (ret) {
-               clk_disable_unprepare(priv->clk);
+       if (ret)
                goto err_phys_power;
-       }
 
-       /* Setup OTG mode corresponding to the ID pin */
-       if (priv->otg_mode == USB_DR_MODE_OTG) {
-               otg_id = dwc3_meson_g12a_get_id(priv);
-               if (otg_id != priv->otg_phy_mode) {
-                       if (dwc3_meson_g12a_otg_mode_set(priv, otg_id))
-                               dev_warn(dev, "Failed to switch OTG mode\n");
-               }
-       }
-
-       /* Setup role switcher */
-       priv->switch_desc.usb2_port = dwc3_meson_g12_find_child(dev,
-                                                               "snps,dwc3");
-       priv->switch_desc.udc = dwc3_meson_g12_find_child(dev, "snps,dwc2");
-       priv->switch_desc.allow_userspace_control = true;
-       priv->switch_desc.set = dwc3_meson_g12a_role_set;
-       priv->switch_desc.get = dwc3_meson_g12a_role_get;
-
-       priv->role_switch = usb_role_switch_register(dev, &priv->switch_desc);
-       if (IS_ERR(priv->role_switch))
-               dev_warn(dev, "Unable to register Role Switch\n");
+       ret = dwc3_meson_g12a_otg_init(pdev, priv);
+       if (ret)
+               goto err_phys_power;
 
        pm_runtime_set_active(dev);
        pm_runtime_enable(dev);
@@ -518,6 +563,10 @@ err_phys_exit:
        for (i = 0 ; i < PHY_COUNT ; ++i)
                phy_exit(priv->phys[i]);
 
+err_disable_clks:
+       clk_bulk_disable_unprepare(priv->drvdata->num_clks,
+                                  priv->drvdata->clks);
+
        return ret;
 }
 
@@ -527,7 +576,8 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev)
        struct device *dev = &pdev->dev;
        int i;
 
-       usb_role_switch_unregister(priv->role_switch);
+       if (priv->drvdata->otg_switch_supported)
+               usb_role_switch_unregister(priv->role_switch);
 
        of_platform_depopulate(dev);
 
@@ -540,6 +590,9 @@ static int dwc3_meson_g12a_remove(struct platform_device *pdev)
        pm_runtime_put_noidle(dev);
        pm_runtime_set_suspended(dev);
 
+       clk_bulk_disable_unprepare(priv->drvdata->num_clks,
+                                  priv->drvdata->clks);
+
        return 0;
 }
 
@@ -547,7 +600,8 @@ static int __maybe_unused dwc3_meson_g12a_runtime_suspend(struct device *dev)
 {
        struct dwc3_meson_g12a  *priv = dev_get_drvdata(dev);
 
-       clk_disable(priv->clk);
+       clk_bulk_disable_unprepare(priv->drvdata->num_clks,
+                                  priv->drvdata->clks);
 
        return 0;
 }
@@ -556,7 +610,8 @@ static int __maybe_unused dwc3_meson_g12a_runtime_resume(struct device *dev)
 {
        struct dwc3_meson_g12a  *priv = dev_get_drvdata(dev);
 
-       return clk_enable(priv->clk);
+       return clk_bulk_prepare_enable(priv->drvdata->num_clks,
+                                      priv->drvdata->clks);
 }
 
 static int __maybe_unused dwc3_meson_g12a_suspend(struct device *dev)
@@ -619,7 +674,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-g12a-usb-ctrl" },
+       {
+               .compatible = "amlogic,meson-g12a-usb-ctrl",
+               .data = &g12a_drvdata,
+       },
+       {
+               .compatible = "amlogic,meson-a1-usb-ctrl",
+               .data = &a1_drvdata,
+       },
        { /* Sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, dwc3_meson_g12a_match);
index 261af9e38dddf5cb796edeaa83614785ca66f290..1dfd024cd06b11ff2a0409acff192890d7754bef 100644 (file)
@@ -9,7 +9,7 @@
 #include <linux/of.h>
 #include <linux/clk.h>
 #include <linux/irq.h>
-#include <linux/clk-provider.h>
+#include <linux/of_clk.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/extcon.h>
index 1e00bf2d65a2045e139317d297831e39815ba005..4d3c79d90a6e4a420cc079dc62e2a25a49f1c705 100644 (file)
@@ -1521,7 +1521,7 @@ static void dwc3_gadget_ep_skip_trbs(struct dwc3_ep *dep, struct dwc3_request *r
        for (i = 0; i < req->num_trbs; i++) {
                struct dwc3_trb *trb;
 
-               trb = req->trb + i;
+               trb = &dep->trb_pool[dep->trb_dequeue];
                trb->ctrl &= ~DWC3_TRB_CTRL_HWO;
                dwc3_ep_inc_deq(dep);
        }
@@ -2570,10 +2570,8 @@ static void dwc3_gadget_endpoint_transfer_in_progress(struct dwc3_ep *dep,
 
        dwc3_gadget_ep_cleanup_completed_requests(dep, event, status);
 
-       if (stop) {
+       if (stop)
                dwc3_stop_active_transfer(dep, true, true);
-               dep->flags = DWC3_EP_ENABLED;
-       }
 
        /*
         * WORKAROUND: This is the 2nd half of U1/U2 -> U0 workaround.
index fa252870c926f1834d8ba6ed29724d1deb32cf7b..86dbd012b984d2bd5f00e65e54cc46c546a2c940 100644 (file)
@@ -1,5 +1,5 @@
 // SPDX-License-Identifier: GPL-2.0
-/**
+/*
  * host.c - DesignWare USB3 DRD Controller Host Glue
  *
  * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
@@ -7,6 +7,7 @@
  * Authors: Felipe Balbi <balbi@ti.com>,
  */
 
+#include <linux/acpi.h>
 #include <linux/platform_device.h>
 
 #include "core.h"
@@ -75,6 +76,7 @@ int dwc3_host_init(struct dwc3 *dwc)
        }
 
        xhci->dev.parent        = dwc->dev;
+       ACPI_COMPANION_SET(&xhci->dev, ACPI_COMPANION(dwc->dev));
 
        dwc->xhci = xhci;
 
index 9edff17111f76be3a94bb5fb425efee3e14bbef9..3054b89512ff89f84077df53c02c5ba574ab00aa 100644 (file)
@@ -227,6 +227,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
                __field(u32, size)
                __field(u32, ctrl)
                __field(u32, type)
+               __field(u32, enqueue)
+               __field(u32, dequeue)
        ),
        TP_fast_assign(
                __assign_str(name, dep->name);
@@ -236,9 +238,12 @@ DECLARE_EVENT_CLASS(dwc3_log_trb,
                __entry->size = trb->size;
                __entry->ctrl = trb->ctrl;
                __entry->type = usb_endpoint_type(dep->endpoint.desc);
+               __entry->enqueue = dep->trb_enqueue;
+               __entry->dequeue = dep->trb_dequeue;
        ),
-       TP_printk("%s: trb %p buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
-               __get_str(name), __entry->trb, __entry->bph, __entry->bpl,
+       TP_printk("%s: trb %p (E%d:D%d) buf %08x%08x size %s%d ctrl %08x (%c%c%c%c:%c%c:%s)",
+               __get_str(name), __entry->trb, __entry->enqueue,
+               __entry->dequeue, __entry->bph, __entry->bpl,
                ({char *s;
                int pcm = ((__entry->size >> 24) & 3) + 1;
                switch (__entry->type) {
index 223f72d4d9eddf2734a4148ce21cd52a358b4102..cb4950cf1cdcb69307c60ee948af30d372e0f29b 100644 (file)
@@ -861,6 +861,11 @@ static int set_config(struct usb_composite_dev *cdev,
        else
                power = min(power, 900U);
 done:
+       if (power <= USB_SELF_POWER_VBUS_MAX_DRAW)
+               usb_gadget_set_selfpowered(gadget);
+       else
+               usb_gadget_clear_selfpowered(gadget);
+
        usb_gadget_vbus_draw(gadget, power);
        if (result >= 0 && cdev->delayed_status)
                result = USB_GADGET_DELAYED_STATUS;
@@ -2279,6 +2284,7 @@ void composite_suspend(struct usb_gadget *gadget)
 
        cdev->suspended = 1;
 
+       usb_gadget_set_selfpowered(gadget);
        usb_gadget_vbus_draw(gadget, 2);
 }
 
@@ -2307,6 +2313,9 @@ void composite_resume(struct usb_gadget *gadget)
                else
                        maxpower = min(maxpower, 900U);
 
+               if (maxpower > USB_SELF_POWER_VBUS_MAX_DRAW)
+                       usb_gadget_clear_selfpowered(gadget);
+
                usb_gadget_vbus_draw(gadget, maxpower);
        }
 
index 571917677d358f4f62dbf34ee7a17a9dce0755f4..767f30b86645ba6f2b9c9838dea44a41591d96f8 100644 (file)
@@ -1120,6 +1120,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
 
                ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
                if (unlikely(ret)) {
+                       io_data->req = NULL;
                        usb_ep_free_request(ep->ep, req);
                        goto error_lock;
                }
index 8b72b192c747688dcc974c0e58324c53c65fe3da..d7f6cc51b7ece7abc93d63fa5931887c052c4c43 100644 (file)
@@ -48,7 +48,7 @@ struct f_phonet {
        struct usb_ep                   *in_ep, *out_ep;
 
        struct usb_request              *in_req;
-       struct usb_request              *out_reqv[0];
+       struct usb_request              *out_reqv[];
 };
 
 static int phonet_rxq_size = 17;
index 6677ae932de05822a26f103b6c9feba3ccebafc1..349deae7cabd89c9a1d6d8ea120c413038f89740 100644 (file)
@@ -752,8 +752,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
        audio->out_ep = ep;
        audio->out_ep->desc = &as_out_ep_desc;
 
-       status = -ENOMEM;
-
        /* copy descriptors, and track endpoint copies */
        status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, NULL,
                                        NULL);
index 6e7e1a9202e6cf2e69fe1c003738f0f4ea32d547..f02c38b32a2b1f092b665844e87e7dc0882e7ae3 100644 (file)
 # With help from a special transceiver and a "Mini-AB" jack, systems with
 # both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
 #
+# A Linux "Gadget Driver" talks to the USB Peripheral Controller
+# driver through the abstract "gadget" API.  Some other operating
+# systems call these "client" drivers, of which "class drivers"
+# are a subset (implementing a USB device class specification).
+# A gadget driver implements one or more USB functions using
+# the peripheral hardware.
+#
+# Gadget drivers are hardware-neutral, or "platform independent",
+# except that they sometimes must understand quirks or limitations
+# of the particular controllers they work with.  For example, when
+# a controller doesn't support alternate configurations or provide
+# enough of the right types of endpoints, the gadget driver might
+# not be able work with that controller, or might need to implement
+# a less common variant of a device class protocol.
+#
+# The available choices each represent a single precomposed USB
+# gadget configuration. In the device model, each option contains
+# both the device instantiation as a child for a USB gadget
+# controller, and the relevant drivers for each function declared
+# by the device.
 
-choice
-       tristate "USB Gadget precomposed configurations"
-       default USB_ETH
-       optional
-       help
-         A Linux "Gadget Driver" talks to the USB Peripheral Controller
-         driver through the abstract "gadget" API.  Some other operating
-         systems call these "client" drivers, of which "class drivers"
-         are a subset (implementing a USB device class specification).
-         A gadget driver implements one or more USB functions using
-         the peripheral hardware.
-
-         Gadget drivers are hardware-neutral, or "platform independent",
-         except that they sometimes must understand quirks or limitations
-         of the particular controllers they work with.  For example, when
-         a controller doesn't support alternate configurations or provide
-         enough of the right types of endpoints, the gadget driver might
-         not be able work with that controller, or might need to implement
-         a less common variant of a device class protocol.
-
-         The available choices each represent a single precomposed USB
-         gadget configuration. In the device model, each option contains
-         both the device instantiation as a child for a USB gadget
-         controller, and the relevant drivers for each function declared
-         by the device.
+menu "USB Gadget precomposed configurations"
 
 config USB_ZERO
        tristate "Gadget Zero (DEVELOPMENT)"
@@ -516,4 +512,15 @@ config USB_G_WEBCAM
          Say "y" to link the driver statically, or "m" to build a
          dynamically linked module called "g_webcam".
 
-endchoice
+config USB_RAW_GADGET
+       tristate "USB Raw Gadget"
+       help
+         USB Raw Gadget is a kernel module that provides a userspace interface
+         for the USB Gadget subsystem. Essentially it allows to emulate USB
+         devices from userspace. See Documentation/usb/raw-gadget.rst for
+         details.
+
+         Say "y" to link the driver statically, or "m" to build a
+         dynamically linked module called "raw_gadget".
+
+endmenu
index abd0c3e66a05d8de76be3039e0a41047247d89c4..4d864bf82799d8c4a01092259dfb06c013ac3338 100644 (file)
@@ -43,3 +43,4 @@ obj-$(CONFIG_USB_G_WEBCAM)    += g_webcam.o
 obj-$(CONFIG_USB_G_NCM)                += g_ncm.o
 obj-$(CONFIG_USB_G_ACM_MS)     += g_acm_ms.o
 obj-$(CONFIG_USB_GADGET_TARGET)        += tcm_usb_gadget.o
+obj-$(CONFIG_USB_RAW_GADGET)   += raw_gadget.o
index 9eea2d18f2bf2baa18a9a51bb03aa58dfb626d6b..265c392810d79c040f445fba3e2a0d03cd1cd13e 100644 (file)
@@ -174,7 +174,7 @@ put:
 }
 
 static struct usb_composite_driver midi_driver = {
-       .name           = (char *) longname,
+       .name           = longname,
        .dev            = &device_desc,
        .strings        = dev_strings,
        .max_speed      = USB_SPEED_HIGH,
index b47938dff1a22e11092413b68fd57ae35d2cc2a7..e3dfc2180555b43bb426546afe2768e96369aede 100644 (file)
@@ -1736,7 +1736,7 @@ static struct usb_gadget_driver gadgetfs_driver = {
        .suspend        = gadgetfs_suspend,
 
        .driver = {
-               .name           = (char *) shortname,
+               .name           = shortname,
        },
 };
 
diff --git a/drivers/usb/gadget/legacy/raw_gadget.c b/drivers/usb/gadget/legacy/raw_gadget.c
new file mode 100644 (file)
index 0000000..7640634
--- /dev/null
@@ -0,0 +1,1078 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USB Raw Gadget driver.
+ * See Documentation/usb/raw-gadget.rst for more details.
+ *
+ * Andrey Konovalov <andreyknvl@gmail.com>
+ */
+
+#include <linux/compiler.h>
+#include <linux/debugfs.h>
+#include <linux/delay.h>
+#include <linux/kref.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/semaphore.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/uaccess.h>
+#include <linux/wait.h>
+
+#include <linux/usb.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/ch11.h>
+#include <linux/usb/gadget.h>
+
+#include <uapi/linux/usb/raw_gadget.h>
+
+#define        DRIVER_DESC "USB Raw Gadget"
+#define DRIVER_NAME "raw-gadget"
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Andrey Konovalov");
+MODULE_LICENSE("GPL");
+
+/*----------------------------------------------------------------------*/
+
+#define RAW_EVENT_QUEUE_SIZE   16
+
+struct raw_event_queue {
+       /* See the comment in raw_event_queue_fetch() for locking details. */
+       spinlock_t              lock;
+       struct semaphore        sema;
+       struct usb_raw_event    *events[RAW_EVENT_QUEUE_SIZE];
+       int                     size;
+};
+
+static void raw_event_queue_init(struct raw_event_queue *queue)
+{
+       spin_lock_init(&queue->lock);
+       sema_init(&queue->sema, 0);
+       queue->size = 0;
+}
+
+static int raw_event_queue_add(struct raw_event_queue *queue,
+       enum usb_raw_event_type type, size_t length, const void *data)
+{
+       unsigned long flags;
+       struct usb_raw_event *event;
+
+       spin_lock_irqsave(&queue->lock, flags);
+       if (WARN_ON(queue->size >= RAW_EVENT_QUEUE_SIZE)) {
+               spin_unlock_irqrestore(&queue->lock, flags);
+               return -ENOMEM;
+       }
+       event = kmalloc(sizeof(*event) + length, GFP_ATOMIC);
+       if (!event) {
+               spin_unlock_irqrestore(&queue->lock, flags);
+               return -ENOMEM;
+       }
+       event->type = type;
+       event->length = length;
+       if (event->length)
+               memcpy(&event->data[0], data, length);
+       queue->events[queue->size] = event;
+       queue->size++;
+       up(&queue->sema);
+       spin_unlock_irqrestore(&queue->lock, flags);
+       return 0;
+}
+
+static struct usb_raw_event *raw_event_queue_fetch(
+                               struct raw_event_queue *queue)
+{
+       unsigned long flags;
+       struct usb_raw_event *event;
+
+       /*
+        * This function can be called concurrently. We first check that
+        * there's at least one event queued by decrementing the semaphore,
+        * and then take the lock to protect queue struct fields.
+        */
+       if (down_interruptible(&queue->sema))
+               return NULL;
+       spin_lock_irqsave(&queue->lock, flags);
+       if (WARN_ON(!queue->size))
+               return NULL;
+       event = queue->events[0];
+       queue->size--;
+       memmove(&queue->events[0], &queue->events[1],
+                       queue->size * sizeof(queue->events[0]));
+       spin_unlock_irqrestore(&queue->lock, flags);
+       return event;
+}
+
+static void raw_event_queue_destroy(struct raw_event_queue *queue)
+{
+       int i;
+
+       for (i = 0; i < queue->size; i++)
+               kfree(queue->events[i]);
+       queue->size = 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+struct raw_dev;
+
+#define USB_RAW_MAX_ENDPOINTS 32
+
+enum ep_state {
+       STATE_EP_DISABLED,
+       STATE_EP_ENABLED,
+};
+
+struct raw_ep {
+       struct raw_dev          *dev;
+       enum ep_state           state;
+       struct usb_ep           *ep;
+       struct usb_request      *req;
+       bool                    urb_queued;
+       bool                    disabling;
+       ssize_t                 status;
+};
+
+enum dev_state {
+       STATE_DEV_INVALID = 0,
+       STATE_DEV_OPENED,
+       STATE_DEV_INITIALIZED,
+       STATE_DEV_RUNNING,
+       STATE_DEV_CLOSED,
+       STATE_DEV_FAILED
+};
+
+struct raw_dev {
+       struct kref                     count;
+       spinlock_t                      lock;
+
+       const char                      *udc_name;
+       struct usb_gadget_driver        driver;
+
+       /* Reference to misc device: */
+       struct device                   *dev;
+
+       /* Protected by lock: */
+       enum dev_state                  state;
+       bool                            gadget_registered;
+       struct usb_gadget               *gadget;
+       struct usb_request              *req;
+       bool                            ep0_in_pending;
+       bool                            ep0_out_pending;
+       bool                            ep0_urb_queued;
+       ssize_t                         ep0_status;
+       struct raw_ep                   eps[USB_RAW_MAX_ENDPOINTS];
+
+       struct completion               ep0_done;
+       struct raw_event_queue          queue;
+};
+
+static struct raw_dev *dev_new(void)
+{
+       struct raw_dev *dev;
+
+       dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+       if (!dev)
+               return NULL;
+       /* Matches kref_put() in raw_release(). */
+       kref_init(&dev->count);
+       spin_lock_init(&dev->lock);
+       init_completion(&dev->ep0_done);
+       raw_event_queue_init(&dev->queue);
+       return dev;
+}
+
+static void dev_free(struct kref *kref)
+{
+       struct raw_dev *dev = container_of(kref, struct raw_dev, count);
+       int i;
+
+       kfree(dev->udc_name);
+       kfree(dev->driver.udc_name);
+       if (dev->req) {
+               if (dev->ep0_urb_queued)
+                       usb_ep_dequeue(dev->gadget->ep0, dev->req);
+               usb_ep_free_request(dev->gadget->ep0, dev->req);
+       }
+       raw_event_queue_destroy(&dev->queue);
+       for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
+               if (dev->eps[i].state != STATE_EP_ENABLED)
+                       continue;
+               usb_ep_disable(dev->eps[i].ep);
+               usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+               kfree(dev->eps[i].ep->desc);
+               dev->eps[i].state = STATE_EP_DISABLED;
+       }
+       kfree(dev);
+}
+
+/*----------------------------------------------------------------------*/
+
+static int raw_queue_event(struct raw_dev *dev,
+       enum usb_raw_event_type type, size_t length, const void *data)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       ret = raw_event_queue_add(&dev->queue, type, length, data);
+       if (ret < 0) {
+               spin_lock_irqsave(&dev->lock, flags);
+               dev->state = STATE_DEV_FAILED;
+               spin_unlock_irqrestore(&dev->lock, flags);
+       }
+       return ret;
+}
+
+static void gadget_ep0_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct raw_dev *dev = req->context;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (req->status)
+               dev->ep0_status = req->status;
+       else
+               dev->ep0_status = req->actual;
+       if (dev->ep0_in_pending)
+               dev->ep0_in_pending = false;
+       else
+               dev->ep0_out_pending = false;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       complete(&dev->ep0_done);
+}
+
+static int gadget_bind(struct usb_gadget *gadget,
+                       struct usb_gadget_driver *driver)
+{
+       int ret = 0;
+       struct raw_dev *dev = container_of(driver, struct raw_dev, driver);
+       struct usb_request *req;
+       unsigned long flags;
+
+       if (strcmp(gadget->name, dev->udc_name) != 0)
+               return -ENODEV;
+
+       set_gadget_data(gadget, dev);
+       req = usb_ep_alloc_request(gadget->ep0, GFP_KERNEL);
+       if (!req) {
+               dev_err(&gadget->dev, "usb_ep_alloc_request failed\n");
+               set_gadget_data(gadget, NULL);
+               return -ENOMEM;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->req = req;
+       dev->req->context = dev;
+       dev->req->complete = gadget_ep0_complete;
+       dev->gadget = gadget;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       /* Matches kref_put() in gadget_unbind(). */
+       kref_get(&dev->count);
+
+       ret = raw_queue_event(dev, USB_RAW_EVENT_CONNECT, 0, NULL);
+       if (ret < 0)
+               dev_err(&gadget->dev, "failed to queue event\n");
+
+       return ret;
+}
+
+static void gadget_unbind(struct usb_gadget *gadget)
+{
+       struct raw_dev *dev = get_gadget_data(gadget);
+
+       set_gadget_data(gadget, NULL);
+       /* Matches kref_get() in gadget_bind(). */
+       kref_put(&dev->count, dev_free);
+}
+
+static int gadget_setup(struct usb_gadget *gadget,
+                       const struct usb_ctrlrequest *ctrl)
+{
+       int ret = 0;
+       struct raw_dev *dev = get_gadget_data(gadget);
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_err(&gadget->dev, "ignoring, device is not running\n");
+               ret = -ENODEV;
+               goto out_unlock;
+       }
+       if (dev->ep0_in_pending || dev->ep0_out_pending) {
+               dev_dbg(&gadget->dev, "stalling, request already pending\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if ((ctrl->bRequestType & USB_DIR_IN) && ctrl->wLength)
+               dev->ep0_in_pending = true;
+       else
+               dev->ep0_out_pending = true;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       ret = raw_queue_event(dev, USB_RAW_EVENT_CONTROL, sizeof(*ctrl), ctrl);
+       if (ret < 0)
+               dev_err(&gadget->dev, "failed to queue event\n");
+       goto out;
+
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+out:
+       return ret;
+}
+
+/* These are currently unused but present in case UDC driver requires them. */
+static void gadget_disconnect(struct usb_gadget *gadget) { }
+static void gadget_suspend(struct usb_gadget *gadget) { }
+static void gadget_resume(struct usb_gadget *gadget) { }
+static void gadget_reset(struct usb_gadget *gadget) { }
+
+/*----------------------------------------------------------------------*/
+
+static struct miscdevice raw_misc_device;
+
+static int raw_open(struct inode *inode, struct file *fd)
+{
+       struct raw_dev *dev;
+
+       /* Nonblocking I/O is not supported yet. */
+       if (fd->f_flags & O_NONBLOCK)
+               return -EINVAL;
+
+       dev = dev_new();
+       if (!dev)
+               return -ENOMEM;
+       fd->private_data = dev;
+       dev->state = STATE_DEV_OPENED;
+       dev->dev = raw_misc_device.this_device;
+       return 0;
+}
+
+static int raw_release(struct inode *inode, struct file *fd)
+{
+       int ret = 0;
+       struct raw_dev *dev = fd->private_data;
+       unsigned long flags;
+       bool unregister = false;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       dev->state = STATE_DEV_CLOSED;
+       if (!dev->gadget) {
+               spin_unlock_irqrestore(&dev->lock, flags);
+               goto out_put;
+       }
+       if (dev->gadget_registered)
+               unregister = true;
+       dev->gadget_registered = false;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       if (unregister) {
+               ret = usb_gadget_unregister_driver(&dev->driver);
+               if (ret != 0)
+                       dev_err(dev->dev,
+                               "usb_gadget_unregister_driver() failed with %d\n",
+                               ret);
+               /* Matches kref_get() in raw_ioctl_run(). */
+               kref_put(&dev->count, dev_free);
+       }
+
+out_put:
+       /* Matches dev_new() in raw_open(). */
+       kref_put(&dev->count, dev_free);
+       return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       struct usb_raw_init arg;
+       char *udc_driver_name;
+       char *udc_device_name;
+       unsigned long flags;
+
+       ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
+       if (ret)
+               return ret;
+
+       switch (arg.speed) {
+       case USB_SPEED_UNKNOWN:
+               arg.speed = USB_SPEED_HIGH;
+               break;
+       case USB_SPEED_LOW:
+       case USB_SPEED_FULL:
+       case USB_SPEED_HIGH:
+       case USB_SPEED_SUPER:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       udc_driver_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
+       if (!udc_driver_name)
+               return -ENOMEM;
+       ret = strscpy(udc_driver_name, &arg.driver_name[0],
+                               UDC_NAME_LENGTH_MAX);
+       if (ret < 0) {
+               kfree(udc_driver_name);
+               return ret;
+       }
+       ret = 0;
+
+       udc_device_name = kmalloc(UDC_NAME_LENGTH_MAX, GFP_KERNEL);
+       if (!udc_device_name) {
+               kfree(udc_driver_name);
+               return -ENOMEM;
+       }
+       ret = strscpy(udc_device_name, &arg.device_name[0],
+                               UDC_NAME_LENGTH_MAX);
+       if (ret < 0) {
+               kfree(udc_driver_name);
+               kfree(udc_device_name);
+               return ret;
+       }
+       ret = 0;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_OPENED) {
+               dev_dbg(dev->dev, "fail, device is not opened\n");
+               kfree(udc_driver_name);
+               kfree(udc_device_name);
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       dev->udc_name = udc_driver_name;
+
+       dev->driver.function = DRIVER_DESC;
+       dev->driver.max_speed = arg.speed;
+       dev->driver.setup = gadget_setup;
+       dev->driver.disconnect = gadget_disconnect;
+       dev->driver.bind = gadget_bind;
+       dev->driver.unbind = gadget_unbind;
+       dev->driver.suspend = gadget_suspend;
+       dev->driver.resume = gadget_resume;
+       dev->driver.reset = gadget_reset;
+       dev->driver.driver.name = DRIVER_NAME;
+       dev->driver.udc_name = udc_device_name;
+       dev->driver.match_existing_only = 1;
+
+       dev->state = STATE_DEV_INITIALIZED;
+
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int raw_ioctl_run(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (value)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_INITIALIZED) {
+               dev_dbg(dev->dev, "fail, device is not initialized\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       ret = usb_gadget_probe_driver(&dev->driver);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (ret) {
+               dev_err(dev->dev,
+                       "fail, usb_gadget_probe_driver returned %d\n", ret);
+               dev->state = STATE_DEV_FAILED;
+               goto out_unlock;
+       }
+       dev->gadget_registered = true;
+       dev->state = STATE_DEV_RUNNING;
+       /* Matches kref_put() in raw_release(). */
+       kref_get(&dev->count);
+
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       struct usb_raw_event arg;
+       unsigned long flags;
+       struct usb_raw_event *event;
+       uint32_t length;
+
+       ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
+       if (ret)
+               return ret;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_dbg(dev->dev, "fail, device is not running\n");
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return -EINVAL;
+       }
+       if (!dev->gadget) {
+               dev_dbg(dev->dev, "fail, gadget is not bound\n");
+               spin_unlock_irqrestore(&dev->lock, flags);
+               return -EBUSY;
+       }
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       event = raw_event_queue_fetch(&dev->queue);
+       if (!event) {
+               dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
+               return -EINTR;
+       }
+       length = min(arg.length, event->length);
+       ret = copy_to_user((void __user *)value, event,
+                               sizeof(*event) + length);
+       return ret;
+}
+
+static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
+                               bool get_from_user)
+{
+       int ret;
+       void *data;
+
+       ret = copy_from_user(io, ptr, sizeof(*io));
+       if (ret)
+               return ERR_PTR(ret);
+       if (io->ep >= USB_RAW_MAX_ENDPOINTS)
+               return ERR_PTR(-EINVAL);
+       if (!usb_raw_io_flags_valid(io->flags))
+               return ERR_PTR(-EINVAL);
+       if (io->length > PAGE_SIZE)
+               return ERR_PTR(-EINVAL);
+       if (get_from_user)
+               data = memdup_user(ptr + sizeof(*io), io->length);
+       else {
+               data = kmalloc(io->length, GFP_KERNEL);
+               if (!data)
+                       data = ERR_PTR(-ENOMEM);
+       }
+       return data;
+}
+
+static int raw_process_ep0_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
+                               void *data, bool in)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_dbg(dev->dev, "fail, device is not running\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (!dev->gadget) {
+               dev_dbg(dev->dev, "fail, gadget is not bound\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if (dev->ep0_urb_queued) {
+               dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if ((in && !dev->ep0_in_pending) ||
+                       (!in && !dev->ep0_out_pending)) {
+               dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if (WARN_ON(in && dev->ep0_out_pending)) {
+               ret = -ENODEV;
+               dev->state = STATE_DEV_FAILED;
+               goto out_done;
+       }
+       if (WARN_ON(!in && dev->ep0_in_pending)) {
+               ret = -ENODEV;
+               dev->state = STATE_DEV_FAILED;
+               goto out_done;
+       }
+
+       dev->req->buf = data;
+       dev->req->length = io->length;
+       dev->req->zero = usb_raw_io_flags_zero(io->flags);
+       dev->ep0_urb_queued = true;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       ret = usb_ep_queue(dev->gadget->ep0, dev->req, GFP_KERNEL);
+       if (ret) {
+               dev_err(&dev->gadget->dev,
+                               "fail, usb_ep_queue returned %d\n", ret);
+               spin_lock_irqsave(&dev->lock, flags);
+               dev->state = STATE_DEV_FAILED;
+               goto out_done;
+       }
+
+       ret = wait_for_completion_interruptible(&dev->ep0_done);
+       if (ret) {
+               dev_dbg(&dev->gadget->dev, "wait interrupted\n");
+               usb_ep_dequeue(dev->gadget->ep0, dev->req);
+               wait_for_completion(&dev->ep0_done);
+               spin_lock_irqsave(&dev->lock, flags);
+               goto out_done;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+       ret = dev->ep0_status;
+
+out_done:
+       dev->ep0_urb_queued = false;
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int raw_ioctl_ep0_write(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       void *data;
+       struct usb_raw_ep_io io;
+
+       data = raw_alloc_io_data(&io, (void __user *)value, true);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       ret = raw_process_ep0_io(dev, &io, data, true);
+       kfree(data);
+       return ret;
+}
+
+static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       void *data;
+       struct usb_raw_ep_io io;
+       unsigned int length;
+
+       data = raw_alloc_io_data(&io, (void __user *)value, false);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       ret = raw_process_ep0_io(dev, &io, data, false);
+       if (ret < 0) {
+               kfree(data);
+               return ret;
+       }
+       length = min(io.length, (unsigned int)ret);
+       ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
+       kfree(data);
+       return ret;
+}
+
+static bool check_ep_caps(struct usb_ep *ep,
+                               struct usb_endpoint_descriptor *desc)
+{
+       switch (usb_endpoint_type(desc)) {
+       case USB_ENDPOINT_XFER_ISOC:
+               if (!ep->caps.type_iso)
+                       return false;
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               if (!ep->caps.type_bulk)
+                       return false;
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               if (!ep->caps.type_int)
+                       return false;
+               break;
+       default:
+               return false;
+       }
+
+       if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in)
+               return false;
+       if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out)
+               return false;
+
+       return true;
+}
+
+static int raw_ioctl_ep_enable(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0, i;
+       unsigned long flags;
+       struct usb_endpoint_descriptor *desc;
+       struct usb_ep *ep = NULL;
+
+       desc = memdup_user((void __user *)value, sizeof(*desc));
+       if (IS_ERR(desc))
+               return PTR_ERR(desc);
+
+       /*
+        * Endpoints with a maxpacket length of 0 can cause crashes in UDC
+        * drivers.
+        */
+       if (usb_endpoint_maxp(desc) == 0) {
+               dev_dbg(dev->dev, "fail, bad endpoint maxpacket\n");
+               kfree(desc);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_dbg(dev->dev, "fail, device is not running\n");
+               ret = -EINVAL;
+               goto out_free;
+       }
+       if (!dev->gadget) {
+               dev_dbg(dev->dev, "fail, gadget is not bound\n");
+               ret = -EBUSY;
+               goto out_free;
+       }
+
+       for (i = 0; i < USB_RAW_MAX_ENDPOINTS; i++) {
+               if (dev->eps[i].state == STATE_EP_ENABLED)
+                       continue;
+               break;
+       }
+       if (i == USB_RAW_MAX_ENDPOINTS) {
+               dev_dbg(&dev->gadget->dev,
+                               "fail, no device endpoints available\n");
+               ret = -EBUSY;
+               goto out_free;
+       }
+
+       gadget_for_each_ep(ep, dev->gadget) {
+               if (ep->enabled)
+                       continue;
+               if (!check_ep_caps(ep, desc))
+                       continue;
+               ep->desc = desc;
+               ret = usb_ep_enable(ep);
+               if (ret < 0) {
+                       dev_err(&dev->gadget->dev,
+                               "fail, usb_ep_enable returned %d\n", ret);
+                       goto out_free;
+               }
+               dev->eps[i].req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+               if (!dev->eps[i].req) {
+                       dev_err(&dev->gadget->dev,
+                               "fail, usb_ep_alloc_request failed\n");
+                       usb_ep_disable(ep);
+                       ret = -ENOMEM;
+                       goto out_free;
+               }
+               dev->eps[i].ep = ep;
+               dev->eps[i].state = STATE_EP_ENABLED;
+               ep->driver_data = &dev->eps[i];
+               ret = i;
+               goto out_unlock;
+       }
+
+       dev_dbg(&dev->gadget->dev, "fail, no gadget endpoints available\n");
+       ret = -EBUSY;
+
+out_free:
+       kfree(desc);
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int raw_ioctl_ep_disable(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0, i = value;
+       unsigned long flags;
+       const void *desc;
+
+       if (i < 0 || i >= USB_RAW_MAX_ENDPOINTS)
+               return -EINVAL;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_dbg(dev->dev, "fail, device is not running\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (!dev->gadget) {
+               dev_dbg(dev->dev, "fail, gadget is not bound\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if (dev->eps[i].state != STATE_EP_ENABLED) {
+               dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (dev->eps[i].disabling) {
+               dev_dbg(&dev->gadget->dev,
+                               "fail, disable already in progress\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (dev->eps[i].urb_queued) {
+               dev_dbg(&dev->gadget->dev,
+                               "fail, waiting for urb completion\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       dev->eps[i].disabling = true;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       usb_ep_disable(dev->eps[i].ep);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       usb_ep_free_request(dev->eps[i].ep, dev->eps[i].req);
+       desc = dev->eps[i].ep->desc;
+       dev->eps[i].ep = NULL;
+       dev->eps[i].state = STATE_EP_DISABLED;
+       kfree(desc);
+       dev->eps[i].disabling = false;
+
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static void gadget_ep_complete(struct usb_ep *ep, struct usb_request *req)
+{
+       struct raw_ep *r_ep = (struct raw_ep *)ep->driver_data;
+       struct raw_dev *dev = r_ep->dev;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (req->status)
+               r_ep->status = req->status;
+       else
+               r_ep->status = req->actual;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       complete((struct completion *)req->context);
+}
+
+static int raw_process_ep_io(struct raw_dev *dev, struct usb_raw_ep_io *io,
+                               void *data, bool in)
+{
+       int ret = 0;
+       unsigned long flags;
+       struct raw_ep *ep = &dev->eps[io->ep];
+       DECLARE_COMPLETION_ONSTACK(done);
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_dbg(dev->dev, "fail, device is not running\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (!dev->gadget) {
+               dev_dbg(dev->dev, "fail, gadget is not bound\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if (ep->state != STATE_EP_ENABLED) {
+               dev_dbg(&dev->gadget->dev, "fail, endpoint is not enabled\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if (ep->disabling) {
+               dev_dbg(&dev->gadget->dev,
+                               "fail, endpoint is already being disabled\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if (ep->urb_queued) {
+               dev_dbg(&dev->gadget->dev, "fail, urb already queued\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       if ((in && !ep->ep->caps.dir_in) || (!in && ep->ep->caps.dir_in)) {
+               dev_dbg(&dev->gadget->dev, "fail, wrong direction\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+
+       ep->dev = dev;
+       ep->req->context = &done;
+       ep->req->complete = gadget_ep_complete;
+       ep->req->buf = data;
+       ep->req->length = io->length;
+       ep->req->zero = usb_raw_io_flags_zero(io->flags);
+       ep->urb_queued = true;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       ret = usb_ep_queue(ep->ep, ep->req, GFP_KERNEL);
+       if (ret) {
+               dev_err(&dev->gadget->dev,
+                               "fail, usb_ep_queue returned %d\n", ret);
+               spin_lock_irqsave(&dev->lock, flags);
+               dev->state = STATE_DEV_FAILED;
+               goto out_done;
+       }
+
+       ret = wait_for_completion_interruptible(&done);
+       if (ret) {
+               dev_dbg(&dev->gadget->dev, "wait interrupted\n");
+               usb_ep_dequeue(ep->ep, ep->req);
+               wait_for_completion(&done);
+               spin_lock_irqsave(&dev->lock, flags);
+               goto out_done;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+       ret = ep->status;
+
+out_done:
+       ep->urb_queued = false;
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int raw_ioctl_ep_write(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       char *data;
+       struct usb_raw_ep_io io;
+
+       data = raw_alloc_io_data(&io, (void __user *)value, true);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       ret = raw_process_ep_io(dev, &io, data, true);
+       kfree(data);
+       return ret;
+}
+
+static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       char *data;
+       struct usb_raw_ep_io io;
+       unsigned int length;
+
+       data = raw_alloc_io_data(&io, (void __user *)value, false);
+       if (IS_ERR(data))
+               return PTR_ERR(data);
+       ret = raw_process_ep_io(dev, &io, data, false);
+       if (ret < 0) {
+               kfree(data);
+               return ret;
+       }
+       length = min(io.length, (unsigned int)ret);
+       ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
+       kfree(data);
+       return ret;
+}
+
+static int raw_ioctl_configure(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       if (value)
+               return -EINVAL;
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_dbg(dev->dev, "fail, device is not running\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (!dev->gadget) {
+               dev_dbg(dev->dev, "fail, gadget is not bound\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       usb_gadget_set_state(dev->gadget, USB_STATE_CONFIGURED);
+
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static int raw_ioctl_vbus_draw(struct raw_dev *dev, unsigned long value)
+{
+       int ret = 0;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dev->lock, flags);
+       if (dev->state != STATE_DEV_RUNNING) {
+               dev_dbg(dev->dev, "fail, device is not running\n");
+               ret = -EINVAL;
+               goto out_unlock;
+       }
+       if (!dev->gadget) {
+               dev_dbg(dev->dev, "fail, gadget is not bound\n");
+               ret = -EBUSY;
+               goto out_unlock;
+       }
+       usb_gadget_vbus_draw(dev->gadget, 2 * value);
+
+out_unlock:
+       spin_unlock_irqrestore(&dev->lock, flags);
+       return ret;
+}
+
+static long raw_ioctl(struct file *fd, unsigned int cmd, unsigned long value)
+{
+       struct raw_dev *dev = fd->private_data;
+       int ret = 0;
+
+       if (!dev)
+               return -EBUSY;
+
+       switch (cmd) {
+       case USB_RAW_IOCTL_INIT:
+               ret = raw_ioctl_init(dev, value);
+               break;
+       case USB_RAW_IOCTL_RUN:
+               ret = raw_ioctl_run(dev, value);
+               break;
+       case USB_RAW_IOCTL_EVENT_FETCH:
+               ret = raw_ioctl_event_fetch(dev, value);
+               break;
+       case USB_RAW_IOCTL_EP0_WRITE:
+               ret = raw_ioctl_ep0_write(dev, value);
+               break;
+       case USB_RAW_IOCTL_EP0_READ:
+               ret = raw_ioctl_ep0_read(dev, value);
+               break;
+       case USB_RAW_IOCTL_EP_ENABLE:
+               ret = raw_ioctl_ep_enable(dev, value);
+               break;
+       case USB_RAW_IOCTL_EP_DISABLE:
+               ret = raw_ioctl_ep_disable(dev, value);
+               break;
+       case USB_RAW_IOCTL_EP_WRITE:
+               ret = raw_ioctl_ep_write(dev, value);
+               break;
+       case USB_RAW_IOCTL_EP_READ:
+               ret = raw_ioctl_ep_read(dev, value);
+               break;
+       case USB_RAW_IOCTL_CONFIGURE:
+               ret = raw_ioctl_configure(dev, value);
+               break;
+       case USB_RAW_IOCTL_VBUS_DRAW:
+               ret = raw_ioctl_vbus_draw(dev, value);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/*----------------------------------------------------------------------*/
+
+static const struct file_operations raw_fops = {
+       .open =                 raw_open,
+       .unlocked_ioctl =       raw_ioctl,
+       .compat_ioctl =         raw_ioctl,
+       .release =              raw_release,
+       .llseek =               no_llseek,
+};
+
+static struct miscdevice raw_misc_device = {
+       .minor = MISC_DYNAMIC_MINOR,
+       .name = DRIVER_NAME,
+       .fops = &raw_fops,
+};
+
+module_misc_device(raw_misc_device);
index 797d6ace89943a0015a3814bef336b7dde724524..3a7179e90f4e8f432534bbb0e53596d57f9a7aa2 100644 (file)
@@ -441,11 +441,20 @@ config USB_GADGET_XILINX
          dynamically linked module called "udc-xilinx" and force all
          gadget drivers to also be dynamically linked.
 
+config USB_MAX3420_UDC
+       tristate "MAX3420 (USB-over-SPI) support"
+       depends on SPI
+       help
+         The Maxim MAX3420 chip supports USB2.0 full-speed peripheral mode.
+         The MAX3420 is run by SPI interface, and hence the dependency.
+
+         To compile this driver as a module, choose M here: the module will
+         be called max3420_udc
+
 config USB_TEGRA_XUDC
        tristate "NVIDIA Tegra Superspeed USB 3.0 Device Controller"
        depends on ARCH_TEGRA || COMPILE_TEST
        depends on PHY_TEGRA_XUSB
-       select USB_ROLE_SWITCH
        help
         Enables NVIDIA Tegra USB 3.0 device mode controller driver.
 
index f6777e654a8e0ba376f2ee8c72dd9ae9016c4edd..f5a7ce28aecdfa23740b441ab4e1748f2094237a 100644 (file)
@@ -42,3 +42,4 @@ obj-$(CONFIG_USB_GADGET_XILINX)       += udc-xilinx.o
 obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o
 obj-$(CONFIG_USB_ASPEED_VHUB)  += aspeed-vhub/
 obj-$(CONFIG_USB_BDC_UDC)      += bdc/
+obj-$(CONFIG_USB_MAX3420_UDC)  += max3420_udc.o
index dfdef6a2890452debad0c634100fa3bbe4b3a6f9..0262383f8c79ce699a4f7d6131663ef7642a46e6 100644 (file)
@@ -440,7 +440,7 @@ struct udc_ep_regs {
        /* endpoint data descriptor pointer */
        u32 desptr;
 
-       /* reserverd */
+       /* reserved */
        u32 reserved;
 
        /* write/read confirmation */
index bfd1c9e80a1f79722157caab0de4a79bcb0f971c..80685e4306f314e909448306975d9a9c05cbd75b 100644 (file)
@@ -202,7 +202,7 @@ MODULE_DEVICE_TABLE(pci, pci_id);
 
 /* PCI functions */
 static struct pci_driver udc_pci_driver = {
-       .name =         (char *) name,
+       .name =         name,
        .id_table =     pci_id,
        .probe =        udc_pci_probe,
        .remove =       udc_pci_remove,
index 83ba8a2eb6af9f95fe84a95e44904a20ada3c5ac..605500b19cf322757980d6bb93c51e6095bc248a 100644 (file)
@@ -4,5 +4,5 @@ config USB_ASPEED_VHUB
        depends on ARCH_ASPEED || COMPILE_TEST
        depends on USB_LIBCOMPOSITE
        help
-         USB peripheral controller for the Aspeed AST2500 family
-         SoCs supporting the "vHub" functionality and USB2.0
+         USB peripheral controller for the Aspeed AST2400, AST2500 and
+         AST2600 family SoCs supporting the "vHub" functionality and USB2.0
index 90b134d5dca95ad53795a2c24f983c772f17cff7..f8d35dd60c345a99629a5735ae0a0081fe0c548c 100644 (file)
@@ -99,7 +99,7 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
 {
        struct ast_vhub *vhub = data;
        irqreturn_t iret = IRQ_NONE;
-       u32 istat;
+       u32 i, istat;
 
        /* Stale interrupt while tearing down */
        if (!vhub->ep0_bufs)
@@ -121,10 +121,10 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
 
        /* Handle generic EPs first */
        if (istat & VHUB_IRQ_EP_POOL_ACK_STALL) {
-               u32 i, ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
+               u32 ep_acks = readl(vhub->regs + AST_VHUB_EP_ACK_ISR);
                writel(ep_acks, vhub->regs + AST_VHUB_EP_ACK_ISR);
 
-               for (i = 0; ep_acks && i < AST_VHUB_NUM_GEN_EPs; i++) {
+               for (i = 0; ep_acks && i < vhub->max_epns; i++) {
                        u32 mask = VHUB_EP_IRQ(i);
                        if (ep_acks & mask) {
                                ast_vhub_epn_ack_irq(&vhub->epns[i]);
@@ -134,21 +134,11 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
        }
 
        /* Handle device interrupts */
-       if (istat & (VHUB_IRQ_DEVICE1 |
-                    VHUB_IRQ_DEVICE2 |
-                    VHUB_IRQ_DEVICE3 |
-                    VHUB_IRQ_DEVICE4 |
-                    VHUB_IRQ_DEVICE5)) {
-               if (istat & VHUB_IRQ_DEVICE1)
-                       ast_vhub_dev_irq(&vhub->ports[0].dev);
-               if (istat & VHUB_IRQ_DEVICE2)
-                       ast_vhub_dev_irq(&vhub->ports[1].dev);
-               if (istat & VHUB_IRQ_DEVICE3)
-                       ast_vhub_dev_irq(&vhub->ports[2].dev);
-               if (istat & VHUB_IRQ_DEVICE4)
-                       ast_vhub_dev_irq(&vhub->ports[3].dev);
-               if (istat & VHUB_IRQ_DEVICE5)
-                       ast_vhub_dev_irq(&vhub->ports[4].dev);
+       for (i = 0; i < vhub->max_ports; i++) {
+               u32 dev_mask = VHUB_IRQ_DEVICE1 << i;
+
+               if (istat & dev_mask)
+                       ast_vhub_dev_irq(&vhub->ports[i].dev);
        }
 
        /* Handle top-level vHub EP0 interrupts */
@@ -182,7 +172,7 @@ static irqreturn_t ast_vhub_irq(int irq, void *data)
 
 void ast_vhub_init_hw(struct ast_vhub *vhub)
 {
-       u32 ctrl;
+       u32 ctrl, port_mask, epn_mask;
 
        UDCDBG(vhub,"(Re)Starting HW ...\n");
 
@@ -222,15 +212,20 @@ void ast_vhub_init_hw(struct ast_vhub *vhub)
        }
 
        /* Reset all devices */
-       writel(VHUB_SW_RESET_ALL, vhub->regs + AST_VHUB_SW_RESET);
+       port_mask = GENMASK(vhub->max_ports, 1);
+       writel(VHUB_SW_RESET_ROOT_HUB |
+              VHUB_SW_RESET_DMA_CONTROLLER |
+              VHUB_SW_RESET_EP_POOL |
+              port_mask, vhub->regs + AST_VHUB_SW_RESET);
        udelay(1);
        writel(0, vhub->regs + AST_VHUB_SW_RESET);
 
        /* Disable and cleanup EP ACK/NACK interrupts */
+       epn_mask = GENMASK(vhub->max_epns - 1, 0);
        writel(0, vhub->regs + AST_VHUB_EP_ACK_IER);
        writel(0, vhub->regs + AST_VHUB_EP_NACK_IER);
-       writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_ACK_ISR);
-       writel(VHUB_EP_IRQ_ALL, vhub->regs + AST_VHUB_EP_NACK_ISR);
+       writel(epn_mask, vhub->regs + AST_VHUB_EP_ACK_ISR);
+       writel(epn_mask, vhub->regs + AST_VHUB_EP_NACK_ISR);
 
        /* Default settings for EP0, enable HW hub EP1 */
        writel(0, vhub->regs + AST_VHUB_EP0_CTRL);
@@ -273,7 +268,7 @@ static int ast_vhub_remove(struct platform_device *pdev)
                return 0;
 
        /* Remove devices */
-       for (i = 0; i < AST_VHUB_NUM_PORTS; i++)
+       for (i = 0; i < vhub->max_ports; i++)
                ast_vhub_del_dev(&vhub->ports[i].dev);
 
        spin_lock_irqsave(&vhub->lock, flags);
@@ -295,7 +290,7 @@ static int ast_vhub_remove(struct platform_device *pdev)
        if (vhub->ep0_bufs)
                dma_free_coherent(&pdev->dev,
                                  AST_VHUB_EP0_MAX_PACKET *
-                                 (AST_VHUB_NUM_PORTS + 1),
+                                 (vhub->max_ports + 1),
                                  vhub->ep0_bufs,
                                  vhub->ep0_bufs_dma);
        vhub->ep0_bufs = NULL;
@@ -309,11 +304,32 @@ static int ast_vhub_probe(struct platform_device *pdev)
        struct ast_vhub *vhub;
        struct resource *res;
        int i, rc = 0;
+       const struct device_node *np = pdev->dev.of_node;
 
        vhub = devm_kzalloc(&pdev->dev, sizeof(*vhub), GFP_KERNEL);
        if (!vhub)
                return -ENOMEM;
 
+       rc = of_property_read_u32(np, "aspeed,vhub-downstream-ports",
+                                 &vhub->max_ports);
+       if (rc < 0)
+               vhub->max_ports = AST_VHUB_NUM_PORTS;
+
+       vhub->ports = devm_kcalloc(&pdev->dev, vhub->max_ports,
+                                  sizeof(*vhub->ports), GFP_KERNEL);
+       if (!vhub->ports)
+               return -ENOMEM;
+
+       rc = of_property_read_u32(np, "aspeed,vhub-generic-endpoints",
+                                 &vhub->max_epns);
+       if (rc < 0)
+               vhub->max_epns = AST_VHUB_NUM_GEN_EPs;
+
+       vhub->epns = devm_kcalloc(&pdev->dev, vhub->max_epns,
+                                 sizeof(*vhub->epns), GFP_KERNEL);
+       if (!vhub->epns)
+               return -ENOMEM;
+
        spin_lock_init(&vhub->lock);
        vhub->pdev = pdev;
 
@@ -366,7 +382,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
         */
        vhub->ep0_bufs = dma_alloc_coherent(&pdev->dev,
                                            AST_VHUB_EP0_MAX_PACKET *
-                                           (AST_VHUB_NUM_PORTS + 1),
+                                           (vhub->max_ports + 1),
                                            &vhub->ep0_bufs_dma, GFP_KERNEL);
        if (!vhub->ep0_bufs) {
                dev_err(&pdev->dev, "Failed to allocate EP0 DMA buffers\n");
@@ -380,7 +396,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
        ast_vhub_init_ep0(vhub, &vhub->ep0, NULL);
 
        /* Init devices */
-       for (i = 0; i < AST_VHUB_NUM_PORTS && rc == 0; i++)
+       for (i = 0; i < vhub->max_ports && rc == 0; i++)
                rc = ast_vhub_init_dev(vhub, i);
        if (rc)
                goto err;
@@ -407,6 +423,9 @@ static const struct of_device_id ast_vhub_dt_ids[] = {
        {
                .compatible = "aspeed,ast2500-usb-vhub",
        },
+       {
+               .compatible = "aspeed,ast2600-usb-vhub",
+       },
        { }
 };
 MODULE_DEVICE_TABLE(of, ast_vhub_dt_ids);
index 4008e7a51188970d0143e97a7ed0a2b30a9c8b91..d268306a7bfeef79338d2eb47f84157a3d9bcf50 100644 (file)
@@ -77,7 +77,7 @@ static void ast_vhub_dev_enable(struct ast_vhub_dev *d)
        writel(d->ep0.buf_dma, d->regs + AST_VHUB_DEV_EP0_DATA);
 
        /* Clear stall on all EPs */
-       for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+       for (i = 0; i < d->max_epns; i++) {
                struct ast_vhub_ep *ep = d->epns[i];
 
                if (ep && (ep->epn.stalled || ep->epn.wedged)) {
@@ -137,7 +137,7 @@ static int ast_vhub_ep_feature(struct ast_vhub_dev *d,
             is_set ? "SET" : "CLEAR", ep_num, wValue);
        if (ep_num == 0)
                return std_req_complete;
-       if (ep_num >= AST_VHUB_NUM_GEN_EPs || !d->epns[ep_num - 1])
+       if (ep_num >= d->max_epns || !d->epns[ep_num - 1])
                return std_req_stall;
        if (wValue != USB_ENDPOINT_HALT)
                return std_req_driver;
@@ -181,7 +181,7 @@ static int ast_vhub_ep_status(struct ast_vhub_dev *d,
 
        DDBG(d, "GET_STATUS(ep%d)\n", ep_num);
 
-       if (ep_num >= AST_VHUB_NUM_GEN_EPs)
+       if (ep_num >= d->max_epns)
                return std_req_stall;
        if (ep_num != 0) {
                ep = d->epns[ep_num - 1];
@@ -299,7 +299,7 @@ static void ast_vhub_dev_nuke(struct ast_vhub_dev *d)
 {
        unsigned int i;
 
-       for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++) {
+       for (i = 0; i < d->max_epns; i++) {
                if (!d->epns[i])
                        continue;
                ast_vhub_nuke(d->epns[i], -ESHUTDOWN);
@@ -416,10 +416,10 @@ static struct usb_ep *ast_vhub_udc_match_ep(struct usb_gadget *gadget,
         * that will allow the generic code to use our
         * assigned address.
         */
-       for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+       for (i = 0; i < d->max_epns; i++)
                if (d->epns[i] == NULL)
                        break;
-       if (i >= AST_VHUB_NUM_GEN_EPs)
+       if (i >= d->max_epns)
                return NULL;
        addr = i + 1;
 
@@ -526,6 +526,7 @@ void ast_vhub_del_dev(struct ast_vhub_dev *d)
 
        usb_del_gadget_udc(&d->gadget);
        device_unregister(d->port_dev);
+       kfree(d->epns);
 }
 
 static void ast_vhub_dev_release(struct device *dev)
@@ -546,14 +547,25 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
 
        ast_vhub_init_ep0(vhub, &d->ep0, d);
 
+       /*
+        * A USB device can have up to 30 endpoints besides control
+        * endpoint 0.
+        */
+       d->max_epns = min_t(u32, vhub->max_epns, 30);
+       d->epns = kcalloc(d->max_epns, sizeof(*d->epns), GFP_KERNEL);
+       if (!d->epns)
+               return -ENOMEM;
+
        /*
         * The UDC core really needs us to have separate and uniquely
         * named "parent" devices for each port so we create a sub device
         * here for that purpose
         */
        d->port_dev = kzalloc(sizeof(struct device), GFP_KERNEL);
-       if (!d->port_dev)
-               return -ENOMEM;
+       if (!d->port_dev) {
+               rc = -ENOMEM;
+               goto fail_alloc;
+       }
        device_initialize(d->port_dev);
        d->port_dev->release = ast_vhub_dev_release;
        d->port_dev->parent = parent;
@@ -584,6 +596,8 @@ int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx)
        device_del(d->port_dev);
  fail_add:
        put_device(d->port_dev);
+ fail_alloc:
+       kfree(d->epns);
 
        return rc;
 }
index 7475c74aa5c5ea86b6da895e2689533c31d23856..0bd6b20435b8a547a1b4dd09cff6be3dbc985f3a 100644 (file)
@@ -800,10 +800,10 @@ struct ast_vhub_ep *ast_vhub_alloc_epn(struct ast_vhub_dev *d, u8 addr)
 
        /* Find a free one (no device) */
        spin_lock_irqsave(&vhub->lock, flags);
-       for (i = 0; i < AST_VHUB_NUM_GEN_EPs; i++)
+       for (i = 0; i < vhub->max_epns; i++)
                if (vhub->epns[i].dev == NULL)
                        break;
-       if (i >= AST_VHUB_NUM_GEN_EPs) {
+       if (i >= vhub->max_epns) {
                spin_unlock_irqrestore(&vhub->lock, flags);
                return NULL;
        }
index 19b3517e04c0abc9f0b300f64a0e6972ecb57d2e..6e565c3dbb5b88d87889649a522c3585e2b806f2 100644 (file)
@@ -93,11 +93,7 @@ static void ast_vhub_patch_dev_desc_usb1(struct usb_device_descriptor *desc)
                                 USB_DT_INTERFACE_SIZE + \
                                 USB_DT_ENDPOINT_SIZE)
 
-static const struct ast_vhub_full_cdesc {
-       struct usb_config_descriptor    cfg;
-       struct usb_interface_descriptor intf;
-       struct usb_endpoint_descriptor  ep;
-} __attribute__ ((packed)) ast_vhub_conf_desc = {
+static const struct ast_vhub_full_cdesc ast_vhub_conf_desc = {
        .cfg = {
                .bLength                = USB_DT_CONFIG_SIZE,
                .bDescriptorType        = USB_DT_CONFIG,
@@ -266,6 +262,7 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
                             u8 desc_type, u16 len)
 {
        size_t dsize;
+       struct ast_vhub *vhub = ep->vhub;
 
        EPDBG(ep, "GET_DESCRIPTOR(type:%d)\n", desc_type);
 
@@ -281,20 +278,20 @@ static int ast_vhub_rep_desc(struct ast_vhub_ep *ep,
        switch(desc_type) {
        case USB_DT_DEVICE:
                dsize = USB_DT_DEVICE_SIZE;
-               memcpy(ep->buf, &ast_vhub_dev_desc, dsize);
-               BUILD_BUG_ON(dsize > sizeof(ast_vhub_dev_desc));
+               memcpy(ep->buf, &vhub->vhub_dev_desc, dsize);
+               BUILD_BUG_ON(dsize > sizeof(vhub->vhub_dev_desc));
                BUILD_BUG_ON(USB_DT_DEVICE_SIZE >= AST_VHUB_EP0_MAX_PACKET);
                break;
        case USB_DT_CONFIG:
                dsize = AST_VHUB_CONF_DESC_SIZE;
-               memcpy(ep->buf, &ast_vhub_conf_desc, dsize);
-               BUILD_BUG_ON(dsize > sizeof(ast_vhub_conf_desc));
+               memcpy(ep->buf, &vhub->vhub_conf_desc, dsize);
+               BUILD_BUG_ON(dsize > sizeof(vhub->vhub_conf_desc));
                BUILD_BUG_ON(AST_VHUB_CONF_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
                break;
        case USB_DT_HUB:
                dsize = AST_VHUB_HUB_DESC_SIZE;
-               memcpy(ep->buf, &ast_vhub_hub_desc, dsize);
-               BUILD_BUG_ON(dsize > sizeof(ast_vhub_hub_desc));
+               memcpy(ep->buf, &vhub->vhub_hub_desc, dsize);
+               BUILD_BUG_ON(dsize > sizeof(vhub->vhub_hub_desc));
                BUILD_BUG_ON(AST_VHUB_HUB_DESC_SIZE >= AST_VHUB_EP0_MAX_PACKET);
                break;
        default:
@@ -317,7 +314,8 @@ 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 (&ast_vhub_strings, string_id, ep->buf);
+       int rc = usb_gadget_get_string(&ep->vhub->vhub_str_desc,
+                                       string_id, ep->buf);
 
        /*
         * This should never happen unless we put too big strings in
@@ -504,7 +502,7 @@ static void ast_vhub_wake_work(struct work_struct *work)
         * we let the normal host wake path deal with it later.
         */
        spin_lock_irqsave(&vhub->lock, flags);
-       for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+       for (i = 0; i < vhub->max_ports; i++) {
                struct ast_vhub_port *p = &vhub->ports[i];
 
                if (!(p->status & USB_PORT_STAT_SUSPEND))
@@ -587,7 +585,7 @@ static enum std_req_rc ast_vhub_set_port_feature(struct ast_vhub_ep *ep,
        struct ast_vhub *vhub = ep->vhub;
        struct ast_vhub_port *p;
 
-       if (port == 0 || port > AST_VHUB_NUM_PORTS)
+       if (port == 0 || port > vhub->max_ports)
                return std_req_stall;
        port--;
        p = &vhub->ports[port];
@@ -630,7 +628,7 @@ static enum std_req_rc ast_vhub_clr_port_feature(struct ast_vhub_ep *ep,
        struct ast_vhub *vhub = ep->vhub;
        struct ast_vhub_port *p;
 
-       if (port == 0 || port > AST_VHUB_NUM_PORTS)
+       if (port == 0 || port > vhub->max_ports)
                return std_req_stall;
        port--;
        p = &vhub->ports[port];
@@ -676,7 +674,7 @@ static enum std_req_rc ast_vhub_get_port_stat(struct ast_vhub_ep *ep,
        struct ast_vhub *vhub = ep->vhub;
        u16 stat, chg;
 
-       if (port == 0 || port > AST_VHUB_NUM_PORTS)
+       if (port == 0 || port > vhub->max_ports)
                return std_req_stall;
        port--;
 
@@ -757,7 +755,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub)
         * Forward to unsuspended ports without changing
         * their connection status.
         */
-       for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+       for (i = 0; i < vhub->max_ports; i++) {
                struct ast_vhub_port *p = &vhub->ports[i];
 
                if (!(p->status & USB_PORT_STAT_SUSPEND))
@@ -780,7 +778,7 @@ void ast_vhub_hub_resume(struct ast_vhub *vhub)
         * Forward to unsuspended ports without changing
         * their connection status.
         */
-       for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+       for (i = 0; i < vhub->max_ports; i++) {
                struct ast_vhub_port *p = &vhub->ports[i];
 
                if (!(p->status & USB_PORT_STAT_SUSPEND))
@@ -814,7 +812,7 @@ void ast_vhub_hub_reset(struct ast_vhub *vhub)
         * Clear all port status, disable gadgets and "suspend"
         * them. They will be woken up by a port reset.
         */
-       for (i = 0; i < AST_VHUB_NUM_PORTS; i++) {
+       for (i = 0; i < vhub->max_ports; i++) {
                struct ast_vhub_port *p = &vhub->ports[i];
 
                /* Only keep the connected flag */
@@ -834,9 +832,31 @@ 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)
+{
+       /* Initialize vhub Device Descriptor. */
+       memcpy(&vhub->vhub_dev_desc, &ast_vhub_dev_desc,
+               sizeof(vhub->vhub_dev_desc));
+
+       /* Initialize vhub Configuration Descriptor. */
+       memcpy(&vhub->vhub_conf_desc, &ast_vhub_conf_desc,
+               sizeof(vhub->vhub_conf_desc));
+
+       /* Initialize vhub Hub Descriptor. */
+       memcpy(&vhub->vhub_hub_desc, &ast_vhub_hub_desc,
+               sizeof(vhub->vhub_hub_desc));
+       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));
+}
+
 void 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);
 }
 
index 761919e220d360154f0350d9a1d54ca7a4da0319..fac79ef6d669eaddedf25788b809c839a77cb2d6 100644 (file)
@@ -2,6 +2,9 @@
 #ifndef __ASPEED_VHUB_H
 #define __ASPEED_VHUB_H
 
+#include <linux/usb.h>
+#include <linux/usb/ch11.h>
+
 /*****************************
  *                           *
  * VHUB register definitions *
 #define VHUB_SW_RESET_DEVICE2                  (1 << 2)
 #define VHUB_SW_RESET_DEVICE1                  (1 << 1)
 #define VHUB_SW_RESET_ROOT_HUB                 (1 << 0)
-#define VHUB_SW_RESET_ALL                      (VHUB_SW_RESET_EP_POOL | \
-                                                VHUB_SW_RESET_DMA_CONTROLLER | \
-                                                VHUB_SW_RESET_DEVICE5 | \
-                                                VHUB_SW_RESET_DEVICE4 | \
-                                                VHUB_SW_RESET_DEVICE3 | \
-                                                VHUB_SW_RESET_DEVICE2 | \
-                                                VHUB_SW_RESET_DEVICE1 | \
-                                                VHUB_SW_RESET_ROOT_HUB)
+
 /* EP ACK/NACK IRQ masks */
 #define VHUB_EP_IRQ(n)                         (1 << (n))
-#define VHUB_EP_IRQ_ALL                                0x7fff  /* 15 EPs */
 
 /* USB status reg */
 #define VHUB_USBSTS_HISPEED                    (1 << 27)
  *                                      *
  ****************************************/
 
+/*
+ * AST_VHUB_NUM_GEN_EPs and AST_VHUB_NUM_PORTS are kept to avoid breaking
+ * existing AST2400/AST2500 platforms. AST2600 and future vhub revisions
+ * should define number of downstream ports and endpoints in device tree.
+ */
 #define AST_VHUB_NUM_GEN_EPs   15      /* Generic non-0 EPs */
 #define AST_VHUB_NUM_PORTS     5       /* vHub ports */
 #define AST_VHUB_EP0_MAX_PACKET        64      /* EP0's max packet size */
@@ -312,7 +312,7 @@ struct ast_vhub_ep {
                        /* Registers */
                        void __iomem            *regs;
 
-                       /* Index in global pool (0..14) */
+                       /* Index in global pool (zero-based) */
                        unsigned int            g_idx;
 
                        /* DMA Descriptors */
@@ -342,7 +342,7 @@ struct ast_vhub_dev {
        struct ast_vhub                 *vhub;
        void __iomem                    *regs;
 
-       /* Device index (0...4) and name string */
+       /* Device index (zero-based) and name string */
        unsigned int                    index;
        const char                      *name;
 
@@ -358,7 +358,8 @@ struct ast_vhub_dev {
 
        /* Endpoint structures */
        struct ast_vhub_ep              ep0;
-       struct ast_vhub_ep              *epns[AST_VHUB_NUM_GEN_EPs];
+       struct ast_vhub_ep              **epns;
+       u32                             max_epns;
 
 };
 #define to_ast_dev(__g) container_of(__g, struct ast_vhub_dev, gadget)
@@ -373,6 +374,12 @@ struct ast_vhub_port {
        struct ast_vhub_dev     dev;
 };
 
+struct ast_vhub_full_cdesc {
+       struct usb_config_descriptor    cfg;
+       struct usb_interface_descriptor intf;
+       struct usb_endpoint_descriptor  ep;
+} __packed;
+
 /* Global vhub structure */
 struct ast_vhub {
        struct platform_device          *pdev;
@@ -393,10 +400,12 @@ struct ast_vhub {
        bool                            ep1_stalled : 1;
 
        /* Per-port info */
-       struct ast_vhub_port            ports[AST_VHUB_NUM_PORTS];
+       struct ast_vhub_port            *ports;
+       u32                             max_ports;
 
        /* Generic EP data structures */
-       struct ast_vhub_ep              epns[AST_VHUB_NUM_GEN_EPs];
+       struct ast_vhub_ep              *epns;
+       u32                             max_epns;
 
        /* Upstream bus is suspended ? */
        bool                            suspended : 1;
@@ -409,6 +418,12 @@ struct ast_vhub {
 
        /* Upstream bus speed captured at bus reset */
        unsigned int                    speed;
+
+       /* Standard USB Descriptors of the 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;
 };
 
 /* Standard request handlers result codes */
index 1b2b548c59a0eee9a6b2bdffc1f56048e016659b..eede5cedacb4a3785000f72439b4145638dbf8b2 100644 (file)
@@ -2021,7 +2021,7 @@ static struct platform_driver at91_udc_driver = {
        .suspend        = at91udc_suspend,
        .resume         = at91udc_resume,
        .driver         = {
-               .name   = (char *) driver_name,
+               .name   = driver_name,
                .of_match_table = at91_udc_dt_ids,
        },
 };
index 4c9d1e49d5ed150f20d193dcc6aa4761f7b47257..6e3e3ebf715f040dd253f1486217fbe6e5a7e61f 100644 (file)
@@ -1134,7 +1134,7 @@ static struct platform_driver dummy_udc_driver = {
        .suspend        = dummy_udc_suspend,
        .resume         = dummy_udc_resume,
        .driver         = {
-               .name   = (char *) gadget_name,
+               .name   = gadget_name,
        },
 };
 
@@ -2720,7 +2720,7 @@ static struct platform_driver dummy_hcd_driver = {
        .suspend        = dummy_hcd_suspend,
        .resume         = dummy_hcd_resume,
        .driver         = {
-               .name   = (char *) driver_name,
+               .name   = driver_name,
        },
 };
 
index 21f3e6c4e4d6492763e14011a7befd5f30025aa2..d6ca50f019853675b865bc8e60423fe73d54744a 100644 (file)
@@ -1199,7 +1199,7 @@ err:
 
 static struct platform_driver fotg210_driver = {
        .driver         = {
-               .name = (char *)udc_name,
+               .name = udc_name,
        },
        .probe          = fotg210_udc_probe,
        .remove         = fotg210_udc_remove,
index ec6eda4262230c5cf3e231cdd42891aea8b82d44..febabde62f71a790e98835d5616d63a4d41d679a 100644 (file)
@@ -53,7 +53,6 @@
 #define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
 
 static const char driver_name[] = "fsl-usb2-udc";
-static const char driver_desc[] = DRIVER_DESC;
 
 static struct usb_dr_device __iomem *dr_regs;
 
index 00e3f66836a982352bf3c3f31907fa3c9154df5a..9af8b415f303bbcd1e4f50a64327fa8bd64b3861 100644 (file)
@@ -1507,7 +1507,7 @@ clean_up:
 static struct platform_driver fusb300_driver = {
        .remove =       fusb300_remove,
        .driver         = {
-               .name = (char *) udc_name,
+               .name = udc_name,
        },
 };
 
index 4a46f661d0e41fe7c1745cfeb42cd487b31bea87..91dcb1995c27d935811671b0c5b27738942e233b 100644 (file)
@@ -1844,7 +1844,7 @@ static const struct pci_device_id pci_ids[] = { {
 MODULE_DEVICE_TABLE (pci, pci_ids);
 
 static struct pci_driver goku_pci_driver = {
-       .name =         (char *) driver_name,
+       .name =         driver_name,
        .id_table =     pci_ids,
 
        .probe =        goku_probe,
index d14b2bb3f67c7015d75343dd1bb7800d12a28146..cb997b82c0088182ed34a23bbe5ba1daf79e24eb 100644 (file)
@@ -3267,7 +3267,7 @@ static struct platform_driver lpc32xx_udc_driver = {
        .suspend        = lpc32xx_udc_suspend,
        .resume         = lpc32xx_udc_resume,
        .driver         = {
-               .name   = (char *) driver_name,
+               .name   = driver_name,
                .of_match_table = of_match_ptr(lpc32xx_udc_of_match),
        },
 };
index a8288df6aadf09c5523d1922d6a71f0c646940fb..75d16a8902e6dbdd4ac5bf92e09a946b75a76605 100644 (file)
@@ -1691,7 +1691,7 @@ clean_up:
 static struct platform_driver m66592_driver = {
        .remove =       m66592_remove,
        .driver         = {
-               .name = (char *) udc_name,
+               .name = udc_name,
        },
 };
 
diff --git a/drivers/usb/gadget/udc/max3420_udc.c b/drivers/usb/gadget/udc/max3420_udc.c
new file mode 100644 (file)
index 0000000..8fbc083
--- /dev/null
@@ -0,0 +1,1331 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * MAX3420 Device Controller driver for USB.
+ *
+ * Author: Jaswinder Singh Brar <jaswinder.singh@linaro.org>
+ * (C) Copyright 2019-2020 Linaro Ltd
+ *
+ * Based on:
+ *     o MAX3420E datasheet
+ *             http://datasheets.maximintegrated.com/en/ds/MAX3420E.pdf
+ *     o MAX342{0,1}E Programming Guides
+ *             https://pdfserv.maximintegrated.com/en/an/AN3598.pdf
+ *             https://pdfserv.maximintegrated.com/en/an/AN3785.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/bitfield.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
+#include <linux/of_irq.h>
+#include <linux/prefetch.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+
+#define MAX3420_MAX_EPS                4
+#define MAX3420_EP_MAX_PACKET          64  /* Same for all Endpoints */
+#define MAX3420_EPNAME_SIZE            16  /* Buffer size for endpoint name */
+
+#define MAX3420_ACKSTAT                BIT(0)
+
+#define MAX3420_SPI_DIR_RD     0       /* read register from MAX3420 */
+#define MAX3420_SPI_DIR_WR     1       /* write register to MAX3420 */
+
+/* SPI commands: */
+#define MAX3420_SPI_DIR_SHIFT  1
+#define MAX3420_SPI_REG_SHIFT  3
+
+#define MAX3420_REG_EP0FIFO    0
+#define MAX3420_REG_EP1FIFO    1
+#define MAX3420_REG_EP2FIFO    2
+#define MAX3420_REG_EP3FIFO    3
+#define MAX3420_REG_SUDFIFO    4
+#define MAX3420_REG_EP0BC      5
+#define MAX3420_REG_EP1BC      6
+#define MAX3420_REG_EP2BC      7
+#define MAX3420_REG_EP3BC      8
+
+#define MAX3420_REG_EPSTALLS   9
+       #define ACKSTAT         BIT(6)
+       #define STLSTAT         BIT(5)
+       #define STLEP3IN        BIT(4)
+       #define STLEP2IN        BIT(3)
+       #define STLEP1OUT       BIT(2)
+       #define STLEP0OUT       BIT(1)
+       #define STLEP0IN        BIT(0)
+
+#define MAX3420_REG_CLRTOGS    10
+       #define EP3DISAB        BIT(7)
+       #define EP2DISAB        BIT(6)
+       #define EP1DISAB        BIT(5)
+       #define CTGEP3IN        BIT(4)
+       #define CTGEP2IN        BIT(3)
+       #define CTGEP1OUT       BIT(2)
+
+#define MAX3420_REG_EPIRQ      11
+#define MAX3420_REG_EPIEN      12
+       #define SUDAVIRQ        BIT(5)
+       #define IN3BAVIRQ       BIT(4)
+       #define IN2BAVIRQ       BIT(3)
+       #define OUT1DAVIRQ      BIT(2)
+       #define OUT0DAVIRQ      BIT(1)
+       #define IN0BAVIRQ       BIT(0)
+
+#define MAX3420_REG_USBIRQ     13
+#define MAX3420_REG_USBIEN     14
+       #define OSCOKIRQ        BIT(0)
+       #define RWUDNIRQ        BIT(1)
+       #define BUSACTIRQ       BIT(2)
+       #define URESIRQ         BIT(3)
+       #define SUSPIRQ         BIT(4)
+       #define NOVBUSIRQ       BIT(5)
+       #define VBUSIRQ         BIT(6)
+       #define URESDNIRQ       BIT(7)
+
+#define MAX3420_REG_USBCTL     15
+       #define HOSCSTEN        BIT(7)
+       #define VBGATE          BIT(6)
+       #define CHIPRES         BIT(5)
+       #define PWRDOWN         BIT(4)
+       #define CONNECT         BIT(3)
+       #define SIGRWU          BIT(2)
+
+#define MAX3420_REG_CPUCTL     16
+       #define IE              BIT(0)
+
+#define MAX3420_REG_PINCTL     17
+       #define EP3INAK         BIT(7)
+       #define EP2INAK         BIT(6)
+       #define EP0INAK         BIT(5)
+       #define FDUPSPI         BIT(4)
+       #define INTLEVEL        BIT(3)
+       #define POSINT          BIT(2)
+       #define GPXB            BIT(1)
+       #define GPXA            BIT(0)
+
+#define MAX3420_REG_REVISION   18
+
+#define MAX3420_REG_FNADDR     19
+       #define FNADDR_MASK     0x7f
+
+#define MAX3420_REG_IOPINS     20
+#define MAX3420_REG_IOPINS2    21
+#define MAX3420_REG_GPINIRQ    22
+#define MAX3420_REG_GPINIEN    23
+#define MAX3420_REG_GPINPOL    24
+#define MAX3420_REG_HIRQ       25
+#define MAX3420_REG_HIEN       26
+#define MAX3420_REG_MODE       27
+#define MAX3420_REG_PERADDR    28
+#define MAX3420_REG_HCTL       29
+#define MAX3420_REG_HXFR       30
+#define MAX3420_REG_HRSL       31
+
+#define ENABLE_IRQ     BIT(0)
+#define IOPIN_UPDATE   BIT(1)
+#define REMOTE_WAKEUP  BIT(2)
+#define CONNECT_HOST   GENMASK(4, 3)
+#define        HCONNECT        (1 << 3)
+#define        HDISCONNECT     (3 << 3)
+#define UDC_START      GENMASK(6, 5)
+#define        START           (1 << 5)
+#define        STOP            (3 << 5)
+#define ENABLE_EP      GENMASK(8, 7)
+#define        ENABLE          (1 << 7)
+#define        DISABLE         (3 << 7)
+#define STALL_EP       GENMASK(10, 9)
+#define        STALL           (1 << 9)
+#define        UNSTALL         (3 << 9)
+
+#define MAX3420_CMD(c)         FIELD_PREP(GENMASK(7, 3), c)
+#define MAX3420_SPI_CMD_RD(c)  (MAX3420_CMD(c) | (0 << 1))
+#define MAX3420_SPI_CMD_WR(c)  (MAX3420_CMD(c) | (1 << 1))
+
+struct max3420_req {
+       struct usb_request usb_req;
+       struct list_head queue;
+       struct max3420_ep *ep;
+};
+
+struct max3420_ep {
+       struct usb_ep ep_usb;
+       struct max3420_udc *udc;
+       struct list_head queue;
+       char name[MAX3420_EPNAME_SIZE];
+       unsigned int maxpacket;
+       spinlock_t lock;
+       int halted;
+       u32 todo;
+       int id;
+};
+
+struct max3420_udc {
+       struct usb_gadget gadget;
+       struct max3420_ep ep[MAX3420_MAX_EPS];
+       struct usb_gadget_driver *driver;
+       struct task_struct *thread_task;
+       int remote_wkp, is_selfpowered;
+       bool vbus_active, softconnect;
+       struct usb_ctrlrequest setup;
+       struct mutex spi_bus_mutex;
+       struct max3420_req ep0req;
+       struct spi_device *spi;
+       struct device *dev;
+       spinlock_t lock;
+       bool suspended;
+       u8 ep0buf[64];
+       u32 todo;
+};
+
+#define to_max3420_req(r)      container_of((r), struct max3420_req, usb_req)
+#define to_max3420_ep(e)       container_of((e), struct max3420_ep, ep_usb)
+#define to_udc(g)              container_of((g), struct max3420_udc, gadget)
+
+#define DRIVER_DESC     "MAX3420 USB Device-Mode Driver"
+static const char driver_name[] = "max3420-udc";
+
+/* Control endpoint configuration.*/
+static const struct usb_endpoint_descriptor ep0_desc = {
+       .bEndpointAddress       = USB_DIR_OUT,
+       .bmAttributes           = USB_ENDPOINT_XFER_CONTROL,
+       .wMaxPacketSize         = cpu_to_le16(MAX3420_EP_MAX_PACKET),
+};
+
+static void spi_ack_ctrl(struct max3420_udc *udc)
+{
+       struct spi_device *spi = udc->spi;
+       struct spi_transfer transfer;
+       struct spi_message msg;
+       u8 txdata[1];
+
+       memset(&transfer, 0, sizeof(transfer));
+
+       spi_message_init(&msg);
+
+       txdata[0] = MAX3420_ACKSTAT;
+       transfer.tx_buf = txdata;
+       transfer.len = 1;
+
+       spi_message_add_tail(&transfer, &msg);
+       spi_sync(spi, &msg);
+}
+
+static u8 spi_rd8_ack(struct max3420_udc *udc, u8 reg, int actstat)
+{
+       struct spi_device *spi = udc->spi;
+       struct spi_transfer transfer;
+       struct spi_message msg;
+       u8 txdata[2], rxdata[2];
+
+       memset(&transfer, 0, sizeof(transfer));
+
+       spi_message_init(&msg);
+
+       txdata[0] = MAX3420_SPI_CMD_RD(reg) | (actstat ? MAX3420_ACKSTAT : 0);
+       transfer.tx_buf = txdata;
+       transfer.rx_buf = rxdata;
+       transfer.len = 2;
+
+       spi_message_add_tail(&transfer, &msg);
+       spi_sync(spi, &msg);
+
+       return rxdata[1];
+}
+
+static u8 spi_rd8(struct max3420_udc *udc, u8 reg)
+{
+       return spi_rd8_ack(udc, reg, 0);
+}
+
+static void spi_wr8_ack(struct max3420_udc *udc, u8 reg, u8 val, int actstat)
+{
+       struct spi_device *spi = udc->spi;
+       struct spi_transfer transfer;
+       struct spi_message msg;
+       u8 txdata[2];
+
+       memset(&transfer, 0, sizeof(transfer));
+
+       spi_message_init(&msg);
+
+       txdata[0] = MAX3420_SPI_CMD_WR(reg) | (actstat ? MAX3420_ACKSTAT : 0);
+       txdata[1] = val;
+
+       transfer.tx_buf = txdata;
+       transfer.len = 2;
+
+       spi_message_add_tail(&transfer, &msg);
+       spi_sync(spi, &msg);
+}
+
+static void spi_wr8(struct max3420_udc *udc, u8 reg, u8 val)
+{
+       spi_wr8_ack(udc, reg, val, 0);
+}
+
+static void spi_rd_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
+{
+       struct spi_device *spi = udc->spi;
+       struct spi_transfer transfer;
+       struct spi_message msg;
+       u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {};
+
+       memset(&transfer, 0, sizeof(transfer));
+
+       spi_message_init(&msg);
+
+       local_buf[0] = MAX3420_SPI_CMD_RD(reg);
+       transfer.tx_buf = &local_buf[0];
+       transfer.rx_buf = &local_buf[0];
+       transfer.len = len + 1;
+
+       spi_message_add_tail(&transfer, &msg);
+       spi_sync(spi, &msg);
+
+       memcpy(buf, &local_buf[1], len);
+}
+
+static void spi_wr_buf(struct max3420_udc *udc, u8 reg, void *buf, u8 len)
+{
+       struct spi_device *spi = udc->spi;
+       struct spi_transfer transfer;
+       struct spi_message msg;
+       u8 local_buf[MAX3420_EP_MAX_PACKET + 1] = {};
+
+       memset(&transfer, 0, sizeof(transfer));
+
+       spi_message_init(&msg);
+
+       local_buf[0] = MAX3420_SPI_CMD_WR(reg);
+       memcpy(&local_buf[1], buf, len);
+
+       transfer.tx_buf = local_buf;
+       transfer.len = len + 1;
+
+       spi_message_add_tail(&transfer, &msg);
+       spi_sync(spi, &msg);
+}
+
+static int spi_max3420_enable(struct max3420_ep *ep)
+{
+       struct max3420_udc *udc = ep->udc;
+       unsigned long flags;
+       u8 epdis, epien;
+       int todo;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       todo = ep->todo & ENABLE_EP;
+       ep->todo &= ~ENABLE_EP;
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       if (!todo || ep->id == 0)
+               return false;
+
+       epien = spi_rd8(udc, MAX3420_REG_EPIEN);
+       epdis = spi_rd8(udc, MAX3420_REG_CLRTOGS);
+
+       if (todo == ENABLE) {
+               epdis &= ~BIT(ep->id + 4);
+               epien |= BIT(ep->id + 1);
+       } else {
+               epdis |= BIT(ep->id + 4);
+               epien &= ~BIT(ep->id + 1);
+       }
+
+       spi_wr8(udc, MAX3420_REG_CLRTOGS, epdis);
+       spi_wr8(udc, MAX3420_REG_EPIEN, epien);
+
+       return true;
+}
+
+static int spi_max3420_stall(struct max3420_ep *ep)
+{
+       struct max3420_udc *udc = ep->udc;
+       unsigned long flags;
+       u8 epstalls;
+       int todo;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       todo = ep->todo & STALL_EP;
+       ep->todo &= ~STALL_EP;
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       if (!todo || ep->id == 0)
+               return false;
+
+       epstalls = spi_rd8(udc, MAX3420_REG_EPSTALLS);
+       if (todo == STALL) {
+               ep->halted = 1;
+               epstalls |= BIT(ep->id + 1);
+       } else {
+               u8 clrtogs;
+
+               ep->halted = 0;
+               epstalls &= ~BIT(ep->id + 1);
+               clrtogs = spi_rd8(udc, MAX3420_REG_CLRTOGS);
+               clrtogs |= BIT(ep->id + 1);
+               spi_wr8(udc, MAX3420_REG_CLRTOGS, clrtogs);
+       }
+       spi_wr8(udc, MAX3420_REG_EPSTALLS, epstalls | ACKSTAT);
+
+       return true;
+}
+
+static int spi_max3420_rwkup(struct max3420_udc *udc)
+{
+       unsigned long flags;
+       int wake_remote;
+       u8 usbctl;
+
+       spin_lock_irqsave(&udc->lock, flags);
+       wake_remote = udc->todo & REMOTE_WAKEUP;
+       udc->todo &= ~REMOTE_WAKEUP;
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (!wake_remote || !udc->suspended)
+               return false;
+
+       /* Set Remote-WkUp Signal*/
+       usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
+       usbctl |= SIGRWU;
+       spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
+
+       msleep_interruptible(5);
+
+       /* Clear Remote-WkUp Signal*/
+       usbctl = spi_rd8(udc, MAX3420_REG_USBCTL);
+       usbctl &= ~SIGRWU;
+       spi_wr8(udc, MAX3420_REG_USBCTL, usbctl);
+
+       udc->suspended = false;
+
+       return true;
+}
+
+static void max3420_nuke(struct max3420_ep *ep, int status);
+static void __max3420_stop(struct max3420_udc *udc)
+{
+       u8 val;
+       int i;
+
+       /* clear all pending requests */
+       for (i = 1; i < MAX3420_MAX_EPS; i++)
+               max3420_nuke(&udc->ep[i], -ECONNRESET);
+
+       /* Disable IRQ to CPU */
+       spi_wr8(udc, MAX3420_REG_CPUCTL, 0);
+
+       val = spi_rd8(udc, MAX3420_REG_USBCTL);
+       val |= PWRDOWN;
+       if (udc->is_selfpowered)
+               val &= ~HOSCSTEN;
+       else
+               val |= HOSCSTEN;
+       spi_wr8(udc, MAX3420_REG_USBCTL, val);
+}
+
+static void __max3420_start(struct max3420_udc *udc)
+{
+       u8 val;
+
+       /* Need this delay if bus-powered,
+        * but even for self-powered it helps stability
+        */
+       msleep_interruptible(250);
+
+       /* configure SPI */
+       spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI);
+
+       /* Chip Reset */
+       spi_wr8(udc, MAX3420_REG_USBCTL, CHIPRES);
+       msleep_interruptible(5);
+       spi_wr8(udc, MAX3420_REG_USBCTL, 0);
+
+       /* Poll for OSC to stabilize */
+       while (1) {
+               val = spi_rd8(udc, MAX3420_REG_USBIRQ);
+               if (val & OSCOKIRQ)
+                       break;
+               cond_resched();
+       }
+
+       /* Enable PULL-UP only when Vbus detected */
+       val = spi_rd8(udc, MAX3420_REG_USBCTL);
+       val |= VBGATE | CONNECT;
+       spi_wr8(udc, MAX3420_REG_USBCTL, val);
+
+       val = URESDNIRQ | URESIRQ;
+       if (udc->is_selfpowered)
+               val |= NOVBUSIRQ;
+       spi_wr8(udc, MAX3420_REG_USBIEN, val);
+
+       /* Enable only EP0 interrupts */
+       val = IN0BAVIRQ | OUT0DAVIRQ | SUDAVIRQ;
+       spi_wr8(udc, MAX3420_REG_EPIEN, val);
+
+       /* Enable IRQ to CPU */
+       spi_wr8(udc, MAX3420_REG_CPUCTL, IE);
+}
+
+static int max3420_start(struct max3420_udc *udc)
+{
+       unsigned long flags;
+       int todo;
+
+       spin_lock_irqsave(&udc->lock, flags);
+       todo = udc->todo & UDC_START;
+       udc->todo &= ~UDC_START;
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (!todo)
+               return false;
+
+       if (udc->vbus_active && udc->softconnect)
+               __max3420_start(udc);
+       else
+               __max3420_stop(udc);
+
+       return true;
+}
+
+static irqreturn_t max3420_vbus_handler(int irq, void *dev_id)
+{
+       struct max3420_udc *udc = dev_id;
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+       /* its a vbus change interrupt */
+       udc->vbus_active = !udc->vbus_active;
+       udc->todo |= UDC_START;
+       usb_udc_vbus_handler(&udc->gadget, udc->vbus_active);
+       usb_gadget_set_state(&udc->gadget, udc->vbus_active
+                            ? USB_STATE_POWERED : USB_STATE_NOTATTACHED);
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (udc->thread_task &&
+           udc->thread_task->state != TASK_RUNNING)
+               wake_up_process(udc->thread_task);
+
+       return IRQ_HANDLED;
+}
+
+static irqreturn_t max3420_irq_handler(int irq, void *dev_id)
+{
+       struct max3420_udc *udc = dev_id;
+       struct spi_device *spi = udc->spi;
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+       if ((udc->todo & ENABLE_IRQ) == 0) {
+               disable_irq_nosync(spi->irq);
+               udc->todo |= ENABLE_IRQ;
+       }
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (udc->thread_task &&
+           udc->thread_task->state != TASK_RUNNING)
+               wake_up_process(udc->thread_task);
+
+       return IRQ_HANDLED;
+}
+
+static void max3420_getstatus(struct max3420_udc *udc)
+{
+       struct max3420_ep *ep;
+       u16 status = 0;
+
+       switch (udc->setup.bRequestType & USB_RECIP_MASK) {
+       case USB_RECIP_DEVICE:
+               /* Get device status */
+               status = udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED;
+               status |= (udc->remote_wkp << USB_DEVICE_REMOTE_WAKEUP);
+               break;
+       case USB_RECIP_INTERFACE:
+               if (udc->driver->setup(&udc->gadget, &udc->setup) < 0)
+                       goto stall;
+               break;
+       case USB_RECIP_ENDPOINT:
+               ep = &udc->ep[udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK];
+               if (udc->setup.wIndex & USB_DIR_IN) {
+                       if (!ep->ep_usb.caps.dir_in)
+                               goto stall;
+               } else {
+                       if (!ep->ep_usb.caps.dir_out)
+                               goto stall;
+               }
+               if (ep->halted)
+                       status = 1 << USB_ENDPOINT_HALT;
+               break;
+       default:
+               goto stall;
+       }
+
+       status = cpu_to_le16(status);
+       spi_wr_buf(udc, MAX3420_REG_EP0FIFO, &status, 2);
+       spi_wr8_ack(udc, MAX3420_REG_EP0BC, 2, 1);
+       return;
+stall:
+       dev_err(udc->dev, "Can't respond to getstatus request\n");
+       spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT);
+}
+
+static void max3420_set_clear_feature(struct max3420_udc *udc)
+{
+       struct max3420_ep *ep;
+       int set = udc->setup.bRequest == USB_REQ_SET_FEATURE;
+       unsigned long flags;
+       int id;
+
+       switch (udc->setup.bRequestType) {
+       case USB_RECIP_DEVICE:
+               if (udc->setup.wValue != USB_DEVICE_REMOTE_WAKEUP)
+                       break;
+
+               if (udc->setup.bRequest == USB_REQ_SET_FEATURE)
+                       udc->remote_wkp = 1;
+               else
+                       udc->remote_wkp = 0;
+
+               return spi_ack_ctrl(udc);
+
+       case USB_RECIP_ENDPOINT:
+               if (udc->setup.wValue != USB_ENDPOINT_HALT)
+                       break;
+
+               id = udc->setup.wIndex & USB_ENDPOINT_NUMBER_MASK;
+               ep = &udc->ep[id];
+
+               spin_lock_irqsave(&ep->lock, flags);
+               ep->todo &= ~STALL_EP;
+               if (set)
+                       ep->todo |= STALL;
+               else
+                       ep->todo |= UNSTALL;
+               spin_unlock_irqrestore(&ep->lock, flags);
+
+               spi_max3420_stall(ep);
+               return;
+       default:
+               break;
+       }
+
+       dev_err(udc->dev, "Can't respond to SET/CLEAR FEATURE\n");
+       spi_wr8(udc, MAX3420_REG_EPSTALLS, STLEP0IN | STLEP0OUT | STLSTAT);
+}
+
+static void max3420_handle_setup(struct max3420_udc *udc)
+{
+       struct usb_ctrlrequest setup;
+       u8 addr;
+
+       spi_rd_buf(udc, MAX3420_REG_SUDFIFO, (void *)&setup, 8);
+
+       udc->setup = setup;
+       udc->setup.wValue = cpu_to_le16(setup.wValue);
+       udc->setup.wIndex = cpu_to_le16(setup.wIndex);
+       udc->setup.wLength = cpu_to_le16(setup.wLength);
+
+       switch (udc->setup.bRequest) {
+       case USB_REQ_GET_STATUS:
+               /* Data+Status phase form udc */
+               if ((udc->setup.bRequestType &
+                               (USB_DIR_IN | USB_TYPE_MASK)) !=
+                               (USB_DIR_IN | USB_TYPE_STANDARD)) {
+                       break;
+               }
+               return max3420_getstatus(udc);
+       case USB_REQ_SET_ADDRESS:
+               /* Status phase from udc */
+               if (udc->setup.bRequestType != (USB_DIR_OUT |
+                               USB_TYPE_STANDARD | USB_RECIP_DEVICE)) {
+                       break;
+               }
+               addr = spi_rd8_ack(udc, MAX3420_REG_FNADDR, 1);
+               dev_dbg(udc->dev, "Assigned Address=%d\n", udc->setup.wValue);
+               return;
+       case USB_REQ_CLEAR_FEATURE:
+       case USB_REQ_SET_FEATURE:
+               /* Requests with no data phase, status phase from udc */
+               if ((udc->setup.bRequestType & USB_TYPE_MASK)
+                               != USB_TYPE_STANDARD)
+                       break;
+               return max3420_set_clear_feature(udc);
+       default:
+               break;
+       }
+
+       if (udc->driver->setup(&udc->gadget, &setup) < 0) {
+               /* Stall EP0 */
+               spi_wr8(udc, MAX3420_REG_EPSTALLS,
+                       STLEP0IN | STLEP0OUT | STLSTAT);
+       }
+}
+
+static void max3420_req_done(struct max3420_req *req, int status)
+{
+       struct max3420_ep *ep = req->ep;
+       struct max3420_udc *udc = ep->udc;
+
+       if (req->usb_req.status == -EINPROGRESS)
+               req->usb_req.status = status;
+       else
+               status = req->usb_req.status;
+
+       if (status && status != -ESHUTDOWN)
+               dev_err(udc->dev, "%s done %p, status %d\n",
+                       ep->ep_usb.name, req, status);
+
+       if (req->usb_req.complete)
+               req->usb_req.complete(&ep->ep_usb, &req->usb_req);
+}
+
+static int max3420_do_data(struct max3420_udc *udc, int ep_id, int in)
+{
+       struct max3420_ep *ep = &udc->ep[ep_id];
+       struct max3420_req *req;
+       int done, length, psz;
+       void *buf;
+
+       if (list_empty(&ep->queue))
+               return false;
+
+       req = list_first_entry(&ep->queue, struct max3420_req, queue);
+       buf = req->usb_req.buf + req->usb_req.actual;
+
+       psz = ep->ep_usb.maxpacket;
+       length = req->usb_req.length - req->usb_req.actual;
+       length = min(length, psz);
+
+       if (length == 0) {
+               done = 1;
+               goto xfer_done;
+       }
+
+       done = 0;
+       if (in) {
+               prefetch(buf);
+               spi_wr_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
+               spi_wr8(udc, MAX3420_REG_EP0BC + ep_id, length);
+               if (length < psz)
+                       done = 1;
+       } else {
+               psz = spi_rd8(udc, MAX3420_REG_EP0BC + ep_id);
+               length = min(length, psz);
+               prefetchw(buf);
+               spi_rd_buf(udc, MAX3420_REG_EP0FIFO + ep_id, buf, length);
+               if (length < ep->ep_usb.maxpacket)
+                       done = 1;
+       }
+
+       req->usb_req.actual += length;
+
+       if (req->usb_req.actual == req->usb_req.length)
+               done = 1;
+
+xfer_done:
+       if (done) {
+               unsigned long flags;
+
+               spin_lock_irqsave(&ep->lock, flags);
+               list_del_init(&req->queue);
+               spin_unlock_irqrestore(&ep->lock, flags);
+
+               if (ep_id == 0)
+                       spi_ack_ctrl(udc);
+
+               max3420_req_done(req, 0);
+       }
+
+       return true;
+}
+
+static int max3420_handle_irqs(struct max3420_udc *udc)
+{
+       u8 epien, epirq, usbirq, usbien, reg[4];
+       bool ret = false;
+
+       spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 4);
+       epirq = reg[0];
+       epien = reg[1];
+       usbirq = reg[2];
+       usbien = reg[3];
+
+       usbirq &= usbien;
+       epirq &= epien;
+
+       if (epirq & SUDAVIRQ) {
+               spi_wr8(udc, MAX3420_REG_EPIRQ, SUDAVIRQ);
+               max3420_handle_setup(udc);
+               return true;
+       }
+
+       if (usbirq & VBUSIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, VBUSIRQ);
+               dev_dbg(udc->dev, "Cable plugged in\n");
+               return true;
+       }
+
+       if (usbirq & NOVBUSIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, NOVBUSIRQ);
+               dev_dbg(udc->dev, "Cable pulled out\n");
+               return true;
+       }
+
+       if (usbirq & URESIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, URESIRQ);
+               dev_dbg(udc->dev, "USB Reset - Start\n");
+               return true;
+       }
+
+       if (usbirq & URESDNIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, URESDNIRQ);
+               dev_dbg(udc->dev, "USB Reset - END\n");
+               spi_wr8(udc, MAX3420_REG_USBIEN, URESDNIRQ | URESIRQ);
+               spi_wr8(udc, MAX3420_REG_EPIEN, SUDAVIRQ | IN0BAVIRQ
+                       | OUT0DAVIRQ);
+               return true;
+       }
+
+       if (usbirq & SUSPIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, SUSPIRQ);
+               dev_dbg(udc->dev, "USB Suspend - Enter\n");
+               udc->suspended = true;
+               return true;
+       }
+
+       if (usbirq & BUSACTIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, BUSACTIRQ);
+               dev_dbg(udc->dev, "USB Suspend - Exit\n");
+               udc->suspended = false;
+               return true;
+       }
+
+       if (usbirq & RWUDNIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, RWUDNIRQ);
+               dev_dbg(udc->dev, "Asked Host to wakeup\n");
+               return true;
+       }
+
+       if (usbirq & OSCOKIRQ) {
+               spi_wr8(udc, MAX3420_REG_USBIRQ, OSCOKIRQ);
+               dev_dbg(udc->dev, "Osc stabilized, start work\n");
+               return true;
+       }
+
+       if (epirq & OUT0DAVIRQ && max3420_do_data(udc, 0, 0)) {
+               spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT0DAVIRQ, 1);
+               ret = true;
+       }
+
+       if (epirq & IN0BAVIRQ && max3420_do_data(udc, 0, 1))
+               ret = true;
+
+       if (epirq & OUT1DAVIRQ && max3420_do_data(udc, 1, 0)) {
+               spi_wr8_ack(udc, MAX3420_REG_EPIRQ, OUT1DAVIRQ, 1);
+               ret = true;
+       }
+
+       if (epirq & IN2BAVIRQ && max3420_do_data(udc, 2, 1))
+               ret = true;
+
+       if (epirq & IN3BAVIRQ && max3420_do_data(udc, 3, 1))
+               ret = true;
+
+       return ret;
+}
+
+static int max3420_thread(void *dev_id)
+{
+       struct max3420_udc *udc = dev_id;
+       struct spi_device *spi = udc->spi;
+       int i, loop_again = 1;
+       unsigned long flags;
+
+       while (!kthread_should_stop()) {
+               if (!loop_again) {
+                       ktime_t kt = ns_to_ktime(1000 * 1000 * 250); /* 250ms */
+
+                       set_current_state(TASK_INTERRUPTIBLE);
+
+                       spin_lock_irqsave(&udc->lock, flags);
+                       if (udc->todo & ENABLE_IRQ) {
+                               enable_irq(spi->irq);
+                               udc->todo &= ~ENABLE_IRQ;
+                       }
+                       spin_unlock_irqrestore(&udc->lock, flags);
+
+                       schedule_hrtimeout(&kt, HRTIMER_MODE_REL);
+               }
+               loop_again = 0;
+
+               mutex_lock(&udc->spi_bus_mutex);
+
+               /* If bus-vbus_active and disconnected */
+               if (!udc->vbus_active || !udc->softconnect)
+                       goto loop;
+
+               if (max3420_start(udc)) {
+                       loop_again = 1;
+                       goto loop;
+               }
+
+               if (max3420_handle_irqs(udc)) {
+                       loop_again = 1;
+                       goto loop;
+               }
+
+               if (spi_max3420_rwkup(udc)) {
+                       loop_again = 1;
+                       goto loop;
+               }
+
+               max3420_do_data(udc, 0, 1); /* get done with the EP0 ZLP */
+
+               for (i = 1; i < MAX3420_MAX_EPS; i++) {
+                       struct max3420_ep *ep = &udc->ep[i];
+
+                       if (spi_max3420_enable(ep))
+                               loop_again = 1;
+                       if (spi_max3420_stall(ep))
+                               loop_again = 1;
+               }
+loop:
+               mutex_unlock(&udc->spi_bus_mutex);
+       }
+
+       set_current_state(TASK_RUNNING);
+       dev_info(udc->dev, "SPI thread exiting");
+       return 0;
+}
+
+static int max3420_ep_set_halt(struct usb_ep *_ep, int stall)
+{
+       struct max3420_ep *ep = to_max3420_ep(_ep);
+       struct max3420_udc *udc = ep->udc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+
+       ep->todo &= ~STALL_EP;
+       if (stall)
+               ep->todo |= STALL;
+       else
+               ep->todo |= UNSTALL;
+
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       wake_up_process(udc->thread_task);
+
+       dev_dbg(udc->dev, "%sStall %s\n", stall ? "" : "Un", ep->name);
+       return 0;
+}
+
+static int __max3420_ep_enable(struct max3420_ep *ep,
+                              const struct usb_endpoint_descriptor *desc)
+{
+       unsigned int maxp = usb_endpoint_maxp(desc);
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       ep->ep_usb.desc = desc;
+       ep->ep_usb.maxpacket = maxp;
+
+       ep->todo &= ~ENABLE_EP;
+       ep->todo |= ENABLE;
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       return 0;
+}
+
+static int max3420_ep_enable(struct usb_ep *_ep,
+                            const struct usb_endpoint_descriptor *desc)
+{
+       struct max3420_ep *ep = to_max3420_ep(_ep);
+       struct max3420_udc *udc = ep->udc;
+
+       __max3420_ep_enable(ep, desc);
+
+       wake_up_process(udc->thread_task);
+
+       return 0;
+}
+
+static void max3420_nuke(struct max3420_ep *ep, int status)
+{
+       struct max3420_req *req, *r;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+
+       list_for_each_entry_safe(req, r, &ep->queue, queue) {
+               list_del_init(&req->queue);
+
+               spin_unlock_irqrestore(&ep->lock, flags);
+               max3420_req_done(req, status);
+               spin_lock_irqsave(&ep->lock, flags);
+       }
+
+       spin_unlock_irqrestore(&ep->lock, flags);
+}
+
+static void __max3420_ep_disable(struct max3420_ep *ep)
+{
+       struct max3420_udc *udc = ep->udc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+
+       ep->ep_usb.desc = NULL;
+
+       ep->todo &= ~ENABLE_EP;
+       ep->todo |= DISABLE;
+
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       dev_dbg(udc->dev, "Disabled %s\n", ep->name);
+}
+
+static int max3420_ep_disable(struct usb_ep *_ep)
+{
+       struct max3420_ep *ep = to_max3420_ep(_ep);
+       struct max3420_udc *udc = ep->udc;
+
+       max3420_nuke(ep, -ESHUTDOWN);
+
+       __max3420_ep_disable(ep);
+
+       wake_up_process(udc->thread_task);
+
+       return 0;
+}
+
+static struct usb_request *max3420_alloc_request(struct usb_ep *_ep,
+                                                gfp_t gfp_flags)
+{
+       struct max3420_ep *ep = to_max3420_ep(_ep);
+       struct max3420_req *req;
+
+       req = kzalloc(sizeof(*req), gfp_flags);
+       if (!req)
+               return NULL;
+
+       req->ep = ep;
+
+       return &req->usb_req;
+}
+
+static void max3420_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+       kfree(to_max3420_req(_req));
+}
+
+static int max3420_ep_queue(struct usb_ep *_ep, struct usb_request *_req,
+                           gfp_t ignored)
+{
+       struct max3420_req *req = to_max3420_req(_req);
+       struct max3420_ep *ep  = to_max3420_ep(_ep);
+       struct max3420_udc *udc = ep->udc;
+       unsigned long flags;
+
+       _req->status = -EINPROGRESS;
+       _req->actual = 0;
+
+       spin_lock_irqsave(&ep->lock, flags);
+       list_add_tail(&req->queue, &ep->queue);
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       wake_up_process(udc->thread_task);
+       return 0;
+}
+
+static int max3420_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct max3420_req *t, *req = to_max3420_req(_req);
+       struct max3420_ep *ep = to_max3420_ep(_ep);
+       unsigned long flags;
+
+       spin_lock_irqsave(&ep->lock, flags);
+
+       /* Pluck the descriptor from queue */
+       list_for_each_entry(t, &ep->queue, queue)
+               if (t == req) {
+                       list_del_init(&req->queue);
+                       break;
+               }
+
+       spin_unlock_irqrestore(&ep->lock, flags);
+
+       if (t == req)
+               max3420_req_done(req, -ECONNRESET);
+
+       return 0;
+}
+
+static const struct usb_ep_ops max3420_ep_ops = {
+       .enable         = max3420_ep_enable,
+       .disable        = max3420_ep_disable,
+       .alloc_request  = max3420_alloc_request,
+       .free_request   = max3420_free_request,
+       .queue          = max3420_ep_queue,
+       .dequeue        = max3420_ep_dequeue,
+       .set_halt       = max3420_ep_set_halt,
+};
+
+static int max3420_wakeup(struct usb_gadget *gadget)
+{
+       struct max3420_udc *udc = to_udc(gadget);
+       unsigned long flags;
+       int ret = -EINVAL;
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       /* Only if wakeup allowed by host */
+       if (udc->remote_wkp) {
+               udc->todo |= REMOTE_WAKEUP;
+               ret = 0;
+       }
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (udc->thread_task &&
+           udc->thread_task->state != TASK_RUNNING)
+               wake_up_process(udc->thread_task);
+       return ret;
+}
+
+static int max3420_udc_start(struct usb_gadget *gadget,
+                            struct usb_gadget_driver *driver)
+{
+       struct max3420_udc *udc = to_udc(gadget);
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+       /* hook up the driver */
+       driver->driver.bus = NULL;
+       udc->driver = driver;
+       udc->gadget.speed = USB_SPEED_FULL;
+
+       udc->gadget.is_selfpowered = udc->is_selfpowered;
+       udc->remote_wkp = 0;
+       udc->softconnect = true;
+       udc->todo |= UDC_START;
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (udc->thread_task &&
+           udc->thread_task->state != TASK_RUNNING)
+               wake_up_process(udc->thread_task);
+
+       return 0;
+}
+
+static int max3420_udc_stop(struct usb_gadget *gadget)
+{
+       struct max3420_udc *udc = to_udc(gadget);
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+       udc->is_selfpowered = udc->gadget.is_selfpowered;
+       udc->gadget.speed = USB_SPEED_UNKNOWN;
+       udc->driver = NULL;
+       udc->softconnect = false;
+       udc->todo |= UDC_START;
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (udc->thread_task &&
+           udc->thread_task->state != TASK_RUNNING)
+               wake_up_process(udc->thread_task);
+
+       return 0;
+}
+
+static const struct usb_gadget_ops max3420_udc_ops = {
+       .udc_start      = max3420_udc_start,
+       .udc_stop       = max3420_udc_stop,
+       .wakeup         = max3420_wakeup,
+};
+
+static void max3420_eps_init(struct max3420_udc *udc)
+{
+       int idx;
+
+       INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+       for (idx = 0; idx < MAX3420_MAX_EPS; idx++) {
+               struct max3420_ep *ep = &udc->ep[idx];
+
+               spin_lock_init(&ep->lock);
+               INIT_LIST_HEAD(&ep->queue);
+
+               ep->udc = udc;
+               ep->id = idx;
+               ep->halted = 0;
+               ep->maxpacket = 0;
+               ep->ep_usb.name = ep->name;
+               ep->ep_usb.ops = &max3420_ep_ops;
+               usb_ep_set_maxpacket_limit(&ep->ep_usb, MAX3420_EP_MAX_PACKET);
+
+               if (idx == 0) { /* For EP0 */
+                       ep->ep_usb.desc = &ep0_desc;
+                       ep->ep_usb.maxpacket = usb_endpoint_maxp(&ep0_desc);
+                       ep->ep_usb.caps.type_control = true;
+                       ep->ep_usb.caps.dir_in = true;
+                       ep->ep_usb.caps.dir_out = true;
+                       snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep0");
+                       continue;
+               }
+
+               if (idx == 1) { /* EP1 is OUT */
+                       ep->ep_usb.caps.dir_in = false;
+                       ep->ep_usb.caps.dir_out = true;
+                       snprintf(ep->name, MAX3420_EPNAME_SIZE, "ep1-bulk-out");
+               } else { /* EP2 & EP3 are IN */
+                       ep->ep_usb.caps.dir_in = true;
+                       ep->ep_usb.caps.dir_out = false;
+                       snprintf(ep->name, MAX3420_EPNAME_SIZE,
+                                "ep%d-bulk-in", idx);
+               }
+               ep->ep_usb.caps.type_iso = false;
+               ep->ep_usb.caps.type_int = false;
+               ep->ep_usb.caps.type_bulk = true;
+
+               list_add_tail(&ep->ep_usb.ep_list,
+                             &udc->gadget.ep_list);
+       }
+}
+
+static int max3420_probe(struct spi_device *spi)
+{
+       struct max3420_udc *udc;
+       int err, irq;
+       u8 reg[8];
+
+       if (spi->master->flags & SPI_MASTER_HALF_DUPLEX) {
+               dev_err(&spi->dev, "UDC needs full duplex to work\n");
+               return -EINVAL;
+       }
+
+       spi->mode = SPI_MODE_3;
+       spi->bits_per_word = 8;
+
+       err = spi_setup(spi);
+       if (err) {
+               dev_err(&spi->dev, "Unable to setup SPI bus\n");
+               return -EFAULT;
+       }
+
+       udc = devm_kzalloc(&spi->dev, sizeof(*udc), GFP_KERNEL);
+       if (!udc)
+               return -ENOMEM;
+
+       udc->spi = spi;
+
+       udc->remote_wkp = 0;
+
+       /* Setup gadget structure */
+       udc->gadget.ops = &max3420_udc_ops;
+       udc->gadget.max_speed = USB_SPEED_FULL;
+       udc->gadget.speed = USB_SPEED_UNKNOWN;
+       udc->gadget.ep0 = &udc->ep[0].ep_usb;
+       udc->gadget.name = driver_name;
+
+       spin_lock_init(&udc->lock);
+       mutex_init(&udc->spi_bus_mutex);
+
+       udc->ep0req.ep = &udc->ep[0];
+       udc->ep0req.usb_req.buf = udc->ep0buf;
+       INIT_LIST_HEAD(&udc->ep0req.queue);
+
+       /* setup Endpoints */
+       max3420_eps_init(udc);
+
+       /* configure SPI */
+       spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8);
+       spi_wr8(udc, MAX3420_REG_PINCTL, FDUPSPI);
+
+       err = usb_add_gadget_udc(&spi->dev, &udc->gadget);
+       if (err)
+               return err;
+
+       udc->dev = &udc->gadget.dev;
+
+       spi_set_drvdata(spi, udc);
+
+       irq = of_irq_get_byname(spi->dev.of_node, "udc");
+       err = devm_request_irq(&spi->dev, irq, max3420_irq_handler, 0,
+                              "max3420", udc);
+       if (err < 0)
+               return err;
+
+       udc->thread_task = kthread_create(max3420_thread, udc,
+                                         "max3420-thread");
+       if (IS_ERR(udc->thread_task))
+               return PTR_ERR(udc->thread_task);
+
+       irq = of_irq_get_byname(spi->dev.of_node, "vbus");
+       if (irq <= 0) { /* no vbus irq implies self-powered design */
+               udc->is_selfpowered = 1;
+               udc->vbus_active = true;
+               udc->todo |= UDC_START;
+               usb_udc_vbus_handler(&udc->gadget, udc->vbus_active);
+               usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED);
+               max3420_start(udc);
+       } else {
+               udc->is_selfpowered = 0;
+               /* Detect current vbus status */
+               spi_rd_buf(udc, MAX3420_REG_EPIRQ, reg, 8);
+               if (reg[7] != 0xff)
+                       udc->vbus_active = true;
+
+               err = devm_request_irq(&spi->dev, irq,
+                                      max3420_vbus_handler, 0, "vbus", udc);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int max3420_remove(struct spi_device *spi)
+{
+       struct max3420_udc *udc = spi_get_drvdata(spi);
+       unsigned long flags;
+
+       usb_del_gadget_udc(&udc->gadget);
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       kthread_stop(udc->thread_task);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return 0;
+}
+
+static const struct of_device_id max3420_udc_of_match[] = {
+       { .compatible = "maxim,max3420-udc"},
+       { .compatible = "maxim,max3421-udc"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, max3420_udc_of_match);
+
+static struct spi_driver max3420_driver = {
+       .driver = {
+               .name = "max3420-udc",
+               .of_match_table = of_match_ptr(max3420_udc_of_match),
+       },
+       .probe = max3420_probe,
+       .remove = max3420_remove,
+};
+
+module_spi_driver(max3420_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Jassi Brar <jaswinder.singh@linaro.org>");
+MODULE_LICENSE("GPL");
index 1fd1b9186e46eab97be02e73b75be42b0da65cdf..5eff85eeaa5a0969f2bb6533636267d16834e241 100644 (file)
@@ -2861,6 +2861,8 @@ static void ep_clear_seqnum(struct net2280_ep *ep)
 static void handle_stat0_irqs_superspeed(struct net2280 *dev,
                struct net2280_ep *ep, struct usb_ctrlrequest r)
 {
+       struct net2280_ep *e;
+       u16 status;
        int tmp = 0;
 
 #define        w_value         le16_to_cpu(r.wValue)
@@ -2868,9 +2870,6 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
 #define        w_length        le16_to_cpu(r.wLength)
 
        switch (r.bRequest) {
-               struct net2280_ep *e;
-               u16 status;
-
        case USB_REQ_SET_CONFIGURATION:
                dev->addressed_state = !w_value;
                goto usb3_delegate;
@@ -3857,7 +3856,7 @@ MODULE_DEVICE_TABLE(pci, pci_ids);
 
 /* pci driver glue; this is a "new style" PCI driver module */
 static struct pci_driver net2280_pci_driver = {
-       .name =         (char *) driver_name,
+       .name =         driver_name,
        .id_table =     pci_ids,
 
        .probe =        net2280_probe,
index bd12417996db300e0a89a4a89ad867e913dbe1b1..bf87c6c0d7f6a3ce115349abd6556727c1251f67 100644 (file)
@@ -3001,7 +3001,7 @@ static struct platform_driver udc_driver = {
        .suspend        = omap_udc_suspend,
        .resume         = omap_udc_resume,
        .driver         = {
-               .name   = (char *) driver_name,
+               .name   = driver_name,
        },
 };
 
index 582a16165ea9ce354762e66f7a2b7f53760c47bb..537094b485bf4f1d095ad4d12781fc1747298e69 100644 (file)
@@ -1968,7 +1968,7 @@ clean_up2:
 static struct platform_driver r8a66597_driver = {
        .remove =       r8a66597_remove,
        .driver         = {
-               .name = (char *) udc_name,
+               .name = udc_name,
        },
 };
 
index c5c3c14df67acdd97903a2706c3b7777982fa7c8..0c418ce50ba0f41a804d6b9000d0bcf9f3caaaea 100644 (file)
@@ -2355,14 +2355,14 @@ static const struct usb_gadget_ops renesas_usb3_gadget_ops = {
        .set_selfpowered        = renesas_usb3_set_selfpowered,
 };
 
-static enum usb_role renesas_usb3_role_switch_get(struct device *dev)
+static enum usb_role renesas_usb3_role_switch_get(struct usb_role_switch *sw)
 {
-       struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+       struct renesas_usb3 *usb3 = usb_role_switch_get_drvdata(sw);
        enum usb_role cur_role;
 
-       pm_runtime_get_sync(dev);
+       pm_runtime_get_sync(usb3_to_dev(usb3));
        cur_role = usb3_is_host(usb3) ? USB_ROLE_HOST : USB_ROLE_DEVICE;
-       pm_runtime_put(dev);
+       pm_runtime_put(usb3_to_dev(usb3));
 
        return cur_role;
 }
@@ -2372,7 +2372,7 @@ static void handle_ext_role_switch_states(struct device *dev,
 {
        struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
        struct device *host = usb3->host_dev;
-       enum usb_role cur_role = renesas_usb3_role_switch_get(dev);
+       enum usb_role cur_role = renesas_usb3_role_switch_get(usb3->role_sw);
 
        switch (role) {
        case USB_ROLE_NONE:
@@ -2424,7 +2424,7 @@ static void handle_role_switch_states(struct device *dev,
 {
        struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
        struct device *host = usb3->host_dev;
-       enum usb_role cur_role = renesas_usb3_role_switch_get(dev);
+       enum usb_role cur_role = renesas_usb3_role_switch_get(usb3->role_sw);
 
        if (cur_role == USB_ROLE_HOST && role == USB_ROLE_DEVICE) {
                device_release_driver(host);
@@ -2438,19 +2438,19 @@ static void handle_role_switch_states(struct device *dev,
        }
 }
 
-static int renesas_usb3_role_switch_set(struct device *dev,
+static int renesas_usb3_role_switch_set(struct usb_role_switch *sw,
                                        enum usb_role role)
 {
-       struct renesas_usb3 *usb3 = dev_get_drvdata(dev);
+       struct renesas_usb3 *usb3 = usb_role_switch_get_drvdata(sw);
 
-       pm_runtime_get_sync(dev);
+       pm_runtime_get_sync(usb3_to_dev(usb3));
 
        if (usb3->role_sw_by_connector)
-               handle_ext_role_switch_states(dev, role);
+               handle_ext_role_switch_states(usb3_to_dev(usb3), role);
        else
-               handle_role_switch_states(dev, role);
+               handle_role_switch_states(usb3_to_dev(usb3), role);
 
-       pm_runtime_put(dev);
+       pm_runtime_put(usb3_to_dev(usb3));
 
        return 0;
 }
@@ -2831,6 +2831,8 @@ static int renesas_usb3_probe(struct platform_device *pdev)
                renesas_usb3_role_switch_desc.fwnode = dev_fwnode(&pdev->dev);
        }
 
+       renesas_usb3_role_switch_desc.driver_data = usb3;
+
        INIT_WORK(&usb3->role_work, renesas_usb3_role_work);
        usb3->role_sw = usb_role_switch_register(&pdev->dev,
                                        &renesas_usb3_role_switch_desc);
@@ -2906,7 +2908,7 @@ static struct platform_driver renesas_usb3_driver = {
        .probe          = renesas_usb3_probe,
        .remove         = renesas_usb3_remove,
        .driver         = {
-               .name = (char *)udc_name,
+               .name = udc_name,
                .pm             = &renesas_usb3_pm_ops,
                .of_match_table = of_match_ptr(usb3_of_match),
        },
index 21252fbc0319de648bf423b157c5860db5707c30..aaca1b0a2f59b93c5544deec04160b208e052f28 100644 (file)
@@ -1285,7 +1285,8 @@ static int s3c_hsudc_probe(struct platform_device *pdev)
        ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(hsudc->supplies),
                                 hsudc->supplies);
        if (ret != 0) {
-               dev_err(dev, "failed to request supplies: %d\n", ret);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "failed to request supplies: %d\n", ret);
                goto err_supplies;
        }
 
index 634c2c19a176a10ac21065168995ce378cbcc34c..52a6add961f443fef3fdf49e4e8aa8dd0d8049b9 100644 (file)
@@ -26,7 +26,9 @@
 #include <linux/reset.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
 #include <linux/usb/role.h>
+#include <linux/usb/phy.h>
 #include <linux/workqueue.h>
 
 /* XUSB_DEV registers */
@@ -477,17 +479,21 @@ struct tegra_xudc {
 
        struct clk_bulk_data *clks;
 
-       enum usb_role device_mode;
-       struct usb_role_switch *usb_role_sw;
+       bool device_mode;
        struct work_struct usb_role_sw_work;
 
-       struct phy *usb3_phy;
-       struct phy *utmi_phy;
+       struct phy **usb3_phy;
+       struct phy *curr_usb3_phy;
+       struct phy **utmi_phy;
+       struct phy *curr_utmi_phy;
 
        struct tegra_xudc_save_regs saved_regs;
        bool suspended;
        bool powergated;
 
+       struct usb_phy **usbphy;
+       struct notifier_block vbus_nb;
+
        struct completion disconnect_complete;
 
        bool selfpowered;
@@ -517,6 +523,7 @@ struct tegra_xudc_soc {
        unsigned int num_supplies;
        const char * const *clock_names;
        unsigned int num_clks;
+       unsigned int num_phys;
        bool u1_enable;
        bool u2_enable;
        bool lpm_enable;
@@ -598,19 +605,18 @@ static void tegra_xudc_device_mode_on(struct tegra_xudc *xudc)
 
        pm_runtime_get_sync(xudc->dev);
 
-       err = phy_power_on(xudc->utmi_phy);
+       err = phy_power_on(xudc->curr_utmi_phy);
        if (err < 0)
                dev_err(xudc->dev, "utmi power on failed %d\n", err);
 
-       err = phy_power_on(xudc->usb3_phy);
+       err = phy_power_on(xudc->curr_usb3_phy);
        if (err < 0)
                dev_err(xudc->dev, "usb3 phy power on failed %d\n", err);
 
        dev_dbg(xudc->dev, "device mode on\n");
 
-       tegra_xusb_padctl_set_vbus_override(xudc->padctl, true);
-
-       xudc->device_mode = USB_ROLE_DEVICE;
+       phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
+                        USB_ROLE_DEVICE);
 }
 
 static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
@@ -625,7 +631,7 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
 
        reinit_completion(&xudc->disconnect_complete);
 
-       tegra_xusb_padctl_set_vbus_override(xudc->padctl, false);
+       phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
 
        pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
                PORTSC_PLS_SHIFT;
@@ -643,8 +649,6 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
                xudc_writel(xudc, val, PORTSC);
        }
 
-       xudc->device_mode = USB_ROLE_NONE;
-
        /* Wait for disconnect event. */
        if (connected)
                wait_for_completion(&xudc->disconnect_complete);
@@ -652,11 +656,11 @@ static void tegra_xudc_device_mode_off(struct tegra_xudc *xudc)
        /* Make sure interrupt handler has completed before powergating. */
        synchronize_irq(xudc->irq);
 
-       err = phy_power_off(xudc->utmi_phy);
+       err = phy_power_off(xudc->curr_utmi_phy);
        if (err < 0)
                dev_err(xudc->dev, "utmi_phy power off failed %d\n", err);
 
-       err = phy_power_off(xudc->usb3_phy);
+       err = phy_power_off(xudc->curr_usb3_phy);
        if (err < 0)
                dev_err(xudc->dev, "usb3_phy power off failed %d\n", err);
 
@@ -668,29 +672,57 @@ static void tegra_xudc_usb_role_sw_work(struct work_struct *work)
        struct tegra_xudc *xudc = container_of(work, struct tegra_xudc,
                                               usb_role_sw_work);
 
-       if (!xudc->usb_role_sw ||
-               usb_role_switch_get_role(xudc->usb_role_sw) == USB_ROLE_DEVICE)
+       if (xudc->device_mode)
                tegra_xudc_device_mode_on(xudc);
        else
                tegra_xudc_device_mode_off(xudc);
+}
+
+static int tegra_xudc_get_phy_index(struct tegra_xudc *xudc,
+                                             struct usb_phy *usbphy)
+{
+       unsigned int i;
 
+       for (i = 0; i < xudc->soc->num_phys; i++) {
+               if (xudc->usbphy[i] && usbphy == xudc->usbphy[i])
+                       return i;
+       }
+
+       dev_info(xudc->dev, "phy index could not be found for shared USB PHY");
+       return -1;
 }
 
-static int tegra_xudc_usb_role_sw_set(struct device *dev, enum usb_role role)
+static int tegra_xudc_vbus_notify(struct notifier_block *nb,
+                                        unsigned long action, void *data)
 {
-       struct tegra_xudc *xudc = dev_get_drvdata(dev);
-       unsigned long flags;
+       struct tegra_xudc *xudc = container_of(nb, struct tegra_xudc,
+                                              vbus_nb);
+       struct usb_phy *usbphy = (struct usb_phy *)data;
+       int phy_index;
 
-       dev_dbg(dev, "%s role is %d\n", __func__, role);
+       dev_dbg(xudc->dev, "%s(): event is %d\n", __func__, usbphy->last_event);
 
-       spin_lock_irqsave(&xudc->lock, flags);
+       if ((xudc->device_mode && usbphy->last_event == USB_EVENT_VBUS) ||
+           (!xudc->device_mode && usbphy->last_event != USB_EVENT_VBUS)) {
+               dev_dbg(xudc->dev, "Same role(%d) received. Ignore",
+                       xudc->device_mode);
+               return NOTIFY_OK;
+       }
 
-       if (!xudc->suspended)
-               schedule_work(&xudc->usb_role_sw_work);
+       xudc->device_mode = (usbphy->last_event == USB_EVENT_VBUS) ? true :
+                                                                    false;
 
-       spin_unlock_irqrestore(&xudc->lock, flags);
+       phy_index = tegra_xudc_get_phy_index(xudc, usbphy);
+       dev_dbg(xudc->dev, "%s(): current phy index is %d\n", __func__,
+               phy_index);
 
-       return 0;
+       if (!xudc->suspended && phy_index != -1) {
+               xudc->curr_utmi_phy = xudc->utmi_phy[phy_index];
+               xudc->curr_usb3_phy = xudc->usb3_phy[phy_index];
+               schedule_work(&xudc->usb_role_sw_work);
+       }
+
+       return NOTIFY_OK;
 }
 
 static void tegra_xudc_plc_reset_work(struct work_struct *work)
@@ -708,9 +740,11 @@ static void tegra_xudc_plc_reset_work(struct work_struct *work)
 
                if (pls == PORTSC_PLS_INACTIVE) {
                        dev_info(xudc->dev, "PLS = Inactive. Toggle VBUS\n");
-                       tegra_xusb_padctl_set_vbus_override(xudc->padctl,
-                                                             false);
-                       tegra_xusb_padctl_set_vbus_override(xudc->padctl, true);
+                       phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
+                                        USB_ROLE_NONE);
+                       phy_set_mode_ext(xudc->curr_utmi_phy, PHY_MODE_USB_OTG,
+                                        USB_ROLE_DEVICE);
+
                        xudc->wait_csc = false;
                }
        }
@@ -729,8 +763,7 @@ static void tegra_xudc_port_reset_war_work(struct work_struct *work)
 
        spin_lock_irqsave(&xudc->lock, flags);
 
-       if ((xudc->device_mode == USB_ROLE_DEVICE)
-                             && xudc->wait_for_sec_prc) {
+       if (xudc->device_mode && xudc->wait_for_sec_prc) {
                pls = (xudc_readl(xudc, PORTSC) & PORTSC_PLS_MASK) >>
                        PORTSC_PLS_SHIFT;
                dev_dbg(xudc->dev, "pls = %x\n", pls);
@@ -738,7 +771,8 @@ static void tegra_xudc_port_reset_war_work(struct work_struct *work)
                if (pls == PORTSC_PLS_DISABLED) {
                        dev_dbg(xudc->dev, "toggle vbus\n");
                        /* PRC doesn't complete in 100ms, toggle the vbus */
-                       ret = tegra_phy_xusb_utmi_port_reset(xudc->utmi_phy);
+                       ret = tegra_phy_xusb_utmi_port_reset(
+                               xudc->curr_utmi_phy);
                        if (ret == 1)
                                xudc->wait_for_sec_prc = 0;
                }
@@ -1927,6 +1961,7 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
        unsigned long flags;
        u32 val;
        int ret;
+       unsigned int i;
 
        if (!driver)
                return -EINVAL;
@@ -1962,6 +1997,10 @@ static int tegra_xudc_gadget_start(struct usb_gadget *gadget,
                xudc_writel(xudc, val, CTRL);
        }
 
+       for (i = 0; i < xudc->soc->num_phys; i++)
+               if (xudc->usbphy[i])
+                       otg_set_peripheral(xudc->usbphy[i]->otg, gadget);
+
        xudc->driver = driver;
 unlock:
        dev_dbg(xudc->dev, "%s: ret value is %d", __func__, ret);
@@ -1977,11 +2016,16 @@ static int tegra_xudc_gadget_stop(struct usb_gadget *gadget)
        struct tegra_xudc *xudc = to_xudc(gadget);
        unsigned long flags;
        u32 val;
+       unsigned int i;
 
        pm_runtime_get_sync(xudc->dev);
 
        spin_lock_irqsave(&xudc->lock, flags);
 
+       for (i = 0; i < xudc->soc->num_phys; i++)
+               if (xudc->usbphy[i])
+                       otg_set_peripheral(xudc->usbphy[i]->otg, NULL);
+
        val = xudc_readl(xudc, CTRL);
        val &= ~(CTRL_IE | CTRL_ENABLE);
        xudc_writel(xudc, val, CTRL);
@@ -3314,33 +3358,120 @@ static void tegra_xudc_device_params_init(struct tegra_xudc *xudc)
        xudc_writel(xudc, val, CFG_DEV_SSPI_XFER);
 }
 
-static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
+static int tegra_xudc_phy_get(struct tegra_xudc *xudc)
 {
-       int err;
+       int err = 0, usb3;
+       unsigned int i;
 
-       err = phy_init(xudc->utmi_phy);
-       if (err < 0) {
-               dev_err(xudc->dev, "utmi phy init failed: %d\n", err);
-               return err;
-       }
+       xudc->utmi_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
+                                          sizeof(*xudc->utmi_phy), GFP_KERNEL);
+       if (!xudc->utmi_phy)
+               return -ENOMEM;
 
-       err = phy_init(xudc->usb3_phy);
-       if (err < 0) {
-               dev_err(xudc->dev, "usb3 phy init failed: %d\n", err);
-               goto exit_utmi_phy;
+       xudc->usb3_phy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
+                                          sizeof(*xudc->usb3_phy), GFP_KERNEL);
+       if (!xudc->usb3_phy)
+               return -ENOMEM;
+
+       xudc->usbphy = devm_kcalloc(xudc->dev, xudc->soc->num_phys,
+                                          sizeof(*xudc->usbphy), GFP_KERNEL);
+       if (!xudc->usbphy)
+               return -ENOMEM;
+
+       xudc->vbus_nb.notifier_call = tegra_xudc_vbus_notify;
+
+       for (i = 0; i < xudc->soc->num_phys; i++) {
+               char phy_name[] = "usb.-.";
+
+               /* Get USB2 phy */
+               snprintf(phy_name, sizeof(phy_name), "usb2-%d", i);
+               xudc->utmi_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
+               if (IS_ERR(xudc->utmi_phy[i])) {
+                       err = PTR_ERR(xudc->utmi_phy[i]);
+                       if (err != -EPROBE_DEFER)
+                               dev_err(xudc->dev, "failed to get usb2-%d phy: %d\n",
+                                       i, err);
+
+                       goto clean_up;
+               } else if (xudc->utmi_phy[i]) {
+                       /* Get usb-phy, if utmi phy is available */
+                       xudc->usbphy[i] = devm_usb_get_phy_by_node(xudc->dev,
+                                               xudc->utmi_phy[i]->dev.of_node,
+                                               &xudc->vbus_nb);
+                       if (IS_ERR(xudc->usbphy[i])) {
+                               err = PTR_ERR(xudc->usbphy[i]);
+                               dev_err(xudc->dev, "failed to get usbphy-%d: %d\n",
+                                       i, err);
+                               goto clean_up;
+                       }
+               } else if (!xudc->utmi_phy[i]) {
+                       /* if utmi phy is not available, ignore USB3 phy get */
+                       continue;
+               }
+
+               /* Get USB3 phy */
+               usb3 = tegra_xusb_padctl_get_usb3_companion(xudc->padctl, i);
+               if (usb3 < 0)
+                       continue;
+
+               snprintf(phy_name, sizeof(phy_name), "usb3-%d", usb3);
+               xudc->usb3_phy[i] = devm_phy_optional_get(xudc->dev, phy_name);
+               if (IS_ERR(xudc->usb3_phy[i])) {
+                       err = PTR_ERR(xudc->usb3_phy[i]);
+                       if (err != -EPROBE_DEFER)
+                               dev_err(xudc->dev, "failed to get usb3-%d phy: %d\n",
+                                       usb3, err);
+
+                       goto clean_up;
+               } else if (xudc->usb3_phy[i])
+                       dev_dbg(xudc->dev, "usb3_phy-%d registered", usb3);
        }
 
-       return 0;
+       return err;
+
+clean_up:
+       for (i = 0; i < xudc->soc->num_phys; i++) {
+               xudc->usb3_phy[i] = NULL;
+               xudc->utmi_phy[i] = NULL;
+               xudc->usbphy[i] = NULL;
+       }
 
-exit_utmi_phy:
-       phy_exit(xudc->utmi_phy);
        return err;
 }
 
 static void tegra_xudc_phy_exit(struct tegra_xudc *xudc)
 {
-       phy_exit(xudc->usb3_phy);
-       phy_exit(xudc->utmi_phy);
+       unsigned int i;
+
+       for (i = 0; i < xudc->soc->num_phys; i++) {
+               phy_exit(xudc->usb3_phy[i]);
+               phy_exit(xudc->utmi_phy[i]);
+       }
+}
+
+static int tegra_xudc_phy_init(struct tegra_xudc *xudc)
+{
+       int err;
+       unsigned int i;
+
+       for (i = 0; i < xudc->soc->num_phys; i++) {
+               err = phy_init(xudc->utmi_phy[i]);
+               if (err < 0) {
+                       dev_err(xudc->dev, "utmi phy init failed: %d\n", err);
+                       goto exit_phy;
+               }
+
+               err = phy_init(xudc->usb3_phy[i]);
+               if (err < 0) {
+                       dev_err(xudc->dev, "usb3 phy init failed: %d\n", err);
+                       goto exit_phy;
+               }
+       }
+       return 0;
+
+exit_phy:
+       tegra_xudc_phy_exit(xudc);
+       return err;
 }
 
 static const char * const tegra210_xudc_supply_names[] = {
@@ -3368,6 +3499,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
        .num_supplies = ARRAY_SIZE(tegra210_xudc_supply_names),
        .clock_names = tegra210_xudc_clock_names,
        .num_clks = ARRAY_SIZE(tegra210_xudc_clock_names),
+       .num_phys = 4,
        .u1_enable = false,
        .u2_enable = true,
        .lpm_enable = false,
@@ -3380,6 +3512,7 @@ static struct tegra_xudc_soc tegra210_xudc_soc_data = {
 static struct tegra_xudc_soc tegra186_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 = false,
@@ -3457,7 +3590,6 @@ static int tegra_xudc_probe(struct platform_device *pdev)
 {
        struct tegra_xudc *xudc;
        struct resource *res;
-       struct usb_role_switch_desc role_sx_desc = { 0 };
        unsigned int i;
        int err;
 
@@ -3492,11 +3624,8 @@ static int tegra_xudc_probe(struct platform_device *pdev)
        }
 
        xudc->irq = platform_get_irq(pdev, 0);
-       if (xudc->irq < 0) {
-               dev_err(xudc->dev, "failed to get IRQ: %d\n",
-                               xudc->irq);
+       if (xudc->irq < 0)
                return xudc->irq;
-       }
 
        err = devm_request_irq(&pdev->dev, xudc->irq, tegra_xudc_irq, 0,
                               dev_name(&pdev->dev), xudc);
@@ -3546,19 +3675,9 @@ static int tegra_xudc_probe(struct platform_device *pdev)
                goto put_padctl;
        }
 
-       xudc->usb3_phy = devm_phy_optional_get(&pdev->dev, "usb3");
-       if (IS_ERR(xudc->usb3_phy)) {
-               err = PTR_ERR(xudc->usb3_phy);
-               dev_err(xudc->dev, "failed to get usb3 phy: %d\n", err);
-               goto disable_regulator;
-       }
-
-       xudc->utmi_phy = devm_phy_optional_get(&pdev->dev, "usb2");
-       if (IS_ERR(xudc->utmi_phy)) {
-               err = PTR_ERR(xudc->utmi_phy);
-               dev_err(xudc->dev, "failed to get usb2 phy: %d\n", err);
+       err = tegra_xudc_phy_get(xudc);
+       if (err)
                goto disable_regulator;
-       }
 
        err = tegra_xudc_powerdomain_init(xudc);
        if (err)
@@ -3587,24 +3706,6 @@ static int tegra_xudc_probe(struct platform_device *pdev)
        INIT_DELAYED_WORK(&xudc->port_reset_war_work,
                                tegra_xudc_port_reset_war_work);
 
-       if (of_property_read_bool(xudc->dev->of_node, "usb-role-switch")) {
-               role_sx_desc.set = tegra_xudc_usb_role_sw_set;
-               role_sx_desc.fwnode = dev_fwnode(xudc->dev);
-
-               xudc->usb_role_sw = usb_role_switch_register(xudc->dev,
-                                                       &role_sx_desc);
-               if (IS_ERR(xudc->usb_role_sw)) {
-                       err = PTR_ERR(xudc->usb_role_sw);
-                       dev_err(xudc->dev, "Failed to register USB role SW: %d",
-                                          err);
-                       goto free_eps;
-               }
-       } else {
-               /* Set the mode as device mode and this keeps phy always ON */
-               dev_info(xudc->dev, "Set usb role to device mode always");
-               schedule_work(&xudc->usb_role_sw_work);
-       }
-
        pm_runtime_enable(&pdev->dev);
 
        xudc->gadget.ops = &tegra_xudc_gadget_ops;
@@ -3639,15 +3740,12 @@ put_padctl:
 static int tegra_xudc_remove(struct platform_device *pdev)
 {
        struct tegra_xudc *xudc = platform_get_drvdata(pdev);
+       unsigned int i;
 
        pm_runtime_get_sync(xudc->dev);
 
        cancel_delayed_work(&xudc->plc_reset_work);
-
-       if (xudc->usb_role_sw) {
-               usb_role_switch_unregister(xudc->usb_role_sw);
-               cancel_work_sync(&xudc->usb_role_sw_work);
-       }
+       cancel_work_sync(&xudc->usb_role_sw_work);
 
        usb_del_gadget_udc(&xudc->gadget);
 
@@ -3658,8 +3756,10 @@ static int tegra_xudc_remove(struct platform_device *pdev)
 
        regulator_bulk_disable(xudc->soc->num_supplies, xudc->supplies);
 
-       phy_power_off(xudc->utmi_phy);
-       phy_power_off(xudc->usb3_phy);
+       for (i = 0; i < xudc->soc->num_phys; i++) {
+               phy_power_off(xudc->utmi_phy[i]);
+               phy_power_off(xudc->usb3_phy[i]);
+       }
 
        tegra_xudc_phy_exit(xudc);
 
index bd4f6ef534d96bd5ec3b45bed66e01bda0f022a4..1300c457d9ed679145abedc39250f677c794ae2d 100644 (file)
@@ -110,11 +110,12 @@ static int mv_ehci_probe(struct platform_device *pdev)
        struct resource *r;
        int retval = -ENODEV;
        u32 offset;
+       u32 status;
 
        if (usb_disabled())
                return -ENODEV;
 
-       hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, "mv ehci");
+       hcd = usb_create_hcd(&ehci_platform_hc_driver, &pdev->dev, dev_name(&pdev->dev));
        if (!hcd)
                return -ENOMEM;
 
@@ -213,6 +214,14 @@ static int mv_ehci_probe(struct platform_device *pdev)
                device_wakeup_enable(hcd->self.controller);
        }
 
+       if (of_usb_get_phy_mode(pdev->dev.of_node) == USBPHY_INTERFACE_MODE_HSIC) {
+               status = ehci_readl(ehci, &ehci->regs->port_status[0]);
+               /* These "reserved" bits actually enable HSIC mode. */
+               status |= BIT(25);
+               status &= ~GENMASK(31, 30);
+               ehci_writel(ehci, status, &ehci->regs->port_status[0]);
+       }
+
        dev_info(&pdev->dev,
                 "successful find EHCI device with regs 0x%p irq %d"
                 " working in %s mode\n", hcd->regs, hcd->irq,
index b0882c13a1d168498e1a15a47b37e3a01842e4a8..1a48ab1bd3b2ab227e85586857fc5549c7aa3693 100644 (file)
@@ -384,7 +384,7 @@ MODULE_DEVICE_TABLE(pci, pci_ids);
 
 /* pci driver glue; this is a "new style" PCI driver module */
 static struct pci_driver ehci_pci_driver = {
-       .name =         (char *) hcd_name,
+       .name =         hcd_name,
        .id_table =     pci_ids,
 
        .probe =        ehci_pci_probe,
index 769749ca5961a422ac9e69834a20c76533cab90d..e4fc3f66d43bf057eed9737e907a2cb84d823b71 100644 (file)
@@ -29,6 +29,8 @@
 #include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/reset.h>
+#include <linux/sys_soc.h>
+#include <linux/timer.h>
 #include <linux/usb.h>
 #include <linux/usb/hcd.h>
 #include <linux/usb/ehci_pdriver.h>
@@ -44,6 +46,9 @@ struct ehci_platform_priv {
        struct clk *clks[EHCI_MAX_CLKS];
        struct reset_control *rsts;
        bool reset_on_resume;
+       bool quirk_poll;
+       struct timer_list poll_timer;
+       struct delayed_work poll_work;
 };
 
 static const char hcd_name[] = "ehci-platform";
@@ -118,6 +123,111 @@ static struct usb_ehci_pdata ehci_platform_defaults = {
        .power_off =            ehci_platform_power_off,
 };
 
+/**
+ * quirk_poll_check_port_status - Poll port_status if the device sticks
+ * @ehci: the ehci hcd pointer
+ *
+ * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting
+ * stuck very rarely after a full/low usb device was disconnected. To
+ * detect such a situation, the controllers require a special way which poll
+ * the EHCI PORTSC register.
+ *
+ * Return: true if the controller's port_status indicated getting stuck
+ */
+static bool quirk_poll_check_port_status(struct ehci_hcd *ehci)
+{
+       u32 port_status = ehci_readl(ehci, &ehci->regs->port_status[0]);
+
+       if (!(port_status & PORT_OWNER) &&
+            (port_status & PORT_POWER) &&
+           !(port_status & PORT_CONNECT) &&
+            (port_status & PORT_LS_MASK))
+               return true;
+
+       return false;
+}
+
+/**
+ * quirk_poll_rebind_companion - rebind comanion device to recover
+ * @ehci: the ehci hcd pointer
+ *
+ * Since EHCI/OHCI controllers on R-Car Gen3 SoCs are possible to be getting
+ * stuck very rarely after a full/low usb device was disconnected. To
+ * recover from such a situation, the controllers require changing the OHCI
+ * functional state.
+ */
+static void quirk_poll_rebind_companion(struct ehci_hcd *ehci)
+{
+       struct device *companion_dev;
+       struct usb_hcd *hcd = ehci_to_hcd(ehci);
+
+       companion_dev = usb_of_get_companion_dev(hcd->self.controller);
+       if (!companion_dev)
+               return;
+
+       device_release_driver(companion_dev);
+       if (device_attach(companion_dev) < 0)
+               ehci_err(ehci, "%s: failed\n", __func__);
+
+       put_device(companion_dev);
+}
+
+static void quirk_poll_work(struct work_struct *work)
+{
+       struct ehci_platform_priv *priv =
+               container_of(to_delayed_work(work), struct ehci_platform_priv,
+                            poll_work);
+       struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd,
+                                            priv);
+
+       /* check the status twice to reduce misdetection rate */
+       if (!quirk_poll_check_port_status(ehci))
+               return;
+       udelay(10);
+       if (!quirk_poll_check_port_status(ehci))
+               return;
+
+       ehci_dbg(ehci, "%s: detected getting stuck. rebind now!\n", __func__);
+       quirk_poll_rebind_companion(ehci);
+}
+
+static void quirk_poll_timer(struct timer_list *t)
+{
+       struct ehci_platform_priv *priv = from_timer(priv, t, poll_timer);
+       struct ehci_hcd *ehci = container_of((void *)priv, struct ehci_hcd,
+                                            priv);
+
+       if (quirk_poll_check_port_status(ehci)) {
+               /*
+                * Now scheduling the work for testing the port more. Note that
+                * updating the status is possible to be delayed when
+                * reconnection. So, this uses delayed work with 5 ms delay
+                * to avoid misdetection.
+                */
+               schedule_delayed_work(&priv->poll_work, msecs_to_jiffies(5));
+       }
+
+       mod_timer(&priv->poll_timer, jiffies + HZ);
+}
+
+static void quirk_poll_init(struct ehci_platform_priv *priv)
+{
+       INIT_DELAYED_WORK(&priv->poll_work, quirk_poll_work);
+       timer_setup(&priv->poll_timer, quirk_poll_timer, 0);
+       mod_timer(&priv->poll_timer, jiffies + HZ);
+}
+
+static void quirk_poll_end(struct ehci_platform_priv *priv)
+{
+       del_timer_sync(&priv->poll_timer);
+       cancel_delayed_work(&priv->poll_work);
+}
+
+static const struct soc_device_attribute quirk_poll_match[] = {
+       { .family = "R-Car Gen3" },
+       { /* sentinel*/ }
+};
+
 static int ehci_platform_probe(struct platform_device *dev)
 {
        struct usb_hcd *hcd;
@@ -176,6 +286,9 @@ static int ehci_platform_probe(struct platform_device *dev)
                                          "has-transaction-translator"))
                        hcd->has_tt = 1;
 
+               if (soc_device_match(quirk_poll_match))
+                       priv->quirk_poll = true;
+
                for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
                        priv->clks[clk] = of_clk_get(dev->dev.of_node, clk);
                        if (IS_ERR(priv->clks[clk])) {
@@ -247,6 +360,9 @@ static int ehci_platform_probe(struct platform_device *dev)
        device_enable_async_suspend(hcd->self.controller);
        platform_set_drvdata(dev, hcd);
 
+       if (priv->quirk_poll)
+               quirk_poll_init(priv);
+
        return err;
 
 err_power:
@@ -273,6 +389,9 @@ static int ehci_platform_remove(struct platform_device *dev)
        struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
        int clk;
 
+       if (priv->quirk_poll)
+               quirk_poll_end(priv);
+
        usb_remove_hcd(hcd);
 
        if (pdata->power_off)
@@ -297,9 +416,13 @@ static int ehci_platform_suspend(struct device *dev)
        struct usb_hcd *hcd = dev_get_drvdata(dev);
        struct usb_ehci_pdata *pdata = dev_get_platdata(dev);
        struct platform_device *pdev = to_platform_device(dev);
+       struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
        bool do_wakeup = device_may_wakeup(dev);
        int ret;
 
+       if (priv->quirk_poll)
+               quirk_poll_end(priv);
+
        ret = ehci_suspend(hcd, do_wakeup);
        if (ret)
                return ret;
@@ -331,6 +454,10 @@ static int ehci_platform_resume(struct device *dev)
        }
 
        ehci_resume(hcd, priv->reset_on_resume);
+
+       if (priv->quirk_poll)
+               quirk_poll_init(priv);
+
        return 0;
 }
 #endif /* CONFIG_PM_SLEEP */
index d6433f206c17063f6e170a3bdd89f2d5dee7af51..10d51daa6a1bb9bc97d257cace287fdd40bf16f9 100644 (file)
@@ -282,7 +282,7 @@ done:
 struct dma_aligned_buffer {
        void *kmalloc_ptr;
        void *old_xfer_buffer;
-       u8 data[0];
+       u8 data[];
 };
 
 static void free_dma_aligned_buffer(struct urb *urb)
index ac5e967907d1412fc403256d22a5369807275eca..229b3de319e64eda9df96265f4ddea95752fafcb 100644 (file)
@@ -255,7 +255,7 @@ struct ehci_hcd {                   /* one per controller */
        struct list_head        tt_list;
 
        /* platform-specific data -- must come last */
-       unsigned long           priv[0] __aligned(sizeof(s64));
+       unsigned long           priv[] __aligned(sizeof(s64));
 };
 
 /* convert between an HCD pointer and the corresponding EHCI_HCD */
@@ -460,7 +460,7 @@ struct ehci_iso_sched {
        struct list_head        td_list;
        unsigned                span;
        unsigned                first_packet;
-       struct ehci_iso_packet  packet[0];
+       struct ehci_iso_packet  packet[];
 };
 
 /*
index 04733876c9c6d0d47c37fa15f28251eb627abaa0..a8e1048278d0c3bbf147c74154328473d06649df 100644 (file)
@@ -396,6 +396,7 @@ static int fhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb,
        case PIPE_CONTROL:
                /* 1 td fro setup,1 for ack */
                size = 2;
+               fallthrough;
        case PIPE_BULK:
                /* one td for every 4096 bytes(can be up to 8k) */
                size += urb->transfer_buffer_length / 4096;
index 1b4db95e5c43aef564e6f47fb5fd441d1b954634..6cee40ec65b4140b0a1352b7b7a8405e8c6889db 100644 (file)
@@ -490,7 +490,7 @@ struct fotg210_iso_packet {
 struct fotg210_iso_sched {
        struct list_head        td_list;
        unsigned                span;
-       struct fotg210_iso_packet       packet[0];
+       struct fotg210_iso_packet       packet[];
 };
 
 /*
index f4e13a3fddee11a4b62acf38265807493012d39d..22117a6aeb4a32065d7db99ebfe00bbbac4ecffb 100644 (file)
@@ -288,7 +288,7 @@ MODULE_DEVICE_TABLE (pci, pci_ids);
 
 /* pci driver glue; this is a "new style" PCI driver module */
 static struct pci_driver ohci_pci_driver = {
-       .name =         (char *) hcd_name,
+       .name =         hcd_name,
        .id_table =     pci_ids,
 
        .probe =        usb_hcd_pci_probe,
index b015b00774b217c2e42b27920e34c25485615ddc..27c26ca10bfd89aca6fa798383938630c474559a 100644 (file)
@@ -337,7 +337,7 @@ typedef struct urb_priv {
        u16                     length;         // # tds in this request
        u16                     td_cnt;         // tds already serviced
        struct list_head        pending;
-       struct td               *td [0];        // all TDs in this request
+       struct td               *td[];          // all TDs in this request
 
 } urb_priv_t;
 
@@ -435,7 +435,7 @@ struct ohci_hcd {
        struct dentry           *debug_dir;
 
        /* platform-specific data -- must come last */
-       unsigned long           priv[0] __aligned(sizeof(s64));
+       unsigned long           priv[] __aligned(sizeof(s64));
 
 };
 
index 72a34a1eb6189c4ab0fa055e3d339ab297cab72e..adaf4063690a451b6a3106f7079317268ecddc25 100644 (file)
@@ -1792,7 +1792,7 @@ struct platform_driver sl811h_driver = {
        .suspend =      sl811h_suspend,
        .resume =       sl811h_resume,
        .driver = {
-               .name = (char *) hcd_name,
+               .name = hcd_name,
        },
 };
 EXPORT_SYMBOL(sl811h_driver);
index 0fa3d72bae261a517e2f1bf45cf04c9e5086640b..957c87efc746c74ea478d96e98ca6fa45e3b18b0 100644 (file)
@@ -294,7 +294,7 @@ static const struct pci_device_id uhci_pci_ids[] = { {
 MODULE_DEVICE_TABLE(pci, uhci_pci_ids);
 
 static struct pci_driver uhci_pci_driver = {
-       .name =         (char *)hcd_name,
+       .name =         hcd_name,
        .id_table =     uhci_pci_ids,
 
        .probe =        usb_hcd_pci_probe,
index 3c4abb5a1c3fc6bdb86e749ab62cf256381c1f75..5546e7e013a8895f8968573c464f84d341f81f4d 100644 (file)
@@ -219,8 +219,7 @@ static int xhci_histb_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       histb->ctrl = devm_ioremap_resource(&pdev->dev, res);
+       histb->ctrl = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(histb->ctrl))
                return PTR_ERR(histb->ctrl);
 
index af92b2576fe91c5d497ab0aa8145aeaabd9ab5c9..9eca1fe81061c81dd46d9a00fa2e6664f00f61bf 100644 (file)
@@ -567,6 +567,7 @@ struct xhci_hub *xhci_get_rhub(struct usb_hcd *hcd)
  */
 static void xhci_set_port_power(struct xhci_hcd *xhci, struct usb_hcd *hcd,
                                u16 index, bool on, unsigned long *flags)
+       __must_hold(&xhci->lock)
 {
        struct xhci_hub *rhub;
        struct xhci_port *port;
@@ -617,6 +618,7 @@ static void xhci_port_set_test_mode(struct xhci_hcd *xhci,
 
 static int xhci_enter_test_mode(struct xhci_hcd *xhci,
                                u16 test_mode, u16 wIndex, unsigned long *flags)
+       __must_hold(&xhci->lock)
 {
        int i, retval;
 
@@ -1306,7 +1308,47 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                         wIndex, link_state);
                                goto error;
                        }
+
+                       /*
+                        * set link to U0, steps depend on current link state.
+                        * U3: set link to U0 and wait for u3exit completion.
+                        * U1/U2:  no PLC complete event, only set link to U0.
+                        * Resume/Recovery: device initiated U0, only wait for
+                        * completion
+                        */
+                       if (link_state == USB_SS_PORT_LS_U0) {
+                               u32 pls = temp & PORT_PLS_MASK;
+                               bool wait_u0 = false;
+
+                               /* already in U0 */
+                               if (pls == XDEV_U0)
+                                       break;
+                               if (pls == XDEV_U3 ||
+                                   pls == XDEV_RESUME ||
+                                   pls == XDEV_RECOVERY) {
+                                       wait_u0 = true;
+                                       reinit_completion(&bus_state->u3exit_done[wIndex]);
+                               }
+                               if (pls <= XDEV_U3) /* U1, U2, U3 */
+                                       xhci_set_link_state(xhci, ports[wIndex],
+                                                           USB_SS_PORT_LS_U0);
+                               if (!wait_u0) {
+                                       if (pls > XDEV_U3)
+                                               goto error;
+                                       break;
+                               }
+                               spin_unlock_irqrestore(&xhci->lock, flags);
+                               if (!wait_for_completion_timeout(&bus_state->u3exit_done[wIndex],
+                                                                msecs_to_jiffies(100)))
+                                       xhci_dbg(xhci, "missing U0 port change event for port %d\n",
+                                                wIndex);
+                               spin_lock_irqsave(&xhci->lock, flags);
+                               temp = readl(ports[wIndex]->addr);
+                               break;
+                       }
+
                        if (link_state == USB_SS_PORT_LS_U3) {
+                               int retries = 16;
                                slot_id = xhci_find_slot_id_by_port(hcd, xhci,
                                                wIndex + 1);
                                if (slot_id) {
@@ -1317,17 +1359,18 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
                                        xhci_stop_device(xhci, slot_id, 1);
                                        spin_lock_irqsave(&xhci->lock, flags);
                                }
-                       }
-
-                       xhci_set_link_state(xhci, ports[wIndex], link_state);
-
-                       spin_unlock_irqrestore(&xhci->lock, flags);
-                       msleep(20); /* wait device to enter */
-                       spin_lock_irqsave(&xhci->lock, flags);
-
-                       temp = readl(ports[wIndex]->addr);
-                       if (link_state == USB_SS_PORT_LS_U3)
+                               xhci_set_link_state(xhci, ports[wIndex], USB_SS_PORT_LS_U3);
+                               spin_unlock_irqrestore(&xhci->lock, flags);
+                               while (retries--) {
+                                       usleep_range(4000, 8000);
+                                       temp = readl(ports[wIndex]->addr);
+                                       if ((temp & PORT_PLS_MASK) == XDEV_U3)
+                                               break;
+                               }
+                               spin_lock_irqsave(&xhci->lock, flags);
+                               temp = readl(ports[wIndex]->addr);
                                bus_state->suspended_ports |= 1 << wIndex;
+                       }
                        break;
                case USB_PORT_FEAT_POWER:
                        /*
index 884c601bfa15f8d809a44bab42f11809a9c1e93c..9764122c9cdf2d9baa8271a4cd927dc8d99efb2d 100644 (file)
@@ -2552,6 +2552,7 @@ int xhci_mem_init(struct xhci_hcd *xhci, gfp_t flags)
                xhci->usb3_rhub.bus_state.resume_done[i] = 0;
                /* Only the USB 2.0 completions will ever be used. */
                init_completion(&xhci->usb2_rhub.bus_state.rexit_done[i]);
+               init_completion(&xhci->usb3_rhub.bus_state.u3exit_done[i]);
        }
 
        if (scratchpad_alloc(xhci, flags))
index 5ac458b7d2e0efa406e5db2ca57dfe3dde59e550..acd56517215aeefe27abd5d27fa695d477bb95ec 100644 (file)
@@ -95,7 +95,7 @@ struct mu3h_sch_ep_info {
        u32 pkts;
        u32 cs_count;
        u32 burst_mode;
-       u32 bw_budget_table[0];
+       u32 bw_budget_table[];
 };
 
 #define MU3C_U3_PORT_MAX 4
index 1fddc41fa1f38408acd9859226c021508b4f61a6..766b74723e649bf0996bdac6a36898f91c2d875d 100644 (file)
@@ -50,6 +50,7 @@
 #define PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI                0x15f0
 #define PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI              0x8a13
 #define PCI_DEVICE_ID_INTEL_CML_XHCI                   0xa3af
+#define PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI            0x9a13
 
 #define PCI_DEVICE_ID_AMD_PROMONTORYA_4                        0x43b9
 #define PCI_DEVICE_ID_AMD_PROMONTORYA_3                        0x43ba
@@ -217,7 +218,8 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
             pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_2C_XHCI ||
             pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_4C_XHCI ||
             pdev->device == PCI_DEVICE_ID_INTEL_TITAN_RIDGE_DD_XHCI ||
-            pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI))
+            pdev->device == PCI_DEVICE_ID_INTEL_ICE_LAKE_XHCI ||
+            pdev->device == PCI_DEVICE_ID_INTEL_TIGER_LAKE_XHCI))
                xhci->quirks |= XHCI_DEFAULT_PM_RUNTIME_ALLOW;
 
        if (pdev->vendor == PCI_VENDOR_ID_ETRON &&
@@ -244,6 +246,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
                        pdev->device == 0x3432)
                xhci->quirks |= XHCI_BROKEN_STREAMS;
 
+       if (pdev->vendor == PCI_VENDOR_ID_VIA && pdev->device == 0x3483)
+               xhci->quirks |= XHCI_LPM_SUPPORT;
+
        if (pdev->vendor == PCI_VENDOR_ID_ASMEDIA &&
                        pdev->device == 0x1042)
                xhci->quirks |= XHCI_BROKEN_STREAMS;
@@ -550,7 +555,7 @@ MODULE_DEVICE_TABLE(pci, pci_ids);
 
 /* pci driver glue; this is a "new style" PCI driver module */
 static struct pci_driver xhci_pci_driver = {
-       .name =         (char *) hcd_name,
+       .name =         hcd_name,
        .id_table =     pci_ids,
 
        .probe =        xhci_pci_probe,
index 315b4552693c79848146e325099c37350d7131e3..1d4f6f85f0febd1d967c6619047156c9607cc2cb 100644 (file)
@@ -219,8 +219,7 @@ static int xhci_plat_probe(struct platform_device *pdev)
                goto disable_runtime;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       hcd->regs = devm_ioremap_resource(&pdev->dev, res);
+       hcd->regs = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
        if (IS_ERR(hcd->regs)) {
                ret = PTR_ERR(hcd->regs);
                goto put_hcd;
index d23f7408c81f1e4409389a5b51b3b05ff93275a0..a78787bb513366ec6830ecf3dea8bf460670febe 100644 (file)
@@ -955,6 +955,7 @@ void xhci_stop_endpoint_command_watchdog(struct timer_list *t)
        struct xhci_virt_ep *ep = from_timer(ep, t, stop_cmd_timer);
        struct xhci_hcd *xhci = ep->xhci;
        unsigned long flags;
+       u32 usbsts;
 
        spin_lock_irqsave(&xhci->lock, flags);
 
@@ -965,8 +966,11 @@ void xhci_stop_endpoint_command_watchdog(struct timer_list *t)
                xhci_dbg(xhci, "Stop EP timer raced with cmd completion, exit");
                return;
        }
+       usbsts = readl(&xhci->op_regs->status);
 
        xhci_warn(xhci, "xHCI host not responding to stop endpoint command.\n");
+       xhci_warn(xhci, "USBSTS:%s\n", xhci_decode_usbsts(usbsts));
+
        ep->ep_state &= ~EP_STOP_CMD_PENDING;
 
        xhci_halt(xhci);
@@ -1677,6 +1681,7 @@ static void handle_port_status(struct xhci_hcd *xhci,
             (portsc & PORT_PLS_MASK) == XDEV_U1 ||
             (portsc & PORT_PLS_MASK) == XDEV_U2)) {
                xhci_dbg(xhci, "resume SS port %d finished\n", port_id);
+               complete(&bus_state->u3exit_done[hcd_portnum]);
                /* We've just brought the device into U0/1/2 through either the
                 * Resume state after a device remote wakeup, or through the
                 * U3Exit state after a host-initiated resume.  If it's a device
@@ -2413,6 +2418,10 @@ static int handle_tx_event(struct xhci_hcd *xhci,
                status = -EPIPE;
                break;
        case COMP_SPLIT_TRANSACTION_ERROR:
+               xhci_dbg(xhci, "Split transaction error for slot %u ep %u\n",
+                        slot_id, ep_index);
+               status = -EPROTO;
+               break;
        case COMP_USB_TRANSACTION_ERROR:
                xhci_dbg(xhci, "Transfer error for slot %u ep %u on endpoint\n",
                         slot_id, ep_index);
index 8163aefc6c6bcc17f8736b3232c7451e4e3ed7ce..2eaf5c0af80ce156b609a85c8239e199c1e478eb 100644 (file)
@@ -24,6 +24,9 @@
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
 #include <linux/slab.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/role.h>
 #include <soc/tegra/pmc.h>
 
 #include "xhci.h"
@@ -203,6 +206,8 @@ struct tegra_xusb_soc {
 
        bool scale_ss_clock;
        bool has_ipfs;
+       bool lpm_support;
+       bool otg_reset_sspi;
 };
 
 struct tegra_xusb_context {
@@ -250,6 +255,14 @@ struct tegra_xusb {
        struct phy **phys;
        unsigned int num_phys;
 
+       struct usb_phy **usbphy;
+       unsigned int num_usb_phys;
+       int otg_usb2_port;
+       int otg_usb3_port;
+       bool host_mode;
+       struct notifier_block id_nb;
+       struct work_struct id_work;
+
        /* Firmware loading related */
        struct {
                size_t size;
@@ -1081,6 +1094,205 @@ static int tegra_xusb_enable_firmware_messages(struct tegra_xusb *tegra)
        return err;
 }
 
+static void tegra_xhci_set_port_power(struct tegra_xusb *tegra, bool main,
+                                                bool set)
+{
+       struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+       struct usb_hcd *hcd = main ?  xhci->main_hcd : xhci->shared_hcd;
+       unsigned int wait = (!main && !set) ? 1000 : 10;
+       u16 typeReq = set ? SetPortFeature : ClearPortFeature;
+       u16 wIndex = main ? tegra->otg_usb2_port + 1 : tegra->otg_usb3_port + 1;
+       u32 status;
+       u32 stat_power = main ? USB_PORT_STAT_POWER : USB_SS_PORT_STAT_POWER;
+       u32 status_val = set ? stat_power : 0;
+
+       dev_dbg(tegra->dev, "%s():%s %s port power\n", __func__,
+               set ? "set" : "clear", main ? "HS" : "SS");
+
+       hcd->driver->hub_control(hcd, typeReq, USB_PORT_FEAT_POWER, wIndex,
+                                NULL, 0);
+
+       do {
+               tegra_xhci_hc_driver.hub_control(hcd, GetPortStatus, 0, wIndex,
+                                       (char *) &status, sizeof(status));
+               if (status_val == (status & stat_power))
+                       break;
+
+               if (!main && !set)
+                       usleep_range(600, 700);
+               else
+                       usleep_range(10, 20);
+       } while (--wait > 0);
+
+       if (status_val != (status & stat_power))
+               dev_info(tegra->dev, "failed to %s %s PP %d\n",
+                                               set ? "set" : "clear",
+                                               main ? "HS" : "SS", status);
+}
+
+static struct phy *tegra_xusb_get_phy(struct tegra_xusb *tegra, char *name,
+                                                               int port)
+{
+       unsigned int i, phy_count = 0;
+
+       for (i = 0; i < tegra->soc->num_types; i++) {
+               if (!strncmp(tegra->soc->phy_types[i].name, "usb2",
+                                                           strlen(name)))
+                       return tegra->phys[phy_count+port];
+
+               phy_count += tegra->soc->phy_types[i].num;
+       }
+
+       return NULL;
+}
+
+static void tegra_xhci_id_work(struct work_struct *work)
+{
+       struct tegra_xusb *tegra = container_of(work, struct tegra_xusb,
+                                               id_work);
+       struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
+       struct tegra_xusb_mbox_msg msg;
+       struct phy *phy = tegra_xusb_get_phy(tegra, "usb2",
+                                                   tegra->otg_usb2_port);
+       u32 status;
+       int ret;
+
+       dev_dbg(tegra->dev, "host mode %s\n", tegra->host_mode ? "on" : "off");
+
+       mutex_lock(&tegra->lock);
+
+       if (tegra->host_mode)
+               phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST);
+       else
+               phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
+
+       mutex_unlock(&tegra->lock);
+
+       if (tegra->host_mode) {
+               /* switch to host mode */
+               if (tegra->otg_usb3_port >= 0) {
+                       if (tegra->soc->otg_reset_sspi) {
+                               /* set PP=0 */
+                               tegra_xhci_hc_driver.hub_control(
+                                       xhci->shared_hcd, GetPortStatus,
+                                       0, tegra->otg_usb3_port+1,
+                                       (char *) &status, sizeof(status));
+                               if (status & USB_SS_PORT_STAT_POWER)
+                                       tegra_xhci_set_port_power(tegra, false,
+                                                                 false);
+
+                               /* reset OTG port SSPI */
+                               msg.cmd = MBOX_CMD_RESET_SSPI;
+                               msg.data = tegra->otg_usb3_port+1;
+
+                               ret = tegra_xusb_mbox_send(tegra, &msg);
+                               if (ret < 0) {
+                                       dev_info(tegra->dev,
+                                               "failed to RESET_SSPI %d\n",
+                                               ret);
+                               }
+                       }
+
+                       tegra_xhci_set_port_power(tegra, false, true);
+               }
+
+               tegra_xhci_set_port_power(tegra, true, true);
+
+       } else {
+               if (tegra->otg_usb3_port >= 0)
+                       tegra_xhci_set_port_power(tegra, false, false);
+
+               tegra_xhci_set_port_power(tegra, true, false);
+       }
+}
+
+static int tegra_xusb_get_usb2_port(struct tegra_xusb *tegra,
+                                             struct usb_phy *usbphy)
+{
+       unsigned int i;
+
+       for (i = 0; i < tegra->num_usb_phys; i++) {
+               if (tegra->usbphy[i] && usbphy == tegra->usbphy[i])
+                       return i;
+       }
+
+       return -1;
+}
+
+static int tegra_xhci_id_notify(struct notifier_block *nb,
+                                        unsigned long action, void *data)
+{
+       struct tegra_xusb *tegra = container_of(nb, struct tegra_xusb,
+                                                   id_nb);
+       struct usb_phy *usbphy = (struct usb_phy *)data;
+
+       dev_dbg(tegra->dev, "%s(): action is %d", __func__, usbphy->last_event);
+
+       if ((tegra->host_mode && usbphy->last_event == USB_EVENT_ID) ||
+               (!tegra->host_mode && usbphy->last_event != USB_EVENT_ID)) {
+               dev_dbg(tegra->dev, "Same role(%d) received. Ignore",
+                       tegra->host_mode);
+               return NOTIFY_OK;
+       }
+
+       tegra->otg_usb2_port = tegra_xusb_get_usb2_port(tegra, usbphy);
+       tegra->otg_usb3_port = tegra_xusb_padctl_get_usb3_companion(
+                                                       tegra->padctl,
+                                                       tegra->otg_usb2_port);
+
+       tegra->host_mode = (usbphy->last_event == USB_EVENT_ID) ? true : false;
+
+       schedule_work(&tegra->id_work);
+
+       return NOTIFY_OK;
+}
+
+static int tegra_xusb_init_usb_phy(struct tegra_xusb *tegra)
+{
+       unsigned int i;
+
+       tegra->usbphy = devm_kcalloc(tegra->dev, tegra->num_usb_phys,
+                                  sizeof(*tegra->usbphy), GFP_KERNEL);
+       if (!tegra->usbphy)
+               return -ENOMEM;
+
+       INIT_WORK(&tegra->id_work, tegra_xhci_id_work);
+       tegra->id_nb.notifier_call = tegra_xhci_id_notify;
+
+       for (i = 0; i < tegra->num_usb_phys; i++) {
+               struct phy *phy = tegra_xusb_get_phy(tegra, "usb2", i);
+
+               if (!phy)
+                       continue;
+
+               tegra->usbphy[i] = devm_usb_get_phy_by_node(tegra->dev,
+                                                       phy->dev.of_node,
+                                                       &tegra->id_nb);
+               if (!IS_ERR(tegra->usbphy[i])) {
+                       dev_dbg(tegra->dev, "usbphy-%d registered", i);
+                       otg_set_host(tegra->usbphy[i]->otg, &tegra->hcd->self);
+               } else {
+                       /*
+                        * usb-phy is optional, continue if its not available.
+                        */
+                       tegra->usbphy[i] = NULL;
+               }
+       }
+
+       return 0;
+}
+
+static void tegra_xusb_deinit_usb_phy(struct tegra_xusb *tegra)
+{
+       unsigned int i;
+
+       cancel_work_sync(&tegra->id_work);
+
+       for (i = 0; i < tegra->num_usb_phys; i++)
+               if (tegra->usbphy[i])
+                       otg_set_host(tegra->usbphy[i]->otg, NULL);
+}
+
 static int tegra_xusb_probe(struct platform_device *pdev)
 {
        struct tegra_xusb *tegra;
@@ -1254,8 +1466,11 @@ static int tegra_xusb_probe(struct platform_device *pdev)
                goto put_powerdomains;
        }
 
-       for (i = 0; i < tegra->soc->num_types; i++)
+       for (i = 0; i < tegra->soc->num_types; i++) {
+               if (!strncmp(tegra->soc->phy_types[i].name, "usb2", 4))
+                       tegra->num_usb_phys = tegra->soc->phy_types[i].num;
                tegra->num_phys += tegra->soc->phy_types[i].num;
+       }
 
        tegra->phys = devm_kcalloc(&pdev->dev, tegra->num_phys,
                                   sizeof(*tegra->phys), GFP_KERNEL);
@@ -1384,6 +1599,12 @@ static int tegra_xusb_probe(struct platform_device *pdev)
                goto remove_usb3;
        }
 
+       err = tegra_xusb_init_usb_phy(tegra);
+       if (err < 0) {
+               dev_err(&pdev->dev, "failed to init USB PHY: %d\n", err);
+               goto remove_usb3;
+       }
+
        return 0;
 
 remove_usb3:
@@ -1420,6 +1641,8 @@ static int tegra_xusb_remove(struct platform_device *pdev)
        struct tegra_xusb *tegra = platform_get_drvdata(pdev);
        struct xhci_hcd *xhci = hcd_to_xhci(tegra->hcd);
 
+       tegra_xusb_deinit_usb_phy(tegra);
+
        usb_remove_hcd(xhci->shared_hcd);
        usb_put_hcd(xhci->shared_hcd);
        xhci->shared_hcd = NULL;
@@ -1694,6 +1917,7 @@ static const struct tegra_xusb_soc tegra124_soc = {
        },
        .scale_ss_clock = true,
        .has_ipfs = true,
+       .otg_reset_sspi = false,
        .mbox = {
                .cmd = 0xe4,
                .data_in = 0xe8,
@@ -1733,6 +1957,7 @@ static const struct tegra_xusb_soc tegra210_soc = {
        },
        .scale_ss_clock = false,
        .has_ipfs = true,
+       .otg_reset_sspi = true,
        .mbox = {
                .cmd = 0xe4,
                .data_in = 0xe8,
@@ -1773,12 +1998,14 @@ static const struct tegra_xusb_soc tegra186_soc = {
        },
        .scale_ss_clock = false,
        .has_ipfs = false,
+       .otg_reset_sspi = false,
        .mbox = {
                .cmd = 0xe4,
                .data_in = 0xe8,
                .data_out = 0xec,
                .owner = 0xf0,
        },
+       .lpm_support = true,
 };
 
 static const char * const tegra194_supply_names[] = {
@@ -1802,12 +2029,14 @@ static const struct tegra_xusb_soc tegra194_soc = {
        },
        .scale_ss_clock = false,
        .has_ipfs = false,
+       .otg_reset_sspi = false,
        .mbox = {
                .cmd = 0x68,
                .data_in = 0x6c,
                .data_out = 0x70,
                .owner = 0x74,
        },
+       .lpm_support = true,
 };
 MODULE_FIRMWARE("nvidia/tegra194/xusb.bin");
 
@@ -1832,7 +2061,11 @@ static struct platform_driver tegra_xusb_driver = {
 
 static void tegra_xhci_quirks(struct device *dev, struct xhci_hcd *xhci)
 {
+       struct tegra_xusb *tegra = dev_get_drvdata(dev);
+
        xhci->quirks |= XHCI_PLAT;
+       if (tegra && tegra->soc->lpm_support)
+               xhci->quirks |= XHCI_LPM_SUPPORT;
 }
 
 static int tegra_xhci_setup(struct usb_hcd *hcd)
index dbac0fa9748d5fcea4bbf44ce610c4ecd1d5b5c7..fe38275363e0f61d5968c0f546cdd9af79589453 100644 (file)
@@ -1157,8 +1157,10 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
                xhci_dbg(xhci, "Stop HCD\n");
                xhci_halt(xhci);
                xhci_zero_64b_regs(xhci);
-               xhci_reset(xhci);
+               retval = xhci_reset(xhci);
                spin_unlock_irq(&xhci->lock);
+               if (retval)
+                       return retval;
                xhci_cleanup_msix(xhci);
 
                xhci_dbg(xhci, "// Disabling event ring interrupts\n");
index 3ecee10fdcdc7dfd697b24310de0ad3885421f10..3289bb51620172c8490135aa3f9f7849a55246fb 100644 (file)
@@ -1642,7 +1642,7 @@ struct xhci_scratchpad {
 struct urb_priv {
        int     num_tds;
        int     num_tds_done;
-       struct  xhci_td td[0];
+       struct  xhci_td td[];
 };
 
 /*
@@ -1694,6 +1694,7 @@ struct xhci_bus_state {
        /* Which ports are waiting on RExit to U0 transition. */
        unsigned long           rexit_ports;
        struct completion       rexit_done[USB_MAXCHILDREN];
+       struct completion       u3exit_done[USB_MAXCHILDREN];
 };
 
 
@@ -1901,7 +1902,7 @@ struct xhci_hcd {
 
        void                    *dbc;
        /* platform-specific data -- must come last */
-       unsigned long           priv[0] __aligned(sizeof(s64));
+       unsigned long           priv[] __aligned(sizeof(s64));
 };
 
 /* Platform specific overrides to generic XHCI hc_driver ops */
@@ -2589,6 +2590,35 @@ static inline const char *xhci_decode_portsc(u32 portsc)
        return str;
 }
 
+static inline const char *xhci_decode_usbsts(u32 usbsts)
+{
+       static char str[256];
+       int ret = 0;
+
+       if (usbsts == ~(u32)0)
+               return " 0xffffffff";
+       if (usbsts & STS_HALT)
+               ret += sprintf(str + ret, " HCHalted");
+       if (usbsts & STS_FATAL)
+               ret += sprintf(str + ret, " HSE");
+       if (usbsts & STS_EINT)
+               ret += sprintf(str + ret, " EINT");
+       if (usbsts & STS_PORT)
+               ret += sprintf(str + ret, " PCD");
+       if (usbsts & STS_SAVE)
+               ret += sprintf(str + ret, " SSS");
+       if (usbsts & STS_RESTORE)
+               ret += sprintf(str + ret, " RSS");
+       if (usbsts & STS_SRE)
+               ret += sprintf(str + ret, " SRE");
+       if (usbsts & STS_CNR)
+               ret += sprintf(str + ret, " CNR");
+       if (usbsts & STS_HCE)
+               ret += sprintf(str + ret, " HCE");
+
+       return str;
+}
+
 static inline const char *xhci_decode_doorbell(u32 slot, u32 doorbell)
 {
        static char str[256];
index 834b2494da73cb1526155b7018b1a15961a2ac15..833a460ee55a1e91d4dcc10c5919acd2a0078fed 100644 (file)
@@ -137,6 +137,16 @@ config USB_APPLEDISPLAY
          Say Y here if you want to control the backlight of Apple Cinema
          Displays over USB. This driver provides a sysfs interface.
 
+config APPLE_MFI_FASTCHARGE
+       tristate "Fast charge control for iOS devices"
+       select POWER_SUPPLY
+       help
+         Say Y here if you want to control whether iOS devices will
+         fast charge from the USB interface, as implemented in "MFi"
+         chargers.
+
+         It is safe to say M here.
+
 source "drivers/usb/misc/sisusbvga/Kconfig"
 
 config USB_LD
index 0d416eb624bbe12247493afa40fe60242086e0d7..da39bddb0604d9550184deea4892ec480a9bae8b 100644 (file)
@@ -11,6 +11,7 @@ obj-$(CONFIG_USB_EMI26)                       += emi26.o
 obj-$(CONFIG_USB_EMI62)                        += emi62.o
 obj-$(CONFIG_USB_EZUSB_FX2)            += ezusb.o
 obj-$(CONFIG_USB_FTDI_ELAN)            += ftdi-elan.o
+obj-$(CONFIG_APPLE_MFI_FASTCHARGE)     += apple-mfi-fastcharge.o
 obj-$(CONFIG_USB_IDMOUSE)              += idmouse.o
 obj-$(CONFIG_USB_IOWARRIOR)            += iowarrior.o
 obj-$(CONFIG_USB_ISIGHTFW)             += isight_firmware.o
diff --git a/drivers/usb/misc/apple-mfi-fastcharge.c b/drivers/usb/misc/apple-mfi-fastcharge.c
new file mode 100644 (file)
index 0000000..b403094
--- /dev/null
@@ -0,0 +1,237 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Fast-charge control for Apple "MFi" devices
+ *
+ * Copyright (C) 2019 Bastien Nocera <hadess@hadess.net>
+ */
+
+/* Standard include files */
+#include <linux/module.h>
+#include <linux/power_supply.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+MODULE_AUTHOR("Bastien Nocera <hadess@hadess.net>");
+MODULE_DESCRIPTION("Fast-charge control for Apple \"MFi\" devices");
+MODULE_LICENSE("GPL");
+
+#define TRICKLE_CURRENT_MA             0
+#define FAST_CURRENT_MA                        2500
+
+#define APPLE_VENDOR_ID                        0x05ac  /* Apple */
+
+/* The product ID is defined as starting with 0x12nn, as per the
+ * "Choosing an Apple Device USB Configuration" section in
+ * release R9 (2012) of the "MFi Accessory Hardware Specification"
+ *
+ * To distinguish an Apple device, a USB host can check the device
+ * descriptor of attached USB devices for the following fields:
+ * ■ Vendor ID: 0x05AC
+ * ■ Product ID: 0x12nn
+ *
+ * Those checks will be done in .match() and .probe().
+ */
+
+static const struct usb_device_id mfi_fc_id_table[] = {
+       { .idVendor = APPLE_VENDOR_ID,
+         .match_flags = USB_DEVICE_ID_MATCH_VENDOR },
+       {},
+};
+
+MODULE_DEVICE_TABLE(usb, mfi_fc_id_table);
+
+/* Driver-local specific stuff */
+struct mfi_device {
+       struct usb_device *udev;
+       struct power_supply *battery;
+       int charge_type;
+};
+
+static int apple_mfi_fc_set_charge_type(struct mfi_device *mfi,
+                                       const union power_supply_propval *val)
+{
+       int current_ma;
+       int retval;
+       __u8 request_type;
+
+       if (mfi->charge_type == val->intval) {
+               dev_dbg(&mfi->udev->dev, "charge type %d already set\n",
+                               mfi->charge_type);
+               return 0;
+       }
+
+       switch (val->intval) {
+       case POWER_SUPPLY_CHARGE_TYPE_TRICKLE:
+               current_ma = TRICKLE_CURRENT_MA;
+               break;
+       case POWER_SUPPLY_CHARGE_TYPE_FAST:
+               current_ma = FAST_CURRENT_MA;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       request_type = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE;
+       retval = usb_control_msg(mfi->udev, usb_sndctrlpipe(mfi->udev, 0),
+                                0x40, /* Vendor‐defined power request */
+                                request_type,
+                                current_ma, /* wValue, current offset */
+                                current_ma, /* wIndex, current offset */
+                                NULL, 0, USB_CTRL_GET_TIMEOUT);
+       if (retval) {
+               dev_dbg(&mfi->udev->dev, "retval = %d\n", retval);
+               return retval;
+       }
+
+       mfi->charge_type = val->intval;
+
+       return 0;
+}
+
+static int apple_mfi_fc_get_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               union power_supply_propval *val)
+{
+       struct mfi_device *mfi = power_supply_get_drvdata(psy);
+
+       dev_dbg(&mfi->udev->dev, "prop: %d\n", psp);
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               val->intval = mfi->charge_type;
+               break;
+       case POWER_SUPPLY_PROP_SCOPE:
+               val->intval = POWER_SUPPLY_SCOPE_DEVICE;
+               break;
+       default:
+               return -ENODATA;
+       }
+
+       return 0;
+}
+
+static int apple_mfi_fc_set_property(struct power_supply *psy,
+               enum power_supply_property psp,
+               const union power_supply_propval *val)
+{
+       struct mfi_device *mfi = power_supply_get_drvdata(psy);
+       int ret;
+
+       dev_dbg(&mfi->udev->dev, "prop: %d\n", psp);
+
+       ret = pm_runtime_get_sync(&mfi->udev->dev);
+       if (ret < 0)
+               return ret;
+
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               ret = apple_mfi_fc_set_charge_type(mfi, val);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       pm_runtime_mark_last_busy(&mfi->udev->dev);
+       pm_runtime_put_autosuspend(&mfi->udev->dev);
+
+       return ret;
+}
+
+static int apple_mfi_fc_property_is_writeable(struct power_supply *psy,
+                                             enum power_supply_property psp)
+{
+       switch (psp) {
+       case POWER_SUPPLY_PROP_CHARGE_TYPE:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static enum power_supply_property apple_mfi_fc_properties[] = {
+       POWER_SUPPLY_PROP_CHARGE_TYPE,
+       POWER_SUPPLY_PROP_SCOPE
+};
+
+static const struct power_supply_desc apple_mfi_fc_desc = {
+       .name                   = "apple_mfi_fastcharge",
+       .type                   = POWER_SUPPLY_TYPE_BATTERY,
+       .properties             = apple_mfi_fc_properties,
+       .num_properties         = ARRAY_SIZE(apple_mfi_fc_properties),
+       .get_property           = apple_mfi_fc_get_property,
+       .set_property           = apple_mfi_fc_set_property,
+       .property_is_writeable  = apple_mfi_fc_property_is_writeable
+};
+
+static int mfi_fc_probe(struct usb_device *udev)
+{
+       struct power_supply_config battery_cfg = {};
+       struct mfi_device *mfi = NULL;
+       int err, idProduct;
+
+       idProduct = le16_to_cpu(udev->descriptor.idProduct);
+       /* See comment above mfi_fc_id_table[] */
+       if (idProduct < 0x1200 || idProduct > 0x12ff) {
+               return -ENODEV;
+       }
+
+       mfi = kzalloc(sizeof(struct mfi_device), GFP_KERNEL);
+       if (!mfi) {
+               err = -ENOMEM;
+               goto error;
+       }
+
+       battery_cfg.drv_data = mfi;
+
+       mfi->charge_type = POWER_SUPPLY_CHARGE_TYPE_TRICKLE;
+       mfi->battery = power_supply_register(&udev->dev,
+                                               &apple_mfi_fc_desc,
+                                               &battery_cfg);
+       if (IS_ERR(mfi->battery)) {
+               dev_err(&udev->dev, "Can't register battery\n");
+               err = PTR_ERR(mfi->battery);
+               goto error;
+       }
+
+       mfi->udev = usb_get_dev(udev);
+       dev_set_drvdata(&udev->dev, mfi);
+
+       return 0;
+
+error:
+       kfree(mfi);
+       return err;
+}
+
+static void mfi_fc_disconnect(struct usb_device *udev)
+{
+       struct mfi_device *mfi;
+
+       mfi = dev_get_drvdata(&udev->dev);
+       if (mfi->battery)
+               power_supply_unregister(mfi->battery);
+       dev_set_drvdata(&udev->dev, NULL);
+       usb_put_dev(mfi->udev);
+       kfree(mfi);
+}
+
+static struct usb_device_driver mfi_fc_driver = {
+       .name =         "apple-mfi-fastcharge",
+       .probe =        mfi_fc_probe,
+       .disconnect =   mfi_fc_disconnect,
+       .id_table =     mfi_fc_id_table,
+       .generic_subclass = 1,
+};
+
+static int __init mfi_fc_driver_init(void)
+{
+       return usb_register_device_driver(&mfi_fc_driver, THIS_MODULE);
+}
+
+static void __exit mfi_fc_driver_exit(void)
+{
+       usb_deregister_device_driver(&mfi_fc_driver);
+}
+
+module_init(mfi_fc_driver_init);
+module_exit(mfi_fc_driver_exit);
index bc5ecd5ff56554e2fd02dd2455b833b9fbd0b8d7..39cb141646526dbc362e71890b6494d677f84f62 100644 (file)
@@ -414,7 +414,7 @@ static ssize_t mon_text_read_t(struct file *file, char __user *buf,
 
                mon_text_read_head_t(rp, &ptr, ep);
                mon_text_read_statset(rp, &ptr, ep);
-               ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+               ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
                    " %d", ep->length);
                mon_text_read_data(rp, &ptr, ep);
 
@@ -462,7 +462,7 @@ static ssize_t mon_text_read_u(struct file *file, char __user *buf,
                } else {
                        mon_text_read_statset(rp, &ptr, ep);
                }
-               ptr.cnt += snprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
+               ptr.cnt += scnprintf(ptr.pbuf + ptr.cnt, ptr.limit - ptr.cnt,
                    " %d", ep->length);
                mon_text_read_data(rp, &ptr, ep);
 
@@ -520,7 +520,7 @@ static void mon_text_read_head_t(struct mon_reader_text *rp,
        case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break;
        default: /* PIPE_BULK */  utype = 'B';
        }
-       p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+       p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
            "%lx %u %c %c%c:%03u:%02u",
            ep->id, ep->tstamp, ep->type,
            utype, udir, ep->devnum, ep->epnum);
@@ -538,7 +538,7 @@ static void mon_text_read_head_u(struct mon_reader_text *rp,
        case USB_ENDPOINT_XFER_CONTROL: utype = 'C'; break;
        default: /* PIPE_BULK */  utype = 'B';
        }
-       p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+       p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
            "%lx %u %c %c%c:%d:%03u:%u",
            ep->id, ep->tstamp, ep->type,
            utype, udir, ep->busnum, ep->devnum, ep->epnum);
@@ -549,7 +549,7 @@ static void mon_text_read_statset(struct mon_reader_text *rp,
 {
 
        if (ep->setup_flag == 0) {   /* Setup packet is present and captured */
-               p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+               p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                    " s %02x %02x %04x %04x %04x",
                    ep->setup[0],
                    ep->setup[1],
@@ -557,10 +557,10 @@ static void mon_text_read_statset(struct mon_reader_text *rp,
                    (ep->setup[5] << 8) | ep->setup[4],
                    (ep->setup[7] << 8) | ep->setup[6]);
        } else if (ep->setup_flag != '-') { /* Unable to capture setup packet */
-               p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+               p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                    " %c __ __ ____ ____ ____", ep->setup_flag);
        } else {                     /* No setup for this kind of URB */
-               p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+               p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                    " %d", ep->status);
        }
 }
@@ -568,7 +568,7 @@ static void mon_text_read_statset(struct mon_reader_text *rp,
 static void mon_text_read_intstat(struct mon_reader_text *rp,
        struct mon_text_ptr *p, const struct mon_event_text *ep)
 {
-       p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+       p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
            " %d:%d", ep->status, ep->interval);
 }
 
@@ -576,10 +576,10 @@ static void mon_text_read_isostat(struct mon_reader_text *rp,
        struct mon_text_ptr *p, const struct mon_event_text *ep)
 {
        if (ep->type == 'S') {
-               p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+               p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                    " %d:%d:%d", ep->status, ep->interval, ep->start_frame);
        } else {
-               p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+               p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                    " %d:%d:%d:%d",
                    ep->status, ep->interval, ep->start_frame, ep->error_count);
        }
@@ -592,7 +592,7 @@ static void mon_text_read_isodesc(struct mon_reader_text *rp,
        int i;
        const struct mon_iso_desc *dp;
 
-       p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+       p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
            " %d", ep->numdesc);
        ndesc = ep->numdesc;
        if (ndesc > ISODESC_MAX)
@@ -601,7 +601,7 @@ static void mon_text_read_isodesc(struct mon_reader_text *rp,
                ndesc = 0;
        dp = ep->isodesc;
        for (i = 0; i < ndesc; i++) {
-               p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+               p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                    " %d:%u:%u", dp->status, dp->offset, dp->length);
                dp++;
        }
@@ -614,28 +614,28 @@ static void mon_text_read_data(struct mon_reader_text *rp,
 
        if ((data_len = ep->length) > 0) {
                if (ep->data_flag == 0) {
-                       p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+                       p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                            " =");
                        if (data_len >= DATA_MAX)
                                data_len = DATA_MAX;
                        for (i = 0; i < data_len; i++) {
                                if (i % 4 == 0) {
-                                       p->cnt += snprintf(p->pbuf + p->cnt,
+                                       p->cnt += scnprintf(p->pbuf + p->cnt,
                                            p->limit - p->cnt,
                                            " ");
                                }
-                               p->cnt += snprintf(p->pbuf + p->cnt,
+                               p->cnt += scnprintf(p->pbuf + p->cnt,
                                    p->limit - p->cnt,
                                    "%02x", ep->data[i]);
                        }
-                       p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+                       p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                            "\n");
                } else {
-                       p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt,
+                       p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt,
                            " %c\n", ep->data_flag);
                }
        } else {
-               p->cnt += snprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
+               p->cnt += scnprintf(p->pbuf + p->cnt, p->limit - p->cnt, "\n");
        }
 }
 
index 08e18448e8b82777f361d9080085f4abc129cc6b..04f666e85731226d35fef1e1aea1af56c86d81cc 100644 (file)
@@ -320,9 +320,9 @@ void ssusb_set_force_mode(struct ssusb_mtk *ssusb,
        mtu3_writel(ssusb->ippc_base, SSUSB_U2_CTRL(0), value);
 }
 
-static int ssusb_role_sw_set(struct device *dev, enum usb_role role)
+static int ssusb_role_sw_set(struct usb_role_switch *sw, enum usb_role role)
 {
-       struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+       struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
        bool to_host = false;
 
        if (role == USB_ROLE_HOST)
@@ -334,9 +334,9 @@ static int ssusb_role_sw_set(struct device *dev, enum usb_role role)
        return 0;
 }
 
-static enum usb_role ssusb_role_sw_get(struct device *dev)
+static enum usb_role ssusb_role_sw_get(struct usb_role_switch *sw)
 {
-       struct ssusb_mtk *ssusb = dev_get_drvdata(dev);
+       struct ssusb_mtk *ssusb = usb_role_switch_get_drvdata(sw);
        enum usb_role role;
 
        role = ssusb->is_host ? USB_ROLE_HOST : USB_ROLE_DEVICE;
@@ -356,6 +356,7 @@ static int ssusb_role_sw_register(struct otg_switch_mtk *otg_sx)
        role_sx_desc.set = ssusb_role_sw_set;
        role_sx_desc.get = ssusb_role_sw_get;
        role_sx_desc.fwnode = dev_fwnode(ssusb->dev);
+       role_sx_desc.driver_data = ssusb;
        otg_sx->role_sw = usb_role_switch_register(ssusb->dev, &role_sx_desc);
 
        return PTR_ERR_OR_ZERO(otg_sx->role_sw);
index eb2ded1026ee8ddbaedbf2f1618e2428bd9bf2c7..3b0d1c20ebe67bf737e23882e4127906fc6c9515 100644 (file)
@@ -110,9 +110,11 @@ config USB_MUSB_UX500
 
 config USB_MUSB_JZ4740
        tristate "JZ4740"
+       depends on OF
        depends on MIPS || COMPILE_TEST
        depends on USB_MUSB_GADGET
        depends on USB=n || USB_OTG_BLACKLIST_HUB
+       select USB_ROLE_SWITCH
 
 config USB_MUSB_MEDIATEK
        tristate "MediaTek platforms"
@@ -144,7 +146,7 @@ config USB_UX500_DMA
 
 config USB_INVENTRA_DMA
        bool 'Inventra'
-       depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK
+       depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740
        help
          Enable DMA transfers using Mentor's engine.
 
index bc0109f4700b4cdf393d9490a29a7147fb9336aa..e64dd30e80e734114074309993012730d851d6ae 100644 (file)
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/usb/role.h>
 #include <linux/usb/usb_phy_generic.h>
 
 #include "musb_core.h"
 
 struct jz4740_glue {
        struct platform_device  *pdev;
+       struct musb             *musb;
        struct clk              *clk;
+       struct usb_role_switch  *role_sw;
 };
 
 static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
 {
        unsigned long   flags;
-       irqreturn_t     retval = IRQ_NONE;
+       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);
+
        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);
@@ -46,7 +52,10 @@ static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
 
        spin_unlock_irqrestore(&musb->lock, flags);
 
-       return retval;
+       if (retval == IRQ_HANDLED || retval_dma == IRQ_HANDLED)
+               return IRQ_HANDLED;
+
+       return IRQ_NONE;
 }
 
 static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
@@ -66,11 +75,40 @@ static const struct musb_hdrc_config jz4740_musb_config = {
        .fifo_cfg_size  = ARRAY_SIZE(jz4740_musb_fifo_cfg),
 };
 
+static int jz4740_musb_role_switch_set(struct usb_role_switch *sw,
+                                      enum usb_role role)
+{
+       struct jz4740_glue *glue = usb_role_switch_get_drvdata(sw);
+       struct usb_phy *phy = glue->musb->xceiv;
+
+       switch (role) {
+       case USB_ROLE_NONE:
+               atomic_notifier_call_chain(&phy->notifier, USB_EVENT_NONE, phy);
+               break;
+       case USB_ROLE_DEVICE:
+               atomic_notifier_call_chain(&phy->notifier, USB_EVENT_VBUS, phy);
+               break;
+       case USB_ROLE_HOST:
+               atomic_notifier_call_chain(&phy->notifier, USB_EVENT_ID, phy);
+               break;
+       }
+
+       return 0;
+}
+
 static int jz4740_musb_init(struct musb *musb)
 {
        struct device *dev = musb->controller->parent;
+       struct jz4740_glue *glue = dev_get_drvdata(dev);
+       struct usb_role_switch_desc role_sw_desc = {
+               .set = jz4740_musb_role_switch_set,
+               .driver_data = glue,
+               .fwnode = dev_fwnode(dev),
+       };
        int err;
 
+       glue->musb = musb;
+
        if (dev->of_node)
                musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
        else
@@ -82,6 +120,12 @@ static int jz4740_musb_init(struct musb *musb)
                return err;
        }
 
+       glue->role_sw = usb_role_switch_register(dev, &role_sw_desc);
+       if (IS_ERR(glue->role_sw)) {
+               dev_err(dev, "Failed to register USB role switch");
+               return PTR_ERR(glue->role_sw);
+       }
+
        /*
         * Silicon does not implement ConfigData register.
         * Set dyn_fifo to avoid reading EP config from hardware.
@@ -93,14 +137,24 @@ static int jz4740_musb_init(struct musb *musb)
        return 0;
 }
 
-/*
- * DMA has not been confirmed to work with CONFIG_USB_INVENTRA_DMA,
- * so let's not set up the dma function pointers yet.
- */
+static int jz4740_musb_exit(struct musb *musb)
+{
+       struct jz4740_glue *glue = dev_get_drvdata(musb->controller->parent);
+
+       usb_role_switch_unregister(glue->role_sw);
+
+       return 0;
+}
+
 static const struct musb_platform_ops jz4740_musb_ops = {
        .quirks         = MUSB_DMA_INVENTRA | MUSB_INDEXED_EP,
        .fifo_mode      = 2,
        .init           = jz4740_musb_init,
+       .exit           = jz4740_musb_exit,
+#ifdef CONFIG_USB_INVENTRA_DMA
+       .dma_init       = musbhs_dma_controller_create_noirq,
+       .dma_exit       = musbhs_dma_controller_destroy,
+#endif
 };
 
 static const struct musb_hdrc_platform_data jz4740_musb_pdata = {
@@ -109,10 +163,37 @@ static const struct musb_hdrc_platform_data jz4740_musb_pdata = {
        .platform_ops   = &jz4740_musb_ops,
 };
 
+static struct musb_fifo_cfg jz4770_musb_fifo_cfg[] = {
+       { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+       { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+       { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, },
+       { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, },
+       { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, },
+       { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, },
+       { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 512, },
+       { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 512, },
+       { .hw_ep_num = 5, .style = FIFO_TX, .maxpacket = 512, },
+       { .hw_ep_num = 5, .style = FIFO_RX, .maxpacket = 512, },
+};
+
+static struct musb_hdrc_config jz4770_musb_config = {
+       .multipoint     = 1,
+       .num_eps        = 11,
+       .ram_bits       = 11,
+       .fifo_cfg       = jz4770_musb_fifo_cfg,
+       .fifo_cfg_size  = ARRAY_SIZE(jz4770_musb_fifo_cfg),
+};
+
+static const struct musb_hdrc_platform_data jz4770_musb_pdata = {
+       .mode           = MUSB_PERIPHERAL, /* TODO: support OTG */
+       .config         = &jz4770_musb_config,
+       .platform_ops   = &jz4740_musb_ops,
+};
+
 static int jz4740_probe(struct platform_device *pdev)
 {
        struct device                   *dev = &pdev->dev;
-       const struct musb_hdrc_platform_data *pdata = &jz4740_musb_pdata;
+       const struct musb_hdrc_platform_data *pdata;
        struct platform_device          *musb;
        struct jz4740_glue              *glue;
        struct clk                      *clk;
@@ -122,6 +203,12 @@ static int jz4740_probe(struct platform_device *pdev)
        if (!glue)
                return -ENOMEM;
 
+       pdata = of_device_get_match_data(dev);
+       if (!pdata) {
+               dev_err(dev, "missing platform data");
+               return -EINVAL;
+       }
+
        musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
        if (!musb) {
                dev_err(dev, "failed to allocate musb device");
@@ -142,6 +229,8 @@ static int jz4740_probe(struct platform_device *pdev)
        }
 
        musb->dev.parent                = dev;
+       musb->dev.dma_mask              = &musb->dev.coherent_dma_mask;
+       musb->dev.coherent_dma_mask     = DMA_BIT_MASK(32);
 
        glue->pdev                      = musb;
        glue->clk                       = clk;
@@ -186,20 +275,19 @@ static int jz4740_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_OF
 static const struct of_device_id jz4740_musb_of_match[] = {
-       { .compatible = "ingenic,jz4740-musb" },
+       { .compatible = "ingenic,jz4740-musb", .data = &jz4740_musb_pdata },
+       { .compatible = "ingenic,jz4770-musb", .data = &jz4770_musb_pdata },
        { /* sentinel */ },
 };
 MODULE_DEVICE_TABLE(of, jz4740_musb_of_match);
-#endif
 
 static struct platform_driver jz4740_driver = {
        .probe          = jz4740_probe,
        .remove         = jz4740_remove,
        .driver         = {
                .name   = "musb-jz4740",
-               .of_match_table = of_match_ptr(jz4740_musb_of_match),
+               .of_match_table = jz4740_musb_of_match,
        },
 };
 
index 6b88c2f5d970413bb76134fddce62bce5689dbb6..6196b0e8d77d2016d6746be91f289134da37f5b4 100644 (file)
@@ -115,9 +115,8 @@ static void mtk_musb_clks_disable(struct mtk_glue *glue)
        clk_disable_unprepare(glue->main);
 }
 
-static int musb_usb_role_sx_set(struct device *dev, enum usb_role role)
+static int mtk_otg_switch_set(struct mtk_glue *glue, enum usb_role role)
 {
-       struct mtk_glue *glue = dev_get_drvdata(dev);
        struct musb *musb = glue->musb;
        u8 devctl = readb(musb->mregs + MUSB_DEVCTL);
        enum usb_role new_role;
@@ -168,9 +167,14 @@ static int musb_usb_role_sx_set(struct device *dev, enum usb_role role)
        return 0;
 }
 
-static enum usb_role musb_usb_role_sx_get(struct device *dev)
+static int musb_usb_role_sx_set(struct usb_role_switch *sw, enum usb_role role)
 {
-       struct mtk_glue *glue = dev_get_drvdata(dev);
+       return mtk_otg_switch_set(usb_role_switch_get_drvdata(sw), role);
+}
+
+static enum usb_role musb_usb_role_sx_get(struct usb_role_switch *sw)
+{
+       struct mtk_glue *glue = usb_role_switch_get_drvdata(sw);
 
        return glue->role;
 }
@@ -182,6 +186,7 @@ static int mtk_otg_switch_init(struct mtk_glue *glue)
        role_sx_desc.set = musb_usb_role_sx_set;
        role_sx_desc.get = musb_usb_role_sx_get;
        role_sx_desc.fwnode = dev_fwnode(glue->dev);
+       role_sx_desc.driver_data = glue;
        glue->role_sw = usb_role_switch_register(glue->dev, &role_sx_desc);
 
        return PTR_ERR_OR_ZERO(glue->role_sw);
@@ -288,8 +293,7 @@ static int mtk_musb_set_mode(struct musb *musb, u8 mode)
                return -EINVAL;
        }
 
-       glue->role = new_role;
-       musb_usb_role_sx_set(dev, glue->role);
+       mtk_otg_switch_set(glue, new_role);
        return 0;
 }
 
@@ -444,7 +448,7 @@ static int mtk_musb_probe(struct platform_device *pdev)
        struct platform_device_info pinfo;
        struct device *dev = &pdev->dev;
        struct device_node *np = dev->of_node;
-       int ret = -ENOMEM;
+       int ret;
 
        glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
        if (!glue)
index f616fb4895429d4e680966db6c0167a7f5ed0335..d590110539ab3c5219079b5efab598ebb7068094 100644 (file)
@@ -2945,7 +2945,7 @@ static const struct dev_pm_ops musb_dev_pm_ops = {
 
 static struct platform_driver musb_driver = {
        .driver = {
-               .name           = (char *)musb_driver_name,
+               .name           = musb_driver_name,
                .bus            = &platform_bus_type,
                .pm             = MUSB_DEV_PM_OPS,
                .dev_groups     = musb_groups,
index 886c9b602f8cf33ca594e7ae3c897cf62c57d52e..8736f4251a222e31550cd9e04b0396a6300f8e15 100644 (file)
@@ -1436,10 +1436,7 @@ done:
         * We need to map sg if the transfer_buffer is
         * NULL.
         */
-       if (!urb->transfer_buffer)
-               qh->use_sg = true;
-
-       if (qh->use_sg) {
+       if (!urb->transfer_buffer) {
                /* sg_miter_start is already done in musb_ep_program */
                if (!sg_miter_next(&qh->sg_miter)) {
                        dev_err(musb->controller, "error: sg list empty\n");
@@ -1447,9 +1444,8 @@ done:
                        status = -EINVAL;
                        goto done;
                }
-               urb->transfer_buffer = qh->sg_miter.addr;
                length = min_t(u32, length, qh->sg_miter.length);
-               musb_write_fifo(hw_ep, length, urb->transfer_buffer);
+               musb_write_fifo(hw_ep, length, qh->sg_miter.addr);
                qh->sg_miter.consumed = length;
                sg_miter_stop(&qh->sg_miter);
        } else {
@@ -1458,11 +1454,6 @@ done:
 
        qh->segsize = length;
 
-       if (qh->use_sg) {
-               if (offset + length >= urb->transfer_buffer_length)
-                       qh->use_sg = false;
-       }
-
        musb_ep_select(mbase, epnum);
        musb_writew(epio, MUSB_TXCSR,
                        MUSB_TXCSR_H_WZC_BITS | MUSB_TXCSR_TXPKTRDY);
@@ -1977,8 +1968,10 @@ finish:
        urb->actual_length += xfer_len;
        qh->offset += xfer_len;
        if (done) {
-               if (qh->use_sg)
+               if (qh->use_sg) {
                        qh->use_sg = false;
+                       urb->transfer_buffer = NULL;
+               }
 
                if (urb->status == -EINPROGRESS)
                        urb->status = status;
@@ -2550,7 +2543,7 @@ static int musb_bus_resume(struct usb_hcd *hcd)
 struct musb_temp_buffer {
        void *kmalloc_ptr;
        void *old_xfer_buffer;
-       u8 data[0];
+       u8 data[];
 };
 
 static void musb_free_temp_buffer(struct urb *urb)
index 5d449089e3ad64b39483db75ff0333c90a221386..99890d1bbfcb54dbbbba7e7c9f0a9c6792cf0222 100644 (file)
@@ -156,7 +156,7 @@ static u8 tusb_readb(void __iomem *addr, u32 offset)
        return val;
 }
 
-static void tusb_writeb(void __iomem *addr, unsigned offset, u8 data)
+static void tusb_writeb(void __iomem *addr, u32 offset, u8 data)
 {
        u16 tmp;
 
index ff24fca0a2d9d38f0d235aba4daa771dcc51dda8..4b3fa78995cf2ff189b64458d9243e8181800076 100644 (file)
@@ -184,4 +184,12 @@ config USB_ULPI_VIEWPORT
          Provides read/write operations to the ULPI phy register set for
          controllers with a viewport register (e.g. Chipidea/ARC controllers).
 
+config JZ4770_PHY
+       tristate "Ingenic JZ4770 Transceiver Driver"
+       depends on MIPS || COMPILE_TEST
+       select USB_PHY
+       help
+         This driver provides PHY support for the USB controller found
+         on the JZ4770 SoC from Ingenic.
+
 endmenu
index df1d99010079fa69dbcdbf5d64c99c8dd7ac8195..b352bdbe871214bfb3da11a85f73daae7a9bd736 100644 (file)
@@ -24,3 +24,4 @@ obj-$(CONFIG_USB_MXS_PHY)             += phy-mxs-usb.o
 obj-$(CONFIG_USB_ULPI)                 += phy-ulpi.o
 obj-$(CONFIG_USB_ULPI_VIEWPORT)                += phy-ulpi-viewport.o
 obj-$(CONFIG_KEYSTONE_USB_PHY)         += phy-keystone.o
+obj-$(CONFIG_JZ4770_PHY)               += phy-jz4770.o
diff --git a/drivers/usb/phy/phy-jz4770.c b/drivers/usb/phy/phy-jz4770.c
new file mode 100644 (file)
index 0000000..3ea1f5b
--- /dev/null
@@ -0,0 +1,243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ingenic JZ4770 USB PHY driver
+ * Copyright (c) Paul Cercueil <paul@crapouillou.net>
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/phy.h>
+
+#define REG_USBPCR_OFFSET      0x00
+#define REG_USBRDT_OFFSET      0x04
+#define REG_USBVBFIL_OFFSET    0x08
+#define REG_USBPCR1_OFFSET     0x0c
+
+/* USBPCR */
+#define USBPCR_USB_MODE                BIT(31)
+#define USBPCR_AVLD_REG                BIT(30)
+#define USBPCR_INCRM           BIT(27)
+#define USBPCR_CLK12_EN                BIT(26)
+#define USBPCR_COMMONONN       BIT(25)
+#define USBPCR_VBUSVLDEXT      BIT(24)
+#define USBPCR_VBUSVLDEXTSEL   BIT(23)
+#define USBPCR_POR             BIT(22)
+#define USBPCR_SIDDQ           BIT(21)
+#define USBPCR_OTG_DISABLE     BIT(20)
+#define USBPCR_TXPREEMPHTUNE   BIT(6)
+
+#define USBPCR_IDPULLUP_LSB    28
+#define USBPCR_IDPULLUP_MASK   GENMASK(29, USBPCR_IDPULLUP_LSB)
+#define USBPCR_IDPULLUP_ALWAYS (3 << USBPCR_IDPULLUP_LSB)
+#define USBPCR_IDPULLUP_SUSPEND        (1 << USBPCR_IDPULLUP_LSB)
+#define USBPCR_IDPULLUP_OTG    (0 << USBPCR_IDPULLUP_LSB)
+
+#define USBPCR_COMPDISTUNE_LSB 17
+#define USBPCR_COMPDISTUNE_MASK        GENMASK(19, USBPCR_COMPDISTUNE_LSB)
+#define USBPCR_COMPDISTUNE_DFT 4
+
+#define USBPCR_OTGTUNE_LSB     14
+#define USBPCR_OTGTUNE_MASK    GENMASK(16, USBPCR_OTGTUNE_LSB)
+#define USBPCR_OTGTUNE_DFT     4
+
+#define USBPCR_SQRXTUNE_LSB    11
+#define USBPCR_SQRXTUNE_MASK   GENMASK(13, USBPCR_SQRXTUNE_LSB)
+#define USBPCR_SQRXTUNE_DFT    3
+
+#define USBPCR_TXFSLSTUNE_LSB  7
+#define USBPCR_TXFSLSTUNE_MASK GENMASK(10, USBPCR_TXFSLSTUNE_LSB)
+#define USBPCR_TXFSLSTUNE_DFT  3
+
+#define USBPCR_TXRISETUNE_LSB  4
+#define USBPCR_TXRISETUNE_MASK GENMASK(5, USBPCR_TXRISETUNE_LSB)
+#define USBPCR_TXRISETUNE_DFT  3
+
+#define USBPCR_TXVREFTUNE_LSB  0
+#define USBPCR_TXVREFTUNE_MASK GENMASK(3, USBPCR_TXVREFTUNE_LSB)
+#define USBPCR_TXVREFTUNE_DFT  5
+
+/* USBRDT */
+#define USBRDT_VBFIL_LD_EN     BIT(25)
+#define USBRDT_IDDIG_EN                BIT(24)
+#define USBRDT_IDDIG_REG       BIT(23)
+
+#define USBRDT_USBRDT_LSB      0
+#define USBRDT_USBRDT_MASK     GENMASK(22, USBRDT_USBRDT_LSB)
+
+/* USBPCR1 */
+#define USBPCR1_UHC_POWON      BIT(5)
+
+struct jz4770_phy {
+       struct usb_phy phy;
+       struct usb_otg otg;
+       struct device *dev;
+       void __iomem *base;
+       struct clk *clk;
+       struct regulator *vcc_supply;
+};
+
+static inline struct jz4770_phy *otg_to_jz4770_phy(struct usb_otg *otg)
+{
+       return container_of(otg, struct jz4770_phy, otg);
+}
+
+static inline struct jz4770_phy *phy_to_jz4770_phy(struct usb_phy *phy)
+{
+       return container_of(phy, struct jz4770_phy, phy);
+}
+
+static int jz4770_phy_set_peripheral(struct usb_otg *otg,
+                                    struct usb_gadget *gadget)
+{
+       struct jz4770_phy *priv = otg_to_jz4770_phy(otg);
+       u32 reg;
+
+       reg = readl(priv->base + REG_USBPCR_OFFSET);
+       reg &= ~USBPCR_USB_MODE;
+       reg |= USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE;
+       writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+       return 0;
+}
+
+static int jz4770_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+       struct jz4770_phy *priv = otg_to_jz4770_phy(otg);
+       u32 reg;
+
+       reg = readl(priv->base + REG_USBPCR_OFFSET);
+       reg &= ~(USBPCR_VBUSVLDEXT | USBPCR_VBUSVLDEXTSEL | USBPCR_OTG_DISABLE);
+       reg |= USBPCR_USB_MODE;
+       writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+       return 0;
+}
+
+static int jz4770_phy_init(struct usb_phy *phy)
+{
+       struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+       int err;
+       u32 reg;
+
+       err = regulator_enable(priv->vcc_supply);
+       if (err) {
+               dev_err(priv->dev, "Unable to enable VCC: %d", err);
+               return err;
+       }
+
+       err = clk_prepare_enable(priv->clk);
+       if (err) {
+               dev_err(priv->dev, "Unable to start clock: %d", err);
+               return err;
+       }
+
+       reg = USBPCR_AVLD_REG | USBPCR_COMMONONN | USBPCR_IDPULLUP_ALWAYS |
+               (USBPCR_COMPDISTUNE_DFT << USBPCR_COMPDISTUNE_LSB) |
+               (USBPCR_OTGTUNE_DFT << USBPCR_OTGTUNE_LSB) |
+               (USBPCR_SQRXTUNE_DFT << USBPCR_SQRXTUNE_LSB) |
+               (USBPCR_TXFSLSTUNE_DFT << USBPCR_TXFSLSTUNE_LSB) |
+               (USBPCR_TXRISETUNE_DFT << USBPCR_TXRISETUNE_LSB) |
+               (USBPCR_TXVREFTUNE_DFT << USBPCR_TXVREFTUNE_LSB) |
+               USBPCR_POR;
+       writel(reg, priv->base + REG_USBPCR_OFFSET);
+
+       /* Wait for PHY to reset */
+       usleep_range(30, 300);
+       writel(reg & ~USBPCR_POR, priv->base + REG_USBPCR_OFFSET);
+       usleep_range(300, 1000);
+
+       return 0;
+}
+
+static void jz4770_phy_shutdown(struct usb_phy *phy)
+{
+       struct jz4770_phy *priv = phy_to_jz4770_phy(phy);
+
+       clk_disable_unprepare(priv->clk);
+       regulator_disable(priv->vcc_supply);
+}
+
+static void jz4770_phy_remove(void *phy)
+{
+       usb_remove_phy(phy);
+}
+
+static int jz4770_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct jz4770_phy *priv;
+       int err;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       platform_set_drvdata(pdev, priv);
+       priv->dev = dev;
+       priv->phy.dev = dev;
+       priv->phy.otg = &priv->otg;
+       priv->phy.label = "jz4770-phy";
+       priv->phy.init = jz4770_phy_init;
+       priv->phy.shutdown = jz4770_phy_shutdown;
+
+       priv->otg.state = OTG_STATE_UNDEFINED;
+       priv->otg.usb_phy = &priv->phy;
+       priv->otg.set_host = jz4770_phy_set_host;
+       priv->otg.set_peripheral = jz4770_phy_set_peripheral;
+
+       priv->base = devm_platform_ioremap_resource(pdev, 0);
+       if (IS_ERR(priv->base)) {
+               dev_err(dev, "Failed to map registers");
+               return PTR_ERR(priv->base);
+       }
+
+       priv->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(priv->clk)) {
+               err = PTR_ERR(priv->clk);
+               if (err != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to get clock");
+               return err;
+       }
+
+       priv->vcc_supply = devm_regulator_get(dev, "vcc");
+       if (IS_ERR(priv->vcc_supply)) {
+               err = PTR_ERR(priv->vcc_supply);
+               if (err != -EPROBE_DEFER)
+                       dev_err(dev, "failed to get regulator");
+               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");
+               return err;
+       }
+
+       return devm_add_action_or_reset(dev, jz4770_phy_remove, &priv->phy);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id jz4770_phy_of_matches[] = {
+       { .compatible = "ingenic,jz4770-phy" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, jz4770_phy_of_matches);
+#endif
+
+static struct platform_driver jz4770_phy_driver = {
+       .probe          = jz4770_phy_probe,
+       .driver         = {
+               .name   = "jz4770-phy",
+               .of_match_table = of_match_ptr(jz4770_phy_of_matches),
+       },
+};
+module_platform_driver(jz4770_phy_driver);
+
+MODULE_AUTHOR("Paul Cercueil <paul@crapouillou.net>");
+MODULE_DESCRIPTION("Ingenic JZ4770 USB PHY driver");
+MODULE_LICENSE("GPL");
index 6153cc35aba0d71cbedaff59f8ae40ae825f43c6..cffe2aced48848f93497f0ae9e7d8455c24e3ac0 100644 (file)
 #include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/export.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/iopoll.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_gpio.h>
 #include <linux/platform_device.h>
 #include <linux/resource.h>
 #include <linux/slab.h>
index 63a00ff26655e3e4614ce9e745b40d0ee4d03b39..5b17709821dfd343f2b1d0361d6e05a401ff12dd 100644 (file)
@@ -48,7 +48,7 @@ int usb_role_switch_set_role(struct usb_role_switch *sw, enum usb_role role)
 
        mutex_lock(&sw->lock);
 
-       ret = sw->set(sw->dev.parent, role);
+       ret = sw->set(sw, role);
        if (!ret)
                sw->role = role;
 
@@ -75,7 +75,7 @@ enum usb_role usb_role_switch_get_role(struct usb_role_switch *sw)
        mutex_lock(&sw->lock);
 
        if (sw->get)
-               role = sw->get(sw->dev.parent);
+               role = sw->get(sw);
        else
                role = sw->role;
 
@@ -199,7 +199,7 @@ EXPORT_SYMBOL_GPL(usb_role_switch_find_by_fwnode);
 static umode_t
 usb_role_switch_is_visible(struct kobject *kobj, struct attribute *attr, int n)
 {
-       struct device *dev = container_of(kobj, typeof(*dev), kobj);
+       struct device *dev = kobj_to_dev(kobj);
        struct usb_role_switch *sw = to_role_switch(dev);
 
        if (sw->allow_userspace_control)
@@ -329,7 +329,9 @@ usb_role_switch_register(struct device *parent,
        sw->dev.fwnode = desc->fwnode;
        sw->dev.class = role_class;
        sw->dev.type = &usb_role_dev_type;
-       dev_set_name(&sw->dev, "%s-role-switch", dev_name(parent));
+       dev_set_drvdata(&sw->dev, desc->driver_data);
+       dev_set_name(&sw->dev, "%s-role-switch",
+                    desc->name ? desc->name : dev_name(parent));
 
        ret = device_register(&sw->dev);
        if (ret) {
@@ -356,6 +358,27 @@ void usb_role_switch_unregister(struct usb_role_switch *sw)
 }
 EXPORT_SYMBOL_GPL(usb_role_switch_unregister);
 
+/**
+ * usb_role_switch_set_drvdata - Assign private data pointer to a switch
+ * @sw: USB Role Switch
+ * @data: Private data pointer
+ */
+void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data)
+{
+       dev_set_drvdata(&sw->dev, data);
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_set_drvdata);
+
+/**
+ * usb_role_switch_get_drvdata - Get the private data pointer of a switch
+ * @sw: USB Role Switch
+ */
+void *usb_role_switch_get_drvdata(struct usb_role_switch *sw)
+{
+       return dev_get_drvdata(&sw->dev);
+}
+EXPORT_SYMBOL_GPL(usb_role_switch_get_drvdata);
+
 static int __init usb_roles_init(void)
 {
        role_class = class_create(THIS_MODULE, "usb_role");
index 80d6559bbcb2832a27c712aed701a9eb074f4482..5c96e929acea0ec490e3cb1719440d9592ffcf26 100644 (file)
@@ -42,6 +42,7 @@
 #define DRV_NAME                       "intel_xhci_usb_sw"
 
 struct intel_xhci_usb_data {
+       struct device *dev;
        struct usb_role_switch *role_sw;
        void __iomem *base;
        bool enable_sw_switch;
@@ -51,9 +52,10 @@ static const struct software_node intel_xhci_usb_node = {
        "intel-xhci-usb-sw",
 };
 
-static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
+static int intel_xhci_usb_set_role(struct usb_role_switch *sw,
+                                  enum usb_role role)
 {
-       struct intel_xhci_usb_data *data = dev_get_drvdata(dev);
+       struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw);
        unsigned long timeout;
        acpi_status status;
        u32 glk, val;
@@ -66,11 +68,11 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
         */
        status = acpi_acquire_global_lock(ACPI_WAIT_FOREVER, &glk);
        if (ACPI_FAILURE(status) && status != AE_NOT_CONFIGURED) {
-               dev_err(dev, "Error could not acquire lock\n");
+               dev_err(data->dev, "Error could not acquire lock\n");
                return -EIO;
        }
 
-       pm_runtime_get_sync(dev);
+       pm_runtime_get_sync(data->dev);
 
        /*
         * Set idpin value as requested.
@@ -112,7 +114,7 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
        do {
                val = readl(data->base + DUAL_ROLE_CFG1);
                if (!!(val & HOST_MODE) == (role == USB_ROLE_HOST)) {
-                       pm_runtime_put(dev);
+                       pm_runtime_put(data->dev);
                        return 0;
                }
 
@@ -120,21 +122,21 @@ static int intel_xhci_usb_set_role(struct device *dev, enum usb_role role)
                usleep_range(5000, 10000);
        } while (time_before(jiffies, timeout));
 
-       pm_runtime_put(dev);
+       pm_runtime_put(data->dev);
 
-       dev_warn(dev, "Timeout waiting for role-switch\n");
+       dev_warn(data->dev, "Timeout waiting for role-switch\n");
        return -ETIMEDOUT;
 }
 
-static enum usb_role intel_xhci_usb_get_role(struct device *dev)
+static enum usb_role intel_xhci_usb_get_role(struct usb_role_switch *sw)
 {
-       struct intel_xhci_usb_data *data = dev_get_drvdata(dev);
+       struct intel_xhci_usb_data *data = usb_role_switch_get_drvdata(sw);
        enum usb_role role;
        u32 val;
 
-       pm_runtime_get_sync(dev);
+       pm_runtime_get_sync(data->dev);
        val = readl(data->base + DUAL_ROLE_CFG0);
-       pm_runtime_put(dev);
+       pm_runtime_put(data->dev);
 
        if (!(val & SW_IDPIN))
                role = USB_ROLE_HOST;
@@ -175,7 +177,9 @@ static int intel_xhci_usb_probe(struct platform_device *pdev)
        sw_desc.get = intel_xhci_usb_get_role,
        sw_desc.allow_userspace_control = true,
        sw_desc.fwnode = software_node_fwnode(&intel_xhci_usb_node);
+       sw_desc.driver_data = data;
 
+       data->dev = dev;
        data->enable_sw_switch = !device_property_read_bool(dev,
                                                "sw_switch_disable");
 
index 578ebdd86520557efe7d491421fdea4e3cdc1f01..91055a191995fcf5f3044e7382b0060a5fa63bc6 100644 (file)
@@ -1472,7 +1472,7 @@ static int digi_read_oob_callback(struct urb *urb)
        struct usb_serial_port *port = urb->context;
        struct usb_serial *serial = port->serial;
        struct tty_struct *tty;
-       struct digi_port *priv = usb_get_serial_port_data(port);
+       struct digi_port *priv;
        unsigned char *buf = urb->transfer_buffer;
        int opcode, line, status, val;
        unsigned long flags;
index 43fa1f0716b7d759d113ffe9b951f85369b98c50..dcda7fb164b43059a59133d6b50b40123a6cfdca 100644 (file)
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0
 /*
  * Fintek F81232 USB to serial adaptor driver
+ * Fintek F81532A/534A/535/536 USB to 2/4/8/12 serial adaptor driver
  *
  * Copyright (C) 2012 Greg Kroah-Hartman (gregkh@linuxfoundation.org)
  * Copyright (C) 2012 Linux Foundation
 #include <linux/usb/serial.h>
 #include <linux/serial_reg.h>
 
-static const struct usb_device_id id_table[] = {
-       { USB_DEVICE(0x1934, 0x0706) },
+#define F81232_ID              \
+       { USB_DEVICE(0x1934, 0x0706) }  /* 1 port UART device */
+
+#define F81534A_SERIES_ID      \
+       { USB_DEVICE(0x2c42, 0x1602) }, /* In-Box 2 port UART device */ \
+       { USB_DEVICE(0x2c42, 0x1604) }, /* In-Box 4 port UART device */ \
+       { USB_DEVICE(0x2c42, 0x1605) }, /* In-Box 8 port UART device */ \
+       { USB_DEVICE(0x2c42, 0x1606) }, /* In-Box 12 port UART device */ \
+       { USB_DEVICE(0x2c42, 0x1608) }, /* Non-Flash type */ \
+       { USB_DEVICE(0x2c42, 0x1632) }, /* 2 port UART device */ \
+       { USB_DEVICE(0x2c42, 0x1634) }, /* 4 port UART device */ \
+       { USB_DEVICE(0x2c42, 0x1635) }, /* 8 port UART device */ \
+       { USB_DEVICE(0x2c42, 0x1636) }  /* 12 port UART device */
+
+#define F81534A_CTRL_ID                \
+       { USB_DEVICE(0x2c42, 0x16f8) }  /* Global control device */
+
+static const struct usb_device_id f81232_id_table[] = {
+       F81232_ID,
        { }                                     /* Terminating entry */
 };
-MODULE_DEVICE_TABLE(usb, id_table);
+
+static const struct usb_device_id f81534a_id_table[] = {
+       F81534A_SERIES_ID,
+       { }                                     /* Terminating entry */
+};
+
+static const struct usb_device_id f81534a_ctrl_id_table[] = {
+       F81534A_CTRL_ID,
+       { }                                     /* Terminating entry */
+};
+
+static const struct usb_device_id combined_id_table[] = {
+       F81232_ID,
+       F81534A_SERIES_ID,
+       F81534A_CTRL_ID,
+       { }                                     /* Terminating entry */
+};
+MODULE_DEVICE_TABLE(usb, combined_id_table);
 
 /* Maximum baudrate for F81232 */
 #define F81232_MAX_BAUDRATE            1500000
@@ -35,6 +70,7 @@ MODULE_DEVICE_TABLE(usb, id_table);
 #define F81232_REGISTER_REQUEST                0xa0
 #define F81232_GET_REGISTER            0xc0
 #define F81232_SET_REGISTER            0x40
+#define F81534A_ACCESS_REG_RETRY       2
 
 #define SERIAL_BASE_ADDRESS            0x0120
 #define RECEIVE_BUFFER_REGISTER                (0x00 + SERIAL_BASE_ADDRESS)
@@ -61,6 +97,22 @@ MODULE_DEVICE_TABLE(usb, id_table);
 #define F81232_CLK_14_77_MHZ           (BIT(1) | BIT(0))
 #define F81232_CLK_MASK                        GENMASK(1, 0)
 
+#define F81534A_MODE_REG               0x107
+#define F81534A_TRIGGER_MASK           GENMASK(3, 2)
+#define F81534A_TRIGGER_MULTIPLE_4X    BIT(3)
+#define F81534A_FIFO_128BYTE           (BIT(1) | BIT(0))
+
+/* Serial port self GPIO control, 2bytes [control&output data][input data] */
+#define F81534A_GPIO_REG               0x10e
+#define F81534A_GPIO_MODE2_DIR         BIT(6) /* 1: input, 0: output */
+#define F81534A_GPIO_MODE1_DIR         BIT(5)
+#define F81534A_GPIO_MODE0_DIR         BIT(4)
+#define F81534A_GPIO_MODE2_OUTPUT      BIT(2)
+#define F81534A_GPIO_MODE1_OUTPUT      BIT(1)
+#define F81534A_GPIO_MODE0_OUTPUT      BIT(0)
+
+#define F81534A_CTRL_CMD_ENABLE_PORT   0x116
+
 struct f81232_private {
        struct mutex lock;
        u8 modem_control;
@@ -322,10 +374,38 @@ exit:
                        __func__, retval);
 }
 
+static char f81232_handle_lsr(struct usb_serial_port *port, u8 lsr)
+{
+       struct f81232_private *priv = usb_get_serial_port_data(port);
+       char tty_flag = TTY_NORMAL;
+
+       if (!(lsr & UART_LSR_BRK_ERROR_BITS))
+               return tty_flag;
+
+       if (lsr & UART_LSR_BI) {
+               tty_flag = TTY_BREAK;
+               port->icount.brk++;
+               usb_serial_handle_break(port);
+       } else if (lsr & UART_LSR_PE) {
+               tty_flag = TTY_PARITY;
+               port->icount.parity++;
+       } else if (lsr & UART_LSR_FE) {
+               tty_flag = TTY_FRAME;
+               port->icount.frame++;
+       }
+
+       if (lsr & UART_LSR_OE) {
+               port->icount.overrun++;
+               schedule_work(&priv->lsr_work);
+               tty_insert_flip_char(&port->port, 0, TTY_OVERRUN);
+       }
+
+       return tty_flag;
+}
+
 static void f81232_process_read_urb(struct urb *urb)
 {
        struct usb_serial_port *port = urb->context;
-       struct f81232_private *priv = usb_get_serial_port_data(port);
        unsigned char *data = urb->transfer_buffer;
        char tty_flag;
        unsigned int i;
@@ -341,29 +421,8 @@ static void f81232_process_read_urb(struct urb *urb)
        /* bulk-in data: [LSR(1Byte)+DATA(1Byte)][LSR(1Byte)+DATA(1Byte)]... */
 
        for (i = 0; i < urb->actual_length; i += 2) {
-               tty_flag = TTY_NORMAL;
                lsr = data[i];
-
-               if (lsr & UART_LSR_BRK_ERROR_BITS) {
-                       if (lsr & UART_LSR_BI) {
-                               tty_flag = TTY_BREAK;
-                               port->icount.brk++;
-                               usb_serial_handle_break(port);
-                       } else if (lsr & UART_LSR_PE) {
-                               tty_flag = TTY_PARITY;
-                               port->icount.parity++;
-                       } else if (lsr & UART_LSR_FE) {
-                               tty_flag = TTY_FRAME;
-                               port->icount.frame++;
-                       }
-
-                       if (lsr & UART_LSR_OE) {
-                               port->icount.overrun++;
-                               schedule_work(&priv->lsr_work);
-                               tty_insert_flip_char(&port->port, 0,
-                                               TTY_OVERRUN);
-                       }
-               }
+               tty_flag = f81232_handle_lsr(port, lsr);
 
                if (port->port.console && port->sysrq) {
                        if (usb_serial_handle_sysrq_char(port, data[i + 1]))
@@ -376,6 +435,47 @@ static void f81232_process_read_urb(struct urb *urb)
        tty_flip_buffer_push(&port->port);
 }
 
+static void f81534a_process_read_urb(struct urb *urb)
+{
+       struct usb_serial_port *port = urb->context;
+       unsigned char *data = urb->transfer_buffer;
+       char tty_flag;
+       unsigned int i;
+       u8 lsr;
+       u8 len;
+
+       if (urb->actual_length < 3) {
+               dev_err(&port->dev, "short message received: %d\n",
+                               urb->actual_length);
+               return;
+       }
+
+       len = data[0];
+       if (len != urb->actual_length) {
+               dev_err(&port->dev, "malformed message received: %d (%d)\n",
+                               urb->actual_length, len);
+               return;
+       }
+
+       /* bulk-in data: [LEN][Data.....][LSR] */
+       lsr = data[len - 1];
+       tty_flag = f81232_handle_lsr(port, lsr);
+
+       if (port->port.console && port->sysrq) {
+               for (i = 1; i < len - 1; ++i) {
+                       if (!usb_serial_handle_sysrq_char(port, data[i])) {
+                               tty_insert_flip_char(&port->port, data[i],
+                                               tty_flag);
+                       }
+               }
+       } else {
+               tty_insert_flip_string_fixed_flag(&port->port, &data[1],
+                                                       tty_flag, len - 2);
+       }
+
+       tty_flip_buffer_push(&port->port);
+}
+
 static void f81232_break_ctl(struct tty_struct *tty, int break_state)
 {
        struct usb_serial_port *port = tty->driver_data;
@@ -659,6 +759,24 @@ static int f81232_open(struct tty_struct *tty, struct usb_serial_port *port)
        return 0;
 }
 
+static int f81534a_open(struct tty_struct *tty, struct usb_serial_port *port)
+{
+       int status;
+       u8 mask;
+       u8 val;
+
+       val = F81534A_TRIGGER_MULTIPLE_4X | F81534A_FIFO_128BYTE;
+       mask = F81534A_TRIGGER_MASK | F81534A_FIFO_128BYTE;
+
+       status = f81232_set_mask_register(port, F81534A_MODE_REG, mask, val);
+       if (status) {
+               dev_err(&port->dev, "failed to set MODE_REG: %d\n", status);
+               return status;
+       }
+
+       return f81232_open(tty, port);
+}
+
 static void f81232_close(struct usb_serial_port *port)
 {
        struct f81232_private *port_priv = usb_get_serial_port_data(port);
@@ -678,6 +796,20 @@ static void f81232_dtr_rts(struct usb_serial_port *port, int on)
                f81232_set_mctrl(port, 0, TIOCM_DTR | TIOCM_RTS);
 }
 
+static bool f81232_tx_empty(struct usb_serial_port *port)
+{
+       int status;
+       u8 tmp;
+
+       status = f81232_get_register(port, LINE_STATUS_REGISTER, &tmp);
+       if (!status) {
+               if ((tmp & UART_LSR_TEMT) != UART_LSR_TEMT)
+                       return false;
+       }
+
+       return true;
+}
+
 static int f81232_carrier_raised(struct usb_serial_port *port)
 {
        u8 msr;
@@ -728,11 +860,98 @@ static void f81232_lsr_worker(struct work_struct *work)
                dev_warn(&port->dev, "read LSR failed: %d\n", status);
 }
 
+static int f81534a_ctrl_set_register(struct usb_interface *intf, u16 reg,
+                                       u16 size, void *val)
+{
+       struct usb_device *dev = interface_to_usbdev(intf);
+       int retry = F81534A_ACCESS_REG_RETRY;
+       int status;
+       u8 *tmp;
+
+       tmp = kmemdup(val, size, GFP_KERNEL);
+       if (!tmp)
+               return -ENOMEM;
+
+       while (retry--) {
+               status = usb_control_msg(dev,
+                                       usb_sndctrlpipe(dev, 0),
+                                       F81232_REGISTER_REQUEST,
+                                       F81232_SET_REGISTER,
+                                       reg,
+                                       0,
+                                       tmp,
+                                       size,
+                                       USB_CTRL_SET_TIMEOUT);
+               if (status < 0) {
+                       status = usb_translate_errors(status);
+                       if (status == -EIO)
+                               continue;
+               } else if (status != size) {
+                       /* Retry on short transfers */
+                       status = -EIO;
+                       continue;
+               } else {
+                       status = 0;
+               }
+
+               break;
+       }
+
+       if (status) {
+               dev_err(&intf->dev, "failed to set register 0x%x: %d\n",
+                               reg, status);
+       }
+
+       kfree(tmp);
+       return status;
+}
+
+static int f81534a_ctrl_enable_all_ports(struct usb_interface *intf, bool en)
+{
+       unsigned char enable[2] = {0};
+       int status;
+
+       /*
+        * Enable all available serial ports, define as following:
+        * bit 15       : Reset behavior (when HUB got soft reset)
+        *                      0: maintain all serial port enabled state.
+        *                      1: disable all serial port.
+        * bit 0~11     : Serial port enable bit.
+        */
+       if (en) {
+               enable[0] = 0xff;
+               enable[1] = 0x8f;
+       }
+
+       status = f81534a_ctrl_set_register(intf, F81534A_CTRL_CMD_ENABLE_PORT,
+                       sizeof(enable), enable);
+       if (status)
+               dev_err(&intf->dev, "failed to enable ports: %d\n", status);
+
+       return status;
+}
+
+static int f81534a_ctrl_probe(struct usb_interface *intf,
+                               const struct usb_device_id *id)
+{
+       return f81534a_ctrl_enable_all_ports(intf, true);
+}
+
+static void f81534a_ctrl_disconnect(struct usb_interface *intf)
+{
+       f81534a_ctrl_enable_all_ports(intf, false);
+}
+
+static int f81534a_ctrl_resume(struct usb_interface *intf)
+{
+       return f81534a_ctrl_enable_all_ports(intf, true);
+}
+
 static int f81232_port_probe(struct usb_serial_port *port)
 {
        struct f81232_private *priv;
 
-       priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(&port->dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
@@ -748,14 +967,17 @@ static int f81232_port_probe(struct usb_serial_port *port)
        return 0;
 }
 
-static int f81232_port_remove(struct usb_serial_port *port)
+static int f81534a_port_probe(struct usb_serial_port *port)
 {
-       struct f81232_private *priv;
+       int status;
 
-       priv = usb_get_serial_port_data(port);
-       kfree(priv);
+       /* tri-state with pull-high, default RS232 Mode */
+       status = f81232_set_register(port, F81534A_GPIO_REG,
+                                       F81534A_GPIO_MODE2_DIR);
+       if (status)
+               return status;
 
-       return 0;
+       return f81232_port_probe(port);
 }
 
 static int f81232_suspend(struct usb_serial *serial, pm_message_t message)
@@ -799,7 +1021,7 @@ static struct usb_serial_driver f81232_device = {
                .owner =        THIS_MODULE,
                .name =         "f81232",
        },
-       .id_table =             id_table,
+       .id_table =             f81232_id_table,
        .num_ports =            1,
        .bulk_in_size =         256,
        .bulk_out_size =        256,
@@ -813,22 +1035,82 @@ static struct usb_serial_driver f81232_device = {
        .tiocmget =             f81232_tiocmget,
        .tiocmset =             f81232_tiocmset,
        .tiocmiwait =           usb_serial_generic_tiocmiwait,
+       .tx_empty =             f81232_tx_empty,
        .process_read_urb =     f81232_process_read_urb,
        .read_int_callback =    f81232_read_int_callback,
        .port_probe =           f81232_port_probe,
-       .port_remove =          f81232_port_remove,
+       .suspend =              f81232_suspend,
+       .resume =               f81232_resume,
+};
+
+static struct usb_serial_driver f81534a_device = {
+       .driver = {
+               .owner =        THIS_MODULE,
+               .name =         "f81534a",
+       },
+       .id_table =             f81534a_id_table,
+       .num_ports =            1,
+       .open =                 f81534a_open,
+       .close =                f81232_close,
+       .dtr_rts =              f81232_dtr_rts,
+       .carrier_raised =       f81232_carrier_raised,
+       .get_serial =           f81232_get_serial_info,
+       .break_ctl =            f81232_break_ctl,
+       .set_termios =          f81232_set_termios,
+       .tiocmget =             f81232_tiocmget,
+       .tiocmset =             f81232_tiocmset,
+       .tiocmiwait =           usb_serial_generic_tiocmiwait,
+       .tx_empty =             f81232_tx_empty,
+       .process_read_urb =     f81534a_process_read_urb,
+       .read_int_callback =    f81232_read_int_callback,
+       .port_probe =           f81534a_port_probe,
        .suspend =              f81232_suspend,
        .resume =               f81232_resume,
 };
 
 static struct usb_serial_driver * const serial_drivers[] = {
        &f81232_device,
+       &f81534a_device,
        NULL,
 };
 
-module_usb_serial_driver(serial_drivers, id_table);
+static struct usb_driver f81534a_ctrl_driver = {
+       .name =         "f81534a_ctrl",
+       .id_table =     f81534a_ctrl_id_table,
+       .probe =        f81534a_ctrl_probe,
+       .disconnect =   f81534a_ctrl_disconnect,
+       .resume =       f81534a_ctrl_resume,
+};
+
+static int __init f81232_init(void)
+{
+       int status;
+
+       status = usb_register_driver(&f81534a_ctrl_driver, THIS_MODULE,
+                       KBUILD_MODNAME);
+       if (status)
+               return status;
+
+       status = usb_serial_register_drivers(serial_drivers, KBUILD_MODNAME,
+                       combined_id_table);
+       if (status) {
+               usb_deregister(&f81534a_ctrl_driver);
+               return status;
+       }
+
+       return 0;
+}
+
+static void __exit f81232_exit(void)
+{
+       usb_serial_deregister_drivers(serial_drivers);
+       usb_deregister(&f81534a_ctrl_driver);
+}
+
+module_init(f81232_init);
+module_exit(f81232_exit);
 
-MODULE_DESCRIPTION("Fintek F81232 USB to serial adaptor driver");
+MODULE_DESCRIPTION("Fintek F81232/532A/534A/535/536 USB to serial driver");
 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");
 MODULE_AUTHOR("Peter Hong <peter_hong@fintek.com.tw>");
 MODULE_LICENSE("GPL v2");
index 1be8bea372a2a56768b3a19db6cc394781dae41e..5cdf180cda23e90ff8b8c3456022d4491221fde3 100644 (file)
@@ -417,7 +417,7 @@ void usb_serial_generic_read_bulk_callback(struct urb *urb)
        /*
         * Make sure URB is marked as free before checking the throttled flag
         * to avoid racing with unthrottle() on another CPU. Matches the
-        * smp_mb() in unthrottle().
+        * smp_mb__after_atomic() in unthrottle().
         */
        smp_mb__after_atomic();
 
@@ -489,7 +489,7 @@ void usb_serial_generic_unthrottle(struct tty_struct *tty)
         * Matches the smp_mb__after_atomic() in
         * usb_serial_generic_read_bulk_callback().
         */
-       smp_mb();
+       smp_mb__after_atomic();
 
        usb_serial_generic_submit_read_urbs(port, GFP_KERNEL);
 }
@@ -609,12 +609,10 @@ EXPORT_SYMBOL_GPL(usb_serial_handle_break);
  * @tty: tty for the port
  * @status: new carrier detect status, nonzero if active
  */
-void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
+void usb_serial_handle_dcd_change(struct usb_serial_port *port,
                                struct tty_struct *tty, unsigned int status)
 {
-       struct tty_port *port = &usb_port->port;
-
-       dev_dbg(&usb_port->dev, "%s - status %d\n", __func__, status);
+       dev_dbg(&port->dev, "%s - status %d\n", __func__, status);
 
        if (tty) {
                struct tty_ldisc *ld = tty_ldisc_ref(tty);
@@ -627,7 +625,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
        }
 
        if (status)
-               wake_up_interruptible(&port->open_wait);
+               wake_up_interruptible(&port->port.open_wait);
        else if (tty && !C_CLOCAL(tty))
                tty_hangup(tty);
 }
index 5737add6a2a436b440792a14112288c4c4801c02..4cca0b836f43092506f40c10fe0f2589fd12de1a 100644 (file)
@@ -710,7 +710,7 @@ static void edge_interrupt_callback(struct urb *urb)
                /* grab the txcredits for the ports if available */
                position = 2;
                portNumber = 0;
-               while ((position < length) &&
+               while ((position < length - 1) &&
                                (portNumber < edge_serial->serial->num_ports)) {
                        txCredits = data[position] | (data[position+1] << 8);
                        if (txCredits) {
index c38e87ac5ea9ea9665115cb74167b311a59c9c7c..0d1a5bb4636ef169ec1c4632992f3fc32b945b92 100644 (file)
@@ -593,7 +593,7 @@ struct ti_i2c_desc {
        __u8    Type;                   // Type of descriptor
        __le16  Size;                   // Size of data only not including header
        __u8    CheckSum;               // Checksum (8 bit sum of data only)
-       __u8    Data[0];                // Data starts here
+       __u8    Data[];         // Data starts here
 } __attribute__((packed));
 
 // for 5152 devices only (type 2 record)
@@ -601,7 +601,7 @@ struct ti_i2c_desc {
 struct ti_i2c_firmware_rec {
        __u8    Ver_Major;              // Firmware Major version number
        __u8    Ver_Minor;              // Firmware Minor version number
-       __u8    Data[0];                // Download starts here
+       __u8    Data[];         // Download starts here
 } __attribute__((packed));
 
 
index 0b5dcf973d94b5adfe14c9ff15af99c9a9d7d7d1..8bfffca3e4ae450bdfd784ae4d57cfd24e2343e3 100644 (file)
@@ -1992,8 +1992,14 @@ static const struct usb_device_id option_ids[] = {
        { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e01, 0xff, 0xff, 0xff) },    /* D-Link DWM-152/C1 */
        { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x3e02, 0xff, 0xff, 0xff) },    /* D-Link DWM-156/C1 */
        { USB_DEVICE_AND_INTERFACE_INFO(0x07d1, 0x7e11, 0xff, 0xff, 0xff) },    /* D-Link DWM-156/A3 */
+       { USB_DEVICE_INTERFACE_CLASS(0x1435, 0xd191, 0xff),                     /* Wistron Neweb D19Q1 */
+         .driver_info = RSVD(1) | RSVD(4) },
+       { USB_DEVICE_INTERFACE_CLASS(0x1690, 0x7588, 0xff),                     /* ASKEY WWHC050 */
+         .driver_info = RSVD(1) | RSVD(4) },
        { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2031, 0xff),                     /* Olicard 600 */
          .driver_info = RSVD(4) },
+       { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2033, 0xff),                     /* BroadMobi BM806U */
+         .driver_info = RSVD(4) },
        { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x2060, 0xff),                     /* BroadMobi BM818 */
          .driver_info = RSVD(4) },
        { USB_DEVICE_INTERFACE_CLASS(0x2020, 0x4000, 0xff) },                   /* OLICARD300 - MT6225 */
index ef23acc9b9ce4bc8fe8ccb8197b42f69928a87b3..73075b9351c58694e4239fe6b18eec4da2fddc42 100644 (file)
@@ -219,7 +219,7 @@ struct ti_write_data_bytes {
        u8      bDataCounter;
        __be16  wBaseAddrHi;
        __be16  wBaseAddrLo;
-       u8      bData[0];
+       u8      bData[];
 } __packed;
 
 struct ti_read_data_request {
@@ -234,7 +234,7 @@ struct ti_read_data_bytes {
        __u8    bCmdCode;
        __u8    bModuleId;
        __u8    bErrorCode;
-       __u8    bData[0];
+       __u8    bData[];
 } __packed;
 
 /* Interrupt struct */
index dc7a65b9ec982113f5b40dc1fb23c0179b655dac..27e3bb58c872eacbd6a2f121571498ae7d10f356 100644 (file)
@@ -288,7 +288,7 @@ static void serial_close(struct tty_struct *tty, struct file *filp)
 
 /**
  * serial_cleanup - free resources post close/hangup
- * @port: port to free up
+ * @tty: tty to clean up
  *
  * Do the resource freeing and refcount dropping for the port.
  * Avoid freeing the console.
index 9a79cd9762f31d50821cb84942a24e0e19290b57..94a64729dc27d6365a42bbd3ea9cdc80ec2da9fb 100644 (file)
@@ -121,12 +121,12 @@ MODULE_PARM_DESC(quirks, "supplemental list of device IDs and their quirks");
        .initFunction = init_function,  \
 }
 
-static struct us_unusual_dev us_unusual_dev_list[] = {
+static const struct us_unusual_dev us_unusual_dev_list[] = {
 #      include "unusual_devs.h"
        { }             /* Terminating entry */
 };
 
-static struct us_unusual_dev for_dynamic_ids =
+static const struct us_unusual_dev for_dynamic_ids =
                USUAL_DEV(USB_SC_SCSI, USB_PR_BULK);
 
 #undef UNUSUAL_DEV
@@ -583,7 +583,7 @@ EXPORT_SYMBOL_GPL(usb_stor_adjust_quirks);
 
 /* Get the unusual_devs entries and the string descriptors */
 static int get_device_info(struct us_data *us, const struct usb_device_id *id,
-               struct us_unusual_dev *unusual_dev)
+               const struct us_unusual_dev *unusual_dev)
 {
        struct usb_device *dev = us->pusb_dev;
        struct usb_interface_descriptor *idesc =
@@ -933,7 +933,7 @@ static unsigned int usb_stor_sg_tablesize(struct usb_interface *intf)
 int usb_stor_probe1(struct us_data **pus,
                struct usb_interface *intf,
                const struct usb_device_id *id,
-               struct us_unusual_dev *unusual_dev,
+               const struct us_unusual_dev *unusual_dev,
                struct scsi_host_template *sht)
 {
        struct Scsi_Host *host;
@@ -1092,7 +1092,7 @@ static struct scsi_host_template usb_stor_host_template;
 static int storage_probe(struct usb_interface *intf,
                         const struct usb_device_id *id)
 {
-       struct us_unusual_dev *unusual_dev;
+       const struct us_unusual_dev *unusual_dev;
        struct us_data *us;
        int result;
        int size;
index 85052cd66839180f97b40700d8def7952d1de63a..5850d624cac72c5f8c08ba490aa160bad67b6c89 100644 (file)
@@ -93,7 +93,8 @@ struct us_data {
        struct mutex            dev_mutex;       /* protect pusb_dev */
        struct usb_device       *pusb_dev;       /* this usb_device */
        struct usb_interface    *pusb_intf;      /* this interface */
-       struct us_unusual_dev   *unusual_dev;    /* device-filter entry     */
+       const struct us_unusual_dev   *unusual_dev;
+                                               /* device-filter entry     */
        unsigned long           fflags;          /* fixed flags from filter */
        unsigned long           dflags;          /* dynamic atomic bitflags */
        unsigned int            send_bulk_pipe;  /* cached pipe values */
@@ -185,7 +186,7 @@ extern int usb_stor_post_reset(struct usb_interface *iface);
 extern int usb_stor_probe1(struct us_data **pus,
                struct usb_interface *intf,
                const struct usb_device_id *id,
-               struct us_unusual_dev *unusual_dev,
+               const struct us_unusual_dev *unusual_dev,
                struct scsi_host_template *sht);
 extern int usb_stor_probe2(struct us_data *us);
 extern void usb_stor_disconnect(struct usb_interface *intf);
index cfd12e523678520b30d1bc6fc378ceba7d77f24d..529512827d8f60b6a4bca7a970a0eb62c3bc077d 100644 (file)
@@ -40,7 +40,7 @@
        .driver_info = (flags) \
 }
 
-struct usb_device_id usb_storage_usb_ids[] = {
+const struct usb_device_id usb_storage_usb_ids[] = {
 #      include "unusual_devs.h"
        { }             /* Terminating entry */
 };
@@ -68,7 +68,7 @@ struct ignore_entry {
        .bcdmax = bcdDeviceMax,         \
 }
 
-static struct ignore_entry ignore_ids[] = {
+static const struct ignore_entry ignore_ids[] = {
 #      include "unusual_alauda.h"
 #      include "unusual_cypress.h"
 #      include "unusual_datafab.h"
@@ -92,7 +92,7 @@ int usb_usual_ignore_device(struct usb_interface *intf)
 {
        struct usb_device *udev;
        unsigned vid, pid, bcd;
-       struct ignore_entry *p;
+       const struct ignore_entry *p;
 
        udev = interface_to_usbdev(intf);
        vid = le16_to_cpu(udev->descriptor.idVendor);
index 2e45eb479386febd043c077b33bdf1d036c801b5..c823122f9cb7b30f66d6fe1082344d36c3f7137d 100644 (file)
@@ -30,17 +30,10 @@ static int typec_altmode_set_state(struct typec_altmode *adev,
 {
        bool is_port = is_typec_port(adev->dev.parent);
        struct altmode *port_altmode;
-       int ret;
 
        port_altmode = is_port ? to_altmode(adev) : to_altmode(adev)->partner;
 
-       ret = typec_altmode_set_mux(port_altmode, conf, data);
-       if (ret)
-               return ret;
-
-       blocking_notifier_call_chain(&port_altmode->nh, conf, NULL);
-
-       return 0;
+       return typec_altmode_set_mux(port_altmode, conf, data);
 }
 
 /* -------------------------------------------------------------------------- */
@@ -82,9 +75,6 @@ int typec_altmode_notify(struct typec_altmode *adev,
        if (ret)
                return ret;
 
-       blocking_notifier_call_chain(is_port ? &altmode->nh : &partner->nh,
-                                    conf, data);
-
        if (partner->adev.ops && partner->adev.ops->notify)
                return partner->adev.ops->notify(&partner->adev, conf, data);
 
index 0c9661c964732c65297e8cf74ed9631fe5e7a16c..8ba8112d2740d4a0c2d93bd316215689befdf171 100644 (file)
@@ -22,8 +22,6 @@ struct altmode {
 
        struct altmode                  *partner;
        struct altmode                  *plug[2];
-
-       struct blocking_notifier_head   nh;
 };
 
 #define to_altmode(d) container_of(d, struct altmode, adev)
index 7c44e930602ff59eb37b77ed87fa8db31ce2834c..8d894bdff77d3658c13aabffa60ceb43833876bb 100644 (file)
@@ -206,69 +206,6 @@ static void typec_altmode_put_partner(struct altmode *altmode)
        put_device(&adev->dev);
 }
 
-static void *typec_port_match(struct device_connection *con, int ep, void *data)
-{
-       struct device *dev;
-
-       /*
-        * FIXME: Check does the fwnode supports the requested SVID. If it does
-        * we need to return ERR_PTR(-PROBE_DEFER) when there is no device.
-        */
-       if (con->fwnode)
-               return class_find_device_by_fwnode(typec_class, con->fwnode);
-
-       dev = class_find_device_by_name(typec_class, con->endpoint[ep]);
-
-       return dev ? dev : ERR_PTR(-EPROBE_DEFER);
-}
-
-struct typec_altmode *
-typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode,
-                               struct notifier_block *nb)
-{
-       struct typec_device_id id = { svid, mode, };
-       struct device *altmode_dev;
-       struct device *port_dev;
-       struct altmode *altmode;
-       int ret;
-
-       /* Find the port linked to the caller */
-       port_dev = device_connection_find_match(dev, NULL, NULL,
-                                               typec_port_match);
-       if (IS_ERR_OR_NULL(port_dev))
-               return port_dev ? ERR_CAST(port_dev) : ERR_PTR(-ENODEV);
-
-       /* Find the altmode with matching svid */
-       altmode_dev = device_find_child(port_dev, &id, altmode_match);
-
-       put_device(port_dev);
-
-       if (!altmode_dev)
-               return ERR_PTR(-ENODEV);
-
-       altmode = to_altmode(to_typec_altmode(altmode_dev));
-
-       /* Register notifier */
-       ret = blocking_notifier_chain_register(&altmode->nh, nb);
-       if (ret) {
-               put_device(altmode_dev);
-               return ERR_PTR(ret);
-       }
-
-       return &altmode->adev;
-}
-EXPORT_SYMBOL_GPL(typec_altmode_register_notifier);
-
-void typec_altmode_unregister_notifier(struct typec_altmode *adev,
-                                      struct notifier_block *nb)
-{
-       struct altmode *altmode = to_altmode(adev);
-
-       blocking_notifier_chain_unregister(&altmode->nh, nb);
-       put_device(&adev->dev);
-}
-EXPORT_SYMBOL_GPL(typec_altmode_unregister_notifier);
-
 /**
  * typec_altmode_update_active - Report Enter/Exit mode
  * @adev: Handle to the alternate mode
@@ -432,7 +369,28 @@ static struct attribute *typec_altmode_attrs[] = {
        &dev_attr_vdo.attr,
        NULL
 };
-ATTRIBUTE_GROUPS(typec_altmode);
+
+static umode_t typec_altmode_attr_is_visible(struct kobject *kobj,
+                                            struct attribute *attr, int n)
+{
+       struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj));
+
+       if (attr == &dev_attr_active.attr)
+               if (!adev->ops || !adev->ops->activate)
+                       return 0444;
+
+       return attr->mode;
+}
+
+static struct attribute_group typec_altmode_group = {
+       .is_visible = typec_altmode_attr_is_visible,
+       .attrs = typec_altmode_attrs,
+};
+
+static const struct attribute_group *typec_altmode_groups[] = {
+       &typec_altmode_group,
+       NULL
+};
 
 static int altmode_id_get(struct device *dev)
 {
@@ -517,9 +475,7 @@ typec_register_altmode(struct device *parent,
        dev_set_name(&alt->adev.dev, "%s.%u", dev_name(parent), id);
 
        /* Link partners and plugs with the ports */
-       if (is_port)
-               BLOCKING_INIT_NOTIFIER_HEAD(&alt->nh);
-       else
+       if (!is_port)
                typec_altmode_set_partner(alt);
 
        /* The partners are bind to drivers */
@@ -859,7 +815,7 @@ struct typec_cable *typec_cable_get(struct typec_port *port)
 EXPORT_SYMBOL_GPL(typec_cable_get);
 
 /**
- * typec_cable_get - Decrement the reference count on USB Type-C cable
+ * typec_cable_put - Decrement the reference count on USB Type-C cable
  * @cable: The USB Type-C cable
  */
 void typec_cable_put(struct typec_cable *cable)
@@ -1091,11 +1047,6 @@ static ssize_t power_role_store(struct device *dev,
        struct typec_port *port = to_typec_port(dev);
        int ret;
 
-       if (!port->cap->pd_revision) {
-               dev_dbg(dev, "USB Power Delivery not supported\n");
-               return -EOPNOTSUPP;
-       }
-
        if (!port->ops || !port->ops->pr_set) {
                dev_dbg(dev, "power role swapping not supported\n");
                return -EOPNOTSUPP;
@@ -1293,6 +1244,25 @@ static ssize_t usb_power_delivery_revision_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(usb_power_delivery_revision);
 
+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");
+       }
+}
+static DEVICE_ATTR_RO(orientation);
+
 static struct attribute *typec_attrs[] = {
        &dev_attr_data_role.attr,
        &dev_attr_power_operation_mode.attr,
@@ -1303,9 +1273,54 @@ static struct attribute *typec_attrs[] = {
        &dev_attr_usb_typec_revision.attr,
        &dev_attr_vconn_source.attr,
        &dev_attr_port_type.attr,
+       &dev_attr_orientation.attr,
        NULL,
 };
-ATTRIBUTE_GROUPS(typec);
+
+static umode_t typec_attr_is_visible(struct kobject *kobj,
+                                    struct attribute *attr, int n)
+{
+       struct typec_port *port = to_typec_port(kobj_to_dev(kobj));
+
+       if (attr == &dev_attr_data_role.attr) {
+               if (port->cap->data != TYPEC_PORT_DRD ||
+                   !port->ops || !port->ops->dr_set)
+                       return 0444;
+       } else if (attr == &dev_attr_power_role.attr) {
+               if (port->cap->type != TYPEC_PORT_DRP ||
+                   !port->ops || !port->ops->pr_set)
+                       return 0444;
+       } else if (attr == &dev_attr_vconn_source.attr) {
+               if (!port->cap->pd_revision ||
+                   !port->ops || !port->ops->vconn_set)
+                       return 0444;
+       } else if (attr == &dev_attr_preferred_role.attr) {
+               if (port->cap->type != TYPEC_PORT_DRP ||
+                   !port->ops || !port->ops->try_role)
+                       return 0444;
+       } else if (attr == &dev_attr_port_type.attr) {
+               if (!port->ops || !port->ops->port_type_set)
+                       return 0;
+               if (port->cap->type != TYPEC_PORT_DRP)
+                       return 0444;
+       } else if (attr == &dev_attr_orientation.attr) {
+               if (port->cap->orientation_aware)
+                       return 0444;
+               return 0;
+       }
+
+       return attr->mode;
+}
+
+static struct attribute_group typec_group = {
+       .is_visible = typec_attr_is_visible,
+       .attrs = typec_attrs,
+};
+
+static const struct attribute_group *typec_groups[] = {
+       &typec_group,
+       NULL
+};
 
 static int typec_uevent(struct device *dev, struct kobj_uevent_env *env)
 {
@@ -1495,13 +1510,13 @@ int typec_set_orientation(struct typec_port *port,
 {
        int ret;
 
-       if (port->sw) {
-               ret = port->sw->set(port->sw, orientation);
-               if (ret)
-                       return ret;
-       }
+       ret = typec_switch_set(port->sw, orientation);
+       if (ret)
+               return ret;
 
        port->orientation = orientation;
+       sysfs_notify(&port->dev.kobj, NULL, "orientation");
+       kobject_uevent(&port->dev.kobj, KOBJ_CHANGE);
 
        return 0;
 }
@@ -1533,7 +1548,7 @@ int typec_set_mode(struct typec_port *port, int mode)
 
        state.mode = mode;
 
-       return port->mux ? port->mux->set(port->mux, &state) : 0;
+       return typec_mux_set(port->mux, &state);
 }
 EXPORT_SYMBOL_GPL(typec_set_mode);
 
index 5baf0f416c73c6016080eac0af64715753446ba4..52ad277e4565b8cce6455802943af4e6159538b9 100644 (file)
 
 #include "bus.h"
 
-static int name_match(struct device *dev, const void *name)
-{
-       return !strcmp((const char *)name, dev_name(dev));
-}
-
 static bool dev_name_ends_with(struct device *dev, const char *suffix)
 {
        const char *name = dev_name(dev);
@@ -44,41 +39,36 @@ static void *typec_switch_match(struct device_connection *con, int ep,
 {
        struct device *dev;
 
-       if (con->fwnode) {
-               if (con->id && !fwnode_property_present(con->fwnode, con->id))
-                       return NULL;
+       if (con->id && !fwnode_property_present(con->fwnode, con->id))
+               return NULL;
 
-               dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
-                                       switch_fwnode_match);
-       } else {
-               dev = class_find_device(&typec_mux_class, NULL,
-                                       con->endpoint[ep], name_match);
-       }
+       dev = class_find_device(&typec_mux_class, NULL, con->fwnode,
+                               switch_fwnode_match);
 
        return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
 }
 
 /**
- * typec_switch_get - Find USB Type-C orientation switch
- * @dev: The caller device
+ * fwnode_typec_switch_get - Find USB Type-C orientation switch
+ * @fwnode: The caller device node
  *
  * Finds a switch linked with @dev. Returns a reference to the switch on
  * success, NULL if no matching connection was found, or
  * ERR_PTR(-EPROBE_DEFER) when a connection was found but the switch
  * has not been enumerated yet.
  */
-struct typec_switch *typec_switch_get(struct device *dev)
+struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode)
 {
        struct typec_switch *sw;
 
-       sw = device_connection_find_match(dev, "orientation-switch", NULL,
+       sw = fwnode_connection_find_match(fwnode, "orientation-switch", NULL,
                                          typec_switch_match);
        if (!IS_ERR_OR_NULL(sw))
                WARN_ON(!try_module_get(sw->dev.parent->driver->owner));
 
        return sw;
 }
-EXPORT_SYMBOL_GPL(typec_switch_get);
+EXPORT_SYMBOL_GPL(fwnode_typec_switch_get);
 
 /**
  * typec_put_switch - Release USB Type-C orientation switch
@@ -137,7 +127,8 @@ typec_switch_register(struct device *parent,
        sw->dev.class = &typec_mux_class;
        sw->dev.type = &typec_switch_dev_type;
        sw->dev.driver_data = desc->drvdata;
-       dev_set_name(&sw->dev, "%s-switch", dev_name(parent));
+       dev_set_name(&sw->dev, "%s-switch",
+                    desc->name ? desc->name : dev_name(parent));
 
        ret = device_add(&sw->dev);
        if (ret) {
@@ -150,6 +141,16 @@ typec_switch_register(struct device *parent,
 }
 EXPORT_SYMBOL_GPL(typec_switch_register);
 
+int typec_switch_set(struct typec_switch *sw,
+                    enum typec_orientation orientation)
+{
+       if (IS_ERR_OR_NULL(sw))
+               return 0;
+
+       return sw->set(sw, orientation);
+}
+EXPORT_SYMBOL_GPL(typec_switch_set);
+
 /**
  * typec_switch_unregister - Unregister USB Type-C orientation switch
  * @sw: USB Type-C orientation switch
@@ -191,13 +192,6 @@ static void *typec_mux_match(struct device_connection *con, int ep, void *data)
        u16 *val;
        int i;
 
-       if (!con->fwnode) {
-               dev = class_find_device(&typec_mux_class, NULL,
-                                       con->endpoint[ep], name_match);
-
-               return dev ? to_typec_switch(dev) : ERR_PTR(-EPROBE_DEFER);
-       }
-
        /*
         * Check has the identifier already been "consumed". If it
         * has, no need to do any extra connection identification.
@@ -247,8 +241,8 @@ find_mux:
 }
 
 /**
- * typec_mux_get - Find USB Type-C Multiplexer
- * @dev: The caller device
+ * fwnode_typec_mux_get - Find USB Type-C Multiplexer
+ * @fwnode: The caller device node
  * @desc: Alt Mode description
  *
  * Finds a mux linked to the caller. This function is primarily meant for the
@@ -256,19 +250,19 @@ find_mux:
  * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection
  * was found but the mux has not been enumerated yet.
  */
-struct typec_mux *typec_mux_get(struct device *dev,
-                               const struct typec_altmode_desc *desc)
+struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
+                                      const struct typec_altmode_desc *desc)
 {
        struct typec_mux *mux;
 
-       mux = device_connection_find_match(dev, "mode-switch", (void *)desc,
+       mux = fwnode_connection_find_match(fwnode, "mode-switch", (void *)desc,
                                           typec_mux_match);
        if (!IS_ERR_OR_NULL(mux))
                WARN_ON(!try_module_get(mux->dev.parent->driver->owner));
 
        return mux;
 }
-EXPORT_SYMBOL_GPL(typec_mux_get);
+EXPORT_SYMBOL_GPL(fwnode_typec_mux_get);
 
 /**
  * typec_mux_put - Release handle to a Multiplexer
@@ -285,6 +279,15 @@ void typec_mux_put(struct typec_mux *mux)
 }
 EXPORT_SYMBOL_GPL(typec_mux_put);
 
+int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+{
+       if (IS_ERR_OR_NULL(mux))
+               return 0;
+
+       return mux->set(mux, state);
+}
+EXPORT_SYMBOL_GPL(typec_mux_set);
+
 static void typec_mux_release(struct device *dev)
 {
        kfree(to_typec_mux(dev));
@@ -326,7 +329,8 @@ typec_mux_register(struct device *parent, const struct typec_mux_desc *desc)
        mux->dev.class = &typec_mux_class;
        mux->dev.type = &typec_mux_dev_type;
        mux->dev.driver_data = desc->drvdata;
-       dev_set_name(&mux->dev, "%s-mux", dev_name(parent));
+       dev_set_name(&mux->dev, "%s-mux",
+                    desc->name ? desc->name : dev_name(parent));
 
        ret = device_add(&mux->dev);
        if (ret) {
index 01ed0d5e10e8cb7a04524ec151a8b55ea7984744..77eb97b2aa866c68902d5c7d64726fdcafe45715 100644 (file)
@@ -9,4 +9,13 @@ config TYPEC_MUX_PI3USB30532
          Say Y or M if your system has a Pericom PI3USB30532 Type-C cross
          switch / mux chip found on some devices with a Type-C port.
 
+config TYPEC_MUX_INTEL_PMC
+       tristate "Intel PMC mux control"
+       depends on INTEL_PMC_IPC
+       select USB_ROLE_SWITCH
+       help
+         Driver for USB muxes controlled by Intel PMC FW. Intel PMC FW can
+         control the USB role switch and also the multiplexer/demultiplexer
+         switches used with USB Type-C Alternate Modes.
+
 endmenu
index 1332e469b8a0227af75ede5b22f9a4f602e0297f..280a6f553115730fd4567731a383a6ae4dee57ec 100644 (file)
@@ -1,3 +1,4 @@
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_TYPEC_MUX_PI3USB30532)    += pi3usb30532.o
+obj-$(CONFIG_TYPEC_MUX_INTEL_PMC)      += intel_pmc_mux.o
diff --git a/drivers/usb/typec/mux/intel_pmc_mux.c b/drivers/usb/typec/mux/intel_pmc_mux.c
new file mode 100644 (file)
index 0000000..f5c5e0a
--- /dev/null
@@ -0,0 +1,434 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Intel PMC USB mux control
+ *
+ * Copyright (C) 2020 Intel Corporation
+ * Author: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+ */
+
+#include <linux/acpi.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/property.h>
+#include <linux/usb/role.h>
+#include <linux/usb/typec_mux.h>
+#include <linux/usb/typec_dp.h>
+#include <linux/usb/typec_tbt.h>
+
+#include <asm/intel_pmc_ipc.h>
+
+#define PMC_USBC_CMD           0xa7
+
+/* "Usage" OOB Message field values */
+enum {
+       PMC_USB_CONNECT,
+       PMC_USB_DISCONNECT,
+       PMC_USB_SAFE_MODE,
+       PMC_USB_ALT_MODE,
+       PMC_USB_DP_HPD,
+};
+
+#define PMC_USB_MSG_USB2_PORT_SHIFT    0
+#define PMC_USB_MSG_USB3_PORT_SHIFT    4
+#define PMC_USB_MSG_UFP_SHIFT          4
+#define PMC_USB_MSG_ORI_HSL_SHIFT      5
+#define PMC_USB_MSG_ORI_AUX_SHIFT      6
+
+/* Alt Mode Request */
+struct altmode_req {
+       u8 usage;
+       u8 mode_type;
+       u8 mode_id;
+       u8 reserved;
+       u32 mode_data;
+} __packed;
+
+#define PMC_USB_MODE_TYPE_SHIFT                4
+
+enum {
+       PMC_USB_MODE_TYPE_USB,
+       PMC_USB_MODE_TYPE_DP,
+       PMC_USB_MODE_TYPE_TBT,
+};
+
+/* Common Mode Data bits */
+#define PMC_USB_ALTMODE_ACTIVE_CABLE   BIT(2)
+
+#define PMC_USB_ALTMODE_ORI_SHIFT      1
+#define PMC_USB_ALTMODE_UFP_SHIFT      3
+#define PMC_USB_ALTMODE_ORI_AUX_SHIFT  4
+#define PMC_USB_ALTMODE_ORI_HSL_SHIFT  5
+
+/* DP specific Mode Data bits */
+#define PMC_USB_ALTMODE_DP_MODE_SHIFT  8
+
+/* TBT specific Mode Data bits */
+#define PMC_USB_ALTMODE_TBT_TYPE       BIT(17)
+#define PMC_USB_ALTMODE_CABLE_TYPE     BIT(18)
+#define PMC_USB_ALTMODE_ACTIVE_LINK    BIT(20)
+#define PMC_USB_ALTMODE_FORCE_LSR      BIT(23)
+#define PMC_USB_ALTMODE_CABLE_SPD(_s_) (((_s_) & GENMASK(2, 0)) << 25)
+#define   PMC_USB_ALTMODE_CABLE_USB31  1
+#define   PMC_USB_ALTMODE_CABLE_10GPS  2
+#define   PMC_USB_ALTMODE_CABLE_20GPS  3
+#define PMC_USB_ALTMODE_TBT_GEN(_g_)   (((_g_) & GENMASK(1, 0)) << 28)
+
+/* Display HPD Request bits */
+#define PMC_USB_DP_HPD_IRQ             BIT(5)
+#define PMC_USB_DP_HPD_LVL             BIT(6)
+
+struct pmc_usb;
+
+struct pmc_usb_port {
+       int num;
+       struct pmc_usb *pmc;
+       struct typec_mux *typec_mux;
+       struct typec_switch *typec_sw;
+       struct usb_role_switch *usb_sw;
+
+       enum typec_orientation orientation;
+       enum usb_role role;
+
+       u8 usb2_port;
+       u8 usb3_port;
+};
+
+struct pmc_usb {
+       u8 num_ports;
+       struct device *dev;
+       struct pmc_usb_port *port;
+};
+
+static int pmc_usb_command(struct pmc_usb_port *port, u8 *msg, u32 len)
+{
+       u8 response[4];
+
+       /*
+        * Error bit will always be 0 with the USBC command.
+        * Status can be checked from the response message.
+        */
+       intel_pmc_ipc_command(PMC_USBC_CMD, 0, msg, len,
+                             (void *)response, 1);
+
+       if (response[2]) {
+               if (response[2] & BIT(1))
+                       return -EIO;
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int
+pmc_usb_mux_dp_hpd(struct pmc_usb_port *port, struct typec_mux_state *state)
+{
+       struct typec_displayport_data *data = state->data;
+       u8 msg[2] = { };
+
+       msg[0] = PMC_USB_DP_HPD;
+       msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+
+       msg[1] = PMC_USB_DP_HPD_IRQ;
+
+       if (data->status & DP_STATUS_HPD_STATE)
+               msg[1] |= PMC_USB_DP_HPD_LVL;
+
+       return pmc_usb_command(port, msg, sizeof(msg));
+}
+
+static int
+pmc_usb_mux_dp(struct pmc_usb_port *port, struct typec_mux_state *state)
+{
+       struct typec_displayport_data *data = state->data;
+       struct altmode_req req = { };
+
+       if (data->status & DP_STATUS_IRQ_HPD)
+               return pmc_usb_mux_dp_hpd(port, state);
+
+       req.usage = PMC_USB_ALT_MODE;
+       req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+       req.mode_type = PMC_USB_MODE_TYPE_DP << PMC_USB_MODE_TYPE_SHIFT;
+
+       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 |= (state->mode - TYPEC_STATE_MODAL) <<
+                        PMC_USB_ALTMODE_DP_MODE_SHIFT;
+
+       return pmc_usb_command(port, (void *)&req, sizeof(req));
+}
+
+static int
+pmc_usb_mux_tbt(struct pmc_usb_port *port, struct typec_mux_state *state)
+{
+       struct typec_thunderbolt_data *data = state->data;
+       u8 cable_speed = TBT_CABLE_SPEED(data->cable_mode);
+       struct altmode_req req = { };
+
+       req.usage = PMC_USB_ALT_MODE;
+       req.usage |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+       req.mode_type = PMC_USB_MODE_TYPE_TBT << PMC_USB_MODE_TYPE_SHIFT;
+
+       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;
+
+       if (TBT_ADAPTER(data->device_mode) == TBT_ADAPTER_TBT3)
+               req.mode_data |= PMC_USB_ALTMODE_TBT_TYPE;
+
+       if (data->cable_mode & TBT_CABLE_OPTICAL)
+               req.mode_data |= PMC_USB_ALTMODE_CABLE_TYPE;
+
+       if (data->cable_mode & TBT_CABLE_LINK_TRAINING)
+               req.mode_data |= PMC_USB_ALTMODE_ACTIVE_LINK;
+
+       if (data->enter_vdo & TBT_ENTER_MODE_ACTIVE_CABLE)
+               req.mode_data |= PMC_USB_ALTMODE_ACTIVE_CABLE;
+
+       req.mode_data |= PMC_USB_ALTMODE_CABLE_SPD(cable_speed);
+
+       return pmc_usb_command(port, (void *)&req, sizeof(req));
+}
+
+static int pmc_usb_mux_safe_state(struct pmc_usb_port *port)
+{
+       u8 msg;
+
+       msg = PMC_USB_SAFE_MODE;
+       msg |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+
+       return pmc_usb_command(port, &msg, sizeof(msg));
+}
+
+static int pmc_usb_connect(struct pmc_usb_port *port)
+{
+       u8 msg[2];
+
+       msg[0] = PMC_USB_CONNECT;
+       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;
+
+       return pmc_usb_command(port, msg, sizeof(msg));
+}
+
+static int pmc_usb_disconnect(struct pmc_usb_port *port)
+{
+       u8 msg[2];
+
+       msg[0] = PMC_USB_DISCONNECT;
+       msg[0] |= port->usb3_port << PMC_USB_MSG_USB3_PORT_SHIFT;
+
+       msg[1] = port->usb2_port << PMC_USB_MSG_USB2_PORT_SHIFT;
+
+       return pmc_usb_command(port, msg, sizeof(msg));
+}
+
+static int
+pmc_usb_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
+{
+       struct pmc_usb_port *port = typec_mux_get_drvdata(mux);
+
+       if (!state->alt)
+               return 0;
+
+       if (state->mode == TYPEC_STATE_SAFE)
+               return pmc_usb_mux_safe_state(port);
+
+       switch (state->alt->svid) {
+       case USB_TYPEC_TBT_SID:
+               return pmc_usb_mux_tbt(port, state);
+       case USB_TYPEC_DP_SID:
+               return pmc_usb_mux_dp(port, state);
+       }
+
+       return -EOPNOTSUPP;
+}
+
+static int pmc_usb_set_orientation(struct typec_switch *sw,
+                                  enum typec_orientation orientation)
+{
+       struct pmc_usb_port *port = typec_switch_get_drvdata(sw);
+
+       if (port->orientation == orientation)
+               return 0;
+
+       port->orientation = orientation;
+
+       if (port->role) {
+               if (orientation == TYPEC_ORIENTATION_NONE)
+                       return pmc_usb_disconnect(port);
+               else
+                       return pmc_usb_connect(port);
+       }
+
+       return 0;
+}
+
+static int pmc_usb_set_role(struct usb_role_switch *sw, enum usb_role role)
+{
+       struct pmc_usb_port *port = usb_role_switch_get_drvdata(sw);
+
+       if (port->role == role)
+               return 0;
+
+       port->role = role;
+
+       if (port->orientation) {
+               if (role == USB_ROLE_NONE)
+                       return pmc_usb_disconnect(port);
+               else
+                       return pmc_usb_connect(port);
+       }
+
+       return 0;
+}
+
+static int pmc_usb_register_port(struct pmc_usb *pmc, int index,
+                                struct fwnode_handle *fwnode)
+{
+       struct pmc_usb_port *port = &pmc->port[index];
+       struct usb_role_switch_desc desc = { };
+       struct typec_switch_desc sw_desc = { };
+       struct typec_mux_desc mux_desc = { };
+       int ret;
+
+       ret = fwnode_property_read_u8(fwnode, "usb2-port", &port->usb2_port);
+       if (ret)
+               return ret;
+
+       ret = fwnode_property_read_u8(fwnode, "usb3-port", &port->usb3_port);
+       if (ret)
+               return ret;
+
+       port->num = index;
+       port->pmc = pmc;
+
+       sw_desc.fwnode = fwnode;
+       sw_desc.drvdata = port;
+       sw_desc.name = fwnode_get_name(fwnode);
+       sw_desc.set = pmc_usb_set_orientation;
+
+       port->typec_sw = typec_switch_register(pmc->dev, &sw_desc);
+       if (IS_ERR(port->typec_sw))
+               return PTR_ERR(port->typec_sw);
+
+       mux_desc.fwnode = fwnode;
+       mux_desc.drvdata = port;
+       mux_desc.name = fwnode_get_name(fwnode);
+       mux_desc.set = pmc_usb_mux_set;
+
+       port->typec_mux = typec_mux_register(pmc->dev, &mux_desc);
+       if (IS_ERR(port->typec_mux)) {
+               ret = PTR_ERR(port->typec_mux);
+               goto err_unregister_switch;
+       }
+
+       desc.fwnode = fwnode;
+       desc.driver_data = port;
+       desc.name = fwnode_get_name(fwnode);
+       desc.set = pmc_usb_set_role;
+
+       port->usb_sw = usb_role_switch_register(pmc->dev, &desc);
+       if (IS_ERR(port->usb_sw)) {
+               ret = PTR_ERR(port->usb_sw);
+               goto err_unregister_mux;
+       }
+
+       return 0;
+
+err_unregister_mux:
+       typec_mux_unregister(port->typec_mux);
+
+err_unregister_switch:
+       typec_switch_unregister(port->typec_sw);
+
+       return ret;
+}
+
+static int pmc_usb_probe(struct platform_device *pdev)
+{
+       struct fwnode_handle *fwnode = NULL;
+       struct pmc_usb *pmc;
+       int i = 0;
+       int ret;
+
+       pmc = devm_kzalloc(&pdev->dev, sizeof(*pmc), GFP_KERNEL);
+       if (!pmc)
+               return -ENOMEM;
+
+       device_for_each_child_node(&pdev->dev, fwnode)
+               pmc->num_ports++;
+
+       pmc->port = devm_kcalloc(&pdev->dev, pmc->num_ports,
+                                sizeof(struct pmc_usb_port), GFP_KERNEL);
+       if (!pmc->port)
+               return -ENOMEM;
+
+       pmc->dev = &pdev->dev;
+
+       /*
+        * For every physical USB connector (USB2 and USB3 combo) there is a
+        * child ACPI device node under the PMC mux ACPI device object.
+        */
+       for (i = 0; i < pmc->num_ports; i++) {
+               fwnode = device_get_next_child_node(pmc->dev, fwnode);
+               if (!fwnode)
+                       break;
+
+               ret = pmc_usb_register_port(pmc, i, fwnode);
+               if (ret)
+                       goto err_remove_ports;
+       }
+
+       platform_set_drvdata(pdev, pmc);
+
+       return 0;
+
+err_remove_ports:
+       for (i = 0; i < pmc->num_ports; i++) {
+               typec_switch_unregister(pmc->port[i].typec_sw);
+               typec_mux_unregister(pmc->port[i].typec_mux);
+       }
+
+       return ret;
+}
+
+static int pmc_usb_remove(struct platform_device *pdev)
+{
+       struct pmc_usb *pmc = platform_get_drvdata(pdev);
+       int i;
+
+       for (i = 0; i < pmc->num_ports; i++) {
+               typec_switch_unregister(pmc->port[i].typec_sw);
+               typec_mux_unregister(pmc->port[i].typec_mux);
+       }
+
+       return 0;
+}
+
+static const struct acpi_device_id pmc_usb_acpi_ids[] = {
+       { "INTC105C", },
+       { }
+};
+MODULE_DEVICE_TABLE(acpi, pmc_usb_acpi_ids);
+
+static struct platform_driver pmc_usb_driver = {
+       .driver = {
+               .name = "intel_pmc_usb",
+               .acpi_match_table = ACPI_PTR(pmc_usb_acpi_ids),
+       },
+       .probe = pmc_usb_probe,
+       .remove = pmc_usb_remove,
+};
+
+module_platform_driver(pmc_usb_driver);
+
+MODULE_AUTHOR("Heikki Krogerus <heikki.krogerus@linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Intel PMC USB mux control");
index f3087ef8265c2bea51d7bd3c03e6b4a0632cf26e..de3576e6530ab2abdd9dc07a4c4c88b51521f0ca 100644 (file)
@@ -373,6 +373,14 @@ struct pd_rx_event {
        ((port)->try_src_count == 0 && (port)->try_role == TYPEC_SOURCE && \
        (port)->port_type == TYPEC_PORT_DRP)
 
+#define tcpm_data_role_for_source(port) \
+       ((port)->typec_caps.data == TYPEC_PORT_UFP ? \
+       TYPEC_DEVICE : TYPEC_HOST)
+
+#define tcpm_data_role_for_sink(port) \
+       ((port)->typec_caps.data == TYPEC_PORT_DFP ? \
+       TYPEC_HOST : TYPEC_DEVICE)
+
 static enum tcpm_state tcpm_default_state(struct tcpm_port *port)
 {
        if (port->port_type == TYPEC_PORT_DRP) {
@@ -788,10 +796,30 @@ static int tcpm_set_roles(struct tcpm_port *port, bool attached,
        else
                orientation = TYPEC_ORIENTATION_REVERSE;
 
-       if (data == TYPEC_HOST)
-               usb_role = USB_ROLE_HOST;
-       else
-               usb_role = USB_ROLE_DEVICE;
+       if (port->typec_caps.data == TYPEC_PORT_DRD) {
+               if (data == TYPEC_HOST)
+                       usb_role = USB_ROLE_HOST;
+               else
+                       usb_role = USB_ROLE_DEVICE;
+       } else if (port->typec_caps.data == TYPEC_PORT_DFP) {
+               if (data == TYPEC_HOST) {
+                       if (role == TYPEC_SOURCE)
+                               usb_role = USB_ROLE_HOST;
+                       else
+                               usb_role = USB_ROLE_NONE;
+               } else {
+                       return -ENOTSUPP;
+               }
+       } else {
+               if (data == TYPEC_DEVICE) {
+                       if (role == TYPEC_SINK)
+                               usb_role = USB_ROLE_DEVICE;
+                       else
+                               usb_role = USB_ROLE_NONE;
+               } else {
+                       return -ENOTSUPP;
+               }
+       }
 
        ret = tcpm_mux_set(port, TYPEC_STATE_USB, usb_role, orientation);
        if (ret < 0)
@@ -1817,7 +1845,7 @@ static void tcpm_pd_ctrl_request(struct tcpm_port *port,
                tcpm_set_state(port, SOFT_RESET, 0);
                break;
        case PD_CTRL_DR_SWAP:
-               if (port->port_type != TYPEC_PORT_DRP) {
+               if (port->typec_caps.data != TYPEC_PORT_DRD) {
                        tcpm_queue_message(port, PD_MSG_CTRL_REJECT);
                        break;
                }
@@ -2618,7 +2646,8 @@ static int tcpm_src_attach(struct tcpm_port *port)
        if (ret < 0)
                return ret;
 
-       ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
+       ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
+                            tcpm_data_role_for_source(port));
        if (ret < 0)
                return ret;
 
@@ -2740,7 +2769,8 @@ static int tcpm_snk_attach(struct tcpm_port *port)
        if (ret < 0)
                return ret;
 
-       ret = tcpm_set_roles(port, true, TYPEC_SINK, TYPEC_DEVICE);
+       ret = tcpm_set_roles(port, true, TYPEC_SINK,
+                            tcpm_data_role_for_sink(port));
        if (ret < 0)
                return ret;
 
@@ -2766,7 +2796,8 @@ static int tcpm_acc_attach(struct tcpm_port *port)
        if (port->attached)
                return 0;
 
-       ret = tcpm_set_roles(port, true, TYPEC_SOURCE, TYPEC_HOST);
+       ret = tcpm_set_roles(port, true, TYPEC_SOURCE,
+                            tcpm_data_role_for_source(port));
        if (ret < 0)
                return ret;
 
@@ -3293,7 +3324,7 @@ static void run_state_machine(struct tcpm_port *port)
                tcpm_set_vconn(port, true);
                tcpm_set_vbus(port, false);
                tcpm_set_roles(port, port->self_powered, TYPEC_SOURCE,
-                              TYPEC_HOST);
+                              tcpm_data_role_for_source(port));
                tcpm_set_state(port, SRC_HARD_RESET_VBUS_ON, PD_T_SRC_RECOVER);
                break;
        case SRC_HARD_RESET_VBUS_ON:
@@ -3308,7 +3339,7 @@ static void run_state_machine(struct tcpm_port *port)
                if (port->pd_capable)
                        tcpm_set_charge(port, false);
                tcpm_set_roles(port, port->self_powered, TYPEC_SINK,
-                              TYPEC_DEVICE);
+                              tcpm_data_role_for_sink(port));
                /*
                 * VBUS may or may not toggle, depending on the adapter.
                 * If it doesn't toggle, transition to SNK_HARD_RESET_SINK_ON
@@ -3649,8 +3680,12 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
        case SRC_SEND_CAPABILITIES:
        case SRC_READY:
                if (tcpm_port_is_disconnected(port) ||
-                   !tcpm_port_is_source(port))
-                       tcpm_set_state(port, SRC_UNATTACHED, 0);
+                   !tcpm_port_is_source(port)) {
+                       if (port->port_type == TYPEC_PORT_SRC)
+                               tcpm_set_state(port, SRC_UNATTACHED, 0);
+                       else
+                               tcpm_set_state(port, SNK_UNATTACHED, 0);
+               }
                break;
        case SNK_UNATTACHED:
                if (tcpm_port_is_sink(port))
@@ -3969,7 +4004,7 @@ static int tcpm_dr_set(struct typec_port *p, enum typec_data_role data)
        mutex_lock(&port->swap_lock);
        mutex_lock(&port->lock);
 
-       if (port->port_type != TYPEC_PORT_DRP) {
+       if (port->typec_caps.data != TYPEC_PORT_DRD) {
                ret = -EINVAL;
                goto port_unlock;
        }
@@ -4711,6 +4746,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
        port->typec_caps.pd_revision = 0x0300;  /* USB-PD spec release 3.0 */
        port->typec_caps.driver_data = port;
        port->typec_caps.ops = &tcpm_ops;
+       port->typec_caps.orientation_aware = 1;
 
        port->partner_desc.identity = &port->partner_ident;
        port->port_type = port->typec_caps.type;
index d5a6aac863274255160429292dc89519690ddee4..ddf2ad3752def3a75bebb21c70b6d640abda6193 100644 (file)
@@ -270,9 +270,16 @@ static int ucsi_register_altmode(struct ucsi_connector *con,
 
                switch (desc->svid) {
                case USB_TYPEC_DP_SID:
-               case USB_TYPEC_NVIDIA_VLINK_SID:
                        alt = ucsi_register_displayport(con, override, i, desc);
                        break;
+               case USB_TYPEC_NVIDIA_VLINK_SID:
+                       if (desc->vdo == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
+                               alt = typec_port_register_altmode(con->port,
+                                                                 desc);
+                       else
+                               alt = ucsi_register_displayport(con, override,
+                                                               i, desc);
+                       break;
                default:
                        alt = typec_port_register_altmode(con->port, desc);
                        break;
@@ -400,7 +407,7 @@ static int ucsi_register_altmodes(struct ucsi_connector *con, u8 recipient)
        struct typec_altmode_desc desc;
        struct ucsi_altmode alt[2];
        u64 command;
-       int num = 1;
+       int num;
        int ret;
        int len;
        int j;
@@ -475,7 +482,8 @@ static void ucsi_unregister_altmodes(struct ucsi_connector *con, u8 recipient)
        while (adev[i]) {
                if (recipient == UCSI_RECIPIENT_SOP &&
                    (adev[i]->svid == USB_TYPEC_DP_SID ||
-                       adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID)) {
+                       (adev[i]->svid == USB_TYPEC_NVIDIA_VLINK_SID &&
+                       adev[i]->vdo != USB_TYPEC_NVIDIA_VLINK_DBG_VDO))) {
                        pdev = typec_altmode_get_partner(adev[i]);
                        ucsi_displayport_remove_partner((void *)pdev);
                }
index e434b9c9a9eb7c1c4075f6ec56013c308eace733..8e831108f4817e6abf34c417a5b75b3f68d1eb79 100644 (file)
@@ -119,12 +119,14 @@ void ucsi_connector_change(struct ucsi *ucsi, u8 num);
 #define UCSI_SET_PDR_ACCEPT_ROLE_SWAPS         BIT(25)
 
 /* GET_ALTERNATE_MODES command bits */
+#define UCSI_ALTMODE_RECIPIENT(_r_)            (((_r_) >> 16) & 0x7)
 #define UCSI_GET_ALTMODE_RECIPIENT(_r_)                ((u64)(_r_) << 16)
 #define   UCSI_RECIPIENT_CON                   0
 #define   UCSI_RECIPIENT_SOP                   1
 #define   UCSI_RECIPIENT_SOP_P                 2
 #define   UCSI_RECIPIENT_SOP_PP                        3
 #define UCSI_GET_ALTMODE_CONNECTOR_NUMBER(_r_) ((u64)(_r_) << 24)
+#define UCSI_ALTMODE_OFFSET(_r_)               (((_r_) >> 32) & 0xff)
 #define UCSI_GET_ALTMODE_OFFSET(_r_)           ((u64)(_r_) << 32)
 #define UCSI_GET_ALTMODE_NUM_ALTMODES(_r_)     ((u64)(_r_) << 40)
 
@@ -340,4 +342,11 @@ static inline void
 ucsi_displayport_remove_partner(struct typec_altmode *adev) { }
 #endif /* CONFIG_TYPEC_DP_ALTMODE */
 
+/*
+ * NVIDIA VirtualLink (svid 0x955) has two altmode. VirtualLink
+ * DP mode with vdo=0x1 and NVIDIA test mode with vdo=0x3
+ */
+#define USB_TYPEC_NVIDIA_VLINK_DP_VDO  0x1
+#define USB_TYPEC_NVIDIA_VLINK_DBG_VDO 0x3
+
 #endif /* __DRIVER_USB_TYPEC_UCSI_H */
index a5b8530490dbac1797ed5fdca8f892f553595a7b..bff96d64dddffee6857125ad6040f5d67a873847 100644 (file)
@@ -125,6 +125,10 @@ struct version_format {
 #define CCG_FW_BUILD_NVIDIA    (('n' << 8) | 'v')
 #define CCG_OLD_FW_VERSION     (CCG_VERSION(0x31) | CCG_VERSION_PATCH(10))
 
+/* Altmode offset for NVIDIA Function Test Board (FTB) */
+#define NVIDIA_FTB_DP_OFFSET   (2)
+#define NVIDIA_FTB_DBG_OFFSET  (3)
+
 struct version_info {
        struct version_format base;
        struct version_format app;
@@ -477,24 +481,65 @@ static void ucsi_ccg_update_set_new_cam_cmd(struct ucsi_ccg *uc,
        *cmd |= UCSI_SET_NEW_CAM_SET_AM(cam);
 }
 
+/*
+ * Change the order of vdo values of NVIDIA test device FTB
+ * (Function Test Board) which reports altmode list with vdo=0x3
+ * first and then vdo=0x. Current logic to assign mode value is
+ * based on order in altmode list and it causes a mismatch of CON
+ * and SOP altmodes since NVIDIA GPU connector has order of vdo=0x1
+ * first and then vdo=0x3
+ */
+static void ucsi_ccg_nvidia_altmode(struct ucsi_ccg *uc,
+                                   struct ucsi_altmode *alt)
+{
+       switch (UCSI_ALTMODE_OFFSET(uc->last_cmd_sent)) {
+       case NVIDIA_FTB_DP_OFFSET:
+               if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DBG_VDO)
+                       alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DP_VDO |
+                               DP_CAP_DP_SIGNALING | DP_CAP_USB |
+                               DP_CONF_SET_PIN_ASSIGN(BIT(DP_PIN_ASSIGN_E));
+               break;
+       case NVIDIA_FTB_DBG_OFFSET:
+               if (alt[0].mid == USB_TYPEC_NVIDIA_VLINK_DP_VDO)
+                       alt[0].mid = USB_TYPEC_NVIDIA_VLINK_DBG_VDO;
+               break;
+       default:
+               break;
+       }
+}
+
 static int ucsi_ccg_read(struct ucsi *ucsi, unsigned int offset,
                         void *val, size_t val_len)
 {
        struct ucsi_ccg *uc = ucsi_get_drvdata(ucsi);
-       int ret;
        u16 reg = CCGX_RAB_UCSI_DATA_BLOCK(offset);
+       struct ucsi_altmode *alt;
+       int ret;
 
        ret = ccg_read(uc, reg, val, val_len);
        if (ret)
                return ret;
 
-       if (offset == UCSI_MESSAGE_IN) {
-               if (UCSI_COMMAND(uc->last_cmd_sent) == UCSI_GET_CURRENT_CAM &&
-                   uc->has_multiple_dp) {
+       if (offset != UCSI_MESSAGE_IN)
+               return ret;
+
+       switch (UCSI_COMMAND(uc->last_cmd_sent)) {
+       case UCSI_GET_CURRENT_CAM:
+               if (uc->has_multiple_dp)
                        ucsi_ccg_update_get_current_cam_cmd(uc, (u8 *)val);
+               break;
+       case UCSI_GET_ALTERNATE_MODES:
+               if (UCSI_ALTMODE_RECIPIENT(uc->last_cmd_sent) ==
+                   UCSI_RECIPIENT_SOP) {
+                       alt = val;
+                       if (alt[0].svid == USB_TYPEC_NVIDIA_VLINK_SID)
+                               ucsi_ccg_nvidia_altmode(uc, alt);
                }
-               uc->last_cmd_sent = 0;
+               break;
+       default:
+               break;
        }
+       uc->last_cmd_sent = 0;
 
        return ret;
 }
@@ -1219,6 +1264,7 @@ static int ccg_restart(struct ucsi_ccg *uc)
                return status;
        }
 
+       pm_runtime_enable(uc->dev);
        return 0;
 }
 
@@ -1234,6 +1280,7 @@ static void ccg_update_firmware(struct work_struct *work)
 
        if (flash_mode != FLASH_NOT_NEEDED) {
                ucsi_unregister(uc->ucsi);
+               pm_runtime_disable(uc->dev);
                free_irq(uc->irq, uc);
 
                ccg_fw_update(uc, flash_mode);
index 1235865e7e2cc4461596e1ce1a9a165af4db34c3..71d956935405f0176ba437f4fea04f8ab5b918b5 100644 (file)
@@ -21,4 +21,6 @@ int tegra_xusb_padctl_usb3_set_lfps_detect(struct tegra_xusb_padctl *padctl,
 int tegra_xusb_padctl_set_vbus_override(struct tegra_xusb_padctl *padctl,
                                        bool val);
 int tegra_phy_xusb_utmi_port_reset(struct phy *phy);
+int tegra_xusb_padctl_get_usb3_companion(struct tegra_xusb_padctl *padctl,
+                                        unsigned int port);
 #endif /* PHY_TEGRA_XUSB_H */
index 041bfa412aa09521813c057fbfc3f273dfda062b..f242f66db19d525a4b370c6cf08717383a099889 100644 (file)
@@ -55,6 +55,9 @@ extern struct device *
 platform_find_device_by_driver(struct device *start,
                               const struct device_driver *drv);
 extern void __iomem *
+devm_platform_get_and_ioremap_resource(struct platform_device *pdev,
+                               unsigned int index, struct resource **res);
+extern void __iomem *
 devm_platform_ioremap_resource(struct platform_device *pdev,
                               unsigned int index);
 extern void __iomem *
index e656e7b4b1e44cea5047477cf238c066559af92d..9f3c721c70dc16be937a039256d38438a14516e8 100644 (file)
@@ -325,7 +325,7 @@ struct usb_interface_cache {
 
        /* variable-length array of alternate settings for this interface,
         * stored in no particular order */
-       struct usb_host_interface altsetting[0];
+       struct usb_host_interface altsetting[];
 };
 #define        ref_to_usb_interface_cache(r) \
                container_of(r, struct usb_interface_cache, ref)
@@ -708,6 +708,7 @@ struct usb_device {
        unsigned lpm_disable_count;
 
        u16 hub_delay;
+       unsigned use_generic_driver:1;
 };
 #define        to_usb_device(d) container_of(d, struct usb_device, dev)
 
@@ -1228,12 +1229,16 @@ struct usb_driver {
  * @drvwrap: Driver-model core structure wrapper.
  * @supports_autosuspend: if set to 0, the USB core will not allow autosuspend
  *     for devices bound to this driver.
+ * @generic_subclass: if set to 1, the generic USB driver's probe, disconnect,
+ *     resume and suspend functions will be called in addition to the driver's
+ *     own, so this part of the setup does not need to be replicated.
  *
  * USB drivers must provide all the fields listed above except drvwrap.
  */
 struct usb_device_driver {
        const char *name;
 
+       bool (*match) (struct usb_device *udev);
        int (*probe) (struct usb_device *udev);
        void (*disconnect) (struct usb_device *udev);
 
@@ -1241,7 +1246,9 @@ struct usb_device_driver {
        int (*resume) (struct usb_device *udev, pm_message_t message);
        const struct attribute_group **dev_groups;
        struct usbdrv_wrap drvwrap;
+       const struct usb_device_id *id_table;
        unsigned int supports_autosuspend:1;
+       unsigned int generic_subclass:1;
 };
 #define        to_usb_device_driver(d) container_of(d, struct usb_device_driver, \
                drvwrap.driver)
@@ -1582,7 +1589,7 @@ struct urb {
        int error_count;                /* (return) number of ISO errors */
        void *context;                  /* (in) context for completion */
        usb_complete_t complete;        /* (in) completion routine */
-       struct usb_iso_packet_descriptor iso_frame_desc[0];
+       struct usb_iso_packet_descriptor iso_frame_desc[];
                                        /* (in) ISO ONLY */
 };
 
index ba4b3e3327ff30a394d82b65f0499dd2b974853f..5e31740c7e40caf67a655d284f874ff48f27a3e8 100644 (file)
@@ -153,7 +153,7 @@ struct uac2_feature_unit_descriptor {
        __u8 bSourceID;
        /* bmaControls is actually u32,
         * but u8 is needed for the hybrid parser */
-       __u8 bmaControls[0]; /* variable length */
+       __u8 bmaControls[]; /* variable length */
 } __attribute__((packed));
 
 /* 4.9.2 Class-Specific AS Interface Descriptor */
index 6b708434b7f94edaec21f06336744899895be537..c69a6f2e6837762cee78476356f3ec18214dbf03 100644 (file)
@@ -109,7 +109,7 @@ struct uac3_feature_unit_descriptor {
        __u8 bSourceID;
        /* bmaControls is actually u32,
         * but u8 is needed for the hybrid parser */
-       __u8 bmaControls[0]; /* variable length */
+       __u8 bmaControls[]; /* variable length */
        /* wFeatureDescrStr omitted */
 } __attribute__((packed));
 
index a15ce99dfc2d6b7ef42b2046c9075c4ea3d0e55b..78e0063555575f156c96576556e58fc869b8bd3a 100644 (file)
@@ -151,7 +151,7 @@ struct ehci_regs {
 #define PORT_OWNER     (1<<13)         /* true: companion hc owns this port */
 #define PORT_POWER     (1<<12)         /* true: has power (see PPC) */
 #define PORT_USB11(x) (((x)&(3<<10)) == (1<<10))       /* USB 1.1 device */
-/* 11:10 for detecting lowspeed devices (reset vs release ownership) */
+#define PORT_LS_MASK   (3<<10)         /* Link status (SE0, K or J */
 /* 9 reserved */
 #define PORT_LPM       (1<<9)          /* LPM transaction */
 #define PORT_RESET     (1<<8)          /* reset port */
index 124462d65eac43e62b03a4236b4c0ab7da4ef8b6..9411c08a5c7e150af4693ec7158071f0df992e0b 100644 (file)
@@ -767,7 +767,7 @@ struct usb_gadget_strings {
 
 struct usb_gadget_string_container {
        struct list_head        list;
-       u8                      *stash[0];
+       u8                      *stash[];
 };
 
 /* put descriptor for string with that id into buf (buflen >= 256) */
index 712b2a603645f5532897f457e6fe7fb3d2ed5a61..e12105ed38341c28f62a531139c4a148838e7d54 100644 (file)
@@ -228,7 +228,7 @@ struct usb_hcd {
        /* The HC driver's private data is stored at the end of
         * this structure.
         */
-       unsigned long hcd_priv[0]
+       unsigned long hcd_priv[]
                        __attribute__ ((aligned(sizeof(s64))));
 };
 
index efac3af83d6be849b56e28129110123bf65e2296..0164fed31b06cc8f5f24c8dab3651961c89528f9 100644 (file)
@@ -13,8 +13,9 @@ enum usb_role {
        USB_ROLE_DEVICE,
 };
 
-typedef int (*usb_role_switch_set_t)(struct device *dev, enum usb_role role);
-typedef enum usb_role (*usb_role_switch_get_t)(struct device *dev);
+typedef int (*usb_role_switch_set_t)(struct usb_role_switch *sw,
+                                    enum usb_role role);
+typedef enum usb_role (*usb_role_switch_get_t)(struct usb_role_switch *sw);
 
 /**
  * struct usb_role_switch_desc - USB Role Switch Descriptor
@@ -25,6 +26,8 @@ typedef enum usb_role (*usb_role_switch_get_t)(struct device *dev);
  * @set: Callback for setting the role
  * @get: Callback for getting the role (optional)
  * @allow_userspace_control: If true userspace may change the role through sysfs
+ * @driver_data: Private data pointer
+ * @name: Name for the switch (optional)
  *
  * @usb2_port and @usb3_port will point to the USB host port and @udc to the USB
  * device controller behind the USB connector with the role switch. If
@@ -40,6 +43,8 @@ struct usb_role_switch_desc {
        usb_role_switch_set_t set;
        usb_role_switch_get_t get;
        bool allow_userspace_control;
+       void *driver_data;
+       const char *name;
 };
 
 
@@ -57,6 +62,9 @@ struct usb_role_switch *
 usb_role_switch_register(struct device *parent,
                         const struct usb_role_switch_desc *desc);
 void usb_role_switch_unregister(struct usb_role_switch *sw);
+
+void usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data);
+void *usb_role_switch_get_drvdata(struct usb_role_switch *sw);
 #else
 static inline int usb_role_switch_set_role(struct usb_role_switch *sw,
                enum usb_role role)
@@ -90,6 +98,17 @@ usb_role_switch_register(struct device *parent,
 }
 
 static inline void usb_role_switch_unregister(struct usb_role_switch *sw) { }
+
+static inline void
+usb_role_switch_set_drvdata(struct usb_role_switch *sw, void *data)
+{
+}
+
+static inline void *usb_role_switch_get_drvdata(struct usb_role_switch *sw)
+{
+       return NULL;
+}
+
 #endif
 
 #endif /* __LINUX_USB_ROLE_H */
index c358b3fd05c97fc436cf9164bd9289c42e7559a5..b00a2642a9cd66467273ff3e2f279b8b196de405 100644 (file)
@@ -198,8 +198,6 @@ struct typec_operations {
  * @pd_revision: USB Power Delivery Specification revision if supported
  * @prefer_role: Initial role preference (DRP ports).
  * @accessory: Supported Accessory Modes
- * @sw: Cable plug orientation switch
- * @mux: Multiplexer switch for Alternate/Accessory Modes
  * @fwnode: Optional fwnode of the port
  * @driver_data: Private pointer for driver specific info
  * @ops: Port operations vector
@@ -213,6 +211,7 @@ struct typec_capability {
        u16                     pd_revision; /* 0300H = "3.0" */
        int                     prefer_role;
        enum typec_accessory    accessory[TYPEC_MAX_ACCESSORY];
+       unsigned int            orientation_aware:1;
 
        struct fwnode_handle    *fwnode;
        void                    *driver_data;
index 923ff3af062878fe6f66c21c2bb73be46def52cb..d834e236c6df63d67ba2533132f7e1fe7e335db3 100644 (file)
@@ -126,13 +126,6 @@ void typec_altmode_put_plug(struct typec_altmode *plug);
 struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes,
                                          size_t n, u16 svid, u8 mode);
 
-struct typec_altmode *
-typec_altmode_register_notifier(struct device *dev, u16 svid, u8 mode,
-                               struct notifier_block *nb);
-
-void typec_altmode_unregister_notifier(struct typec_altmode *adev,
-                                      struct notifier_block *nb);
-
 /**
  * typec_altmode_get_orientation - Get cable plug orientation
  * altmode: Handle to the alternate mode
index be7292c0be5e030ba36fe3e63925077bc50a0968..a9d9957933dc4b23477f58cbfcf9c90f17c05ad1 100644 (file)
@@ -3,6 +3,7 @@
 #ifndef __USB_TYPEC_MUX
 #define __USB_TYPEC_MUX
 
+#include <linux/property.h>
 #include <linux/usb/typec.h>
 
 struct device;
@@ -17,11 +18,20 @@ typedef int (*typec_switch_set_fn_t)(struct typec_switch *sw,
 struct typec_switch_desc {
        struct fwnode_handle *fwnode;
        typec_switch_set_fn_t set;
+       const char *name;
        void *drvdata;
 };
 
-struct typec_switch *typec_switch_get(struct device *dev);
+struct typec_switch *fwnode_typec_switch_get(struct fwnode_handle *fwnode);
 void typec_switch_put(struct typec_switch *sw);
+int typec_switch_set(struct typec_switch *sw,
+                    enum typec_orientation orientation);
+
+static inline struct typec_switch *typec_switch_get(struct device *dev)
+{
+       return fwnode_typec_switch_get(dev_fwnode(dev));
+}
+
 struct typec_switch *
 typec_switch_register(struct device *parent,
                      const struct typec_switch_desc *desc);
@@ -42,12 +52,21 @@ typedef int (*typec_mux_set_fn_t)(struct typec_mux *mux,
 struct typec_mux_desc {
        struct fwnode_handle *fwnode;
        typec_mux_set_fn_t set;
+       const char *name;
        void *drvdata;
 };
 
-struct typec_mux *
-typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc);
+struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode,
+                                      const struct typec_altmode_desc *desc);
 void typec_mux_put(struct typec_mux *mux);
+int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state);
+
+static inline struct typec_mux *
+typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc)
+{
+       return fwnode_typec_mux_get(dev_fwnode(dev), desc);
+}
+
 struct typec_mux *
 typec_mux_register(struct device *parent, const struct typec_mux_desc *desc);
 void typec_mux_unregister(struct typec_mux *mux);
diff --git a/include/linux/usb/typec_tbt.h b/include/linux/usb/typec_tbt.h
new file mode 100644 (file)
index 0000000..47c2d50
--- /dev/null
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __USB_TYPEC_TBT_H
+#define __USB_TYPEC_TBT_H
+
+#include <linux/usb/typec_altmode.h>
+
+#define USB_TYPEC_VENDOR_INTEL         0x8087
+/* Alias for convenience */
+#define USB_TYPEC_TBT_SID              USB_TYPEC_VENDOR_INTEL
+
+/* Connector state for Thunderbolt3 */
+#define TYPEC_TBT_MODE                 TYPEC_STATE_MODAL
+
+/**
+ * struct typec_thunderbolt_data - Thundebolt3 Alt Mode specific data
+ * @device_mode: Device Discover Mode VDO
+ * @cable_mode: Cable Discover Mode VDO
+ * @enter_vdo: Enter Mode VDO
+ */
+struct typec_thunderbolt_data {
+       u32 device_mode;
+       u32 cable_mode;
+       u32 enter_vdo;
+};
+
+/* TBT3 Device Discover Mode VDO bits */
+#define TBT_MODE                       BIT(0)
+#define TBT_ADAPTER(_vdo_)             (((_vdo_) & BIT(16)) >> 16)
+#define   TBT_ADAPTER_LEGACY           0
+#define   TBT_ADAPTER_TBT3             1
+#define TBT_INTEL_SPECIFIC_B0          BIT(26)
+#define TBT_VENDOR_SPECIFIC_B0         BIT(30)
+#define TBT_VENDOR_SPECIFIC_B1         BIT(31)
+
+#define TBT_SET_ADAPTER(a)             (((a) & 1) << 16)
+
+/* TBT3 Cable Discover Mode VDO bits */
+#define TBT_CABLE_SPEED(_vdo_)         (((_vdo_) & GENMASK(18, 16)) >> 16)
+#define   TBT_CABLE_USB3_GEN1          1
+#define   TBT_CABLE_USB3_PASSIVE       2
+#define   TBT_CABLE_10_AND_20GBPS      3
+#define TBT_CABLE_ROUNDED              BIT(19)
+#define TBT_CABLE_OPTICAL              BIT(21)
+#define TBT_CABLE_RETIMER              BIT(22)
+#define TBT_CABLE_LINK_TRAINING                BIT(23)
+
+#define TBT_SET_CABLE_SPEED(_s_)       (((_s_) & GENMASK(2, 0)) << 16)
+
+/* TBT3 Device Enter Mode VDO bits */
+#define TBT_ENTER_MODE_CABLE_SPEED(s)  TBT_SET_CABLE_SPEED(s)
+#define TBT_ENTER_MODE_ACTIVE_CABLE    BIT(24)
+
+#endif /* __USB_TYPEC_TBT_H */
index 000a5954b2e89b1397cee2852d597baf74c82b26..4a19ac3f24d068348baaff5b35c8999d609ae0a7 100644 (file)
@@ -92,6 +92,6 @@ enum { US_DO_ALL_FLAGS };
 #include <linux/usb/storage.h>
 
 extern int usb_usual_ignore_device(struct usb_interface *intf);
-extern struct usb_device_id usb_storage_usb_ids[];
+extern const struct usb_device_id usb_storage_usb_ids[];
 
 #endif /* __LINUX_USB_USUAL_H */
index 79aab0065ec80ae183fccfdd67f2ce2eb776eaa2..14ea197ce37f24c4e0e7bbb1d6a858fbbc0d26de 100644 (file)
@@ -69,7 +69,7 @@ struct usbdevfs_urb32 {
        compat_int_t error_count;
        compat_uint_t signr;
        compat_caddr_t usercontext; /* unused */
-       struct usbdevfs_iso_packet_desc iso_frame_desc[0];
+       struct usbdevfs_iso_packet_desc iso_frame_desc[];
 };
 
 struct usbdevfs_ioctl32 {
diff --git a/include/uapi/linux/usb/raw_gadget.h b/include/uapi/linux/usb/raw_gadget.h
new file mode 100644 (file)
index 0000000..ea37508
--- /dev/null
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * USB Raw Gadget driver.
+ *
+ * See Documentation/usb/raw-gadget.rst for more details.
+ */
+
+#ifndef _UAPI__LINUX_USB_RAW_GADGET_H
+#define _UAPI__LINUX_USB_RAW_GADGET_H
+
+#include <asm/ioctl.h>
+#include <linux/types.h>
+#include <linux/usb/ch9.h>
+
+/* Maximum length of driver_name/device_name in the usb_raw_init struct. */
+#define UDC_NAME_LENGTH_MAX 128
+
+/*
+ * struct usb_raw_init - argument for USB_RAW_IOCTL_INIT ioctl.
+ * @speed: The speed of the emulated USB device, takes the same values as
+ *     the usb_device_speed enum: USB_SPEED_FULL, USB_SPEED_HIGH, etc.
+ * @driver_name: The name of the UDC driver.
+ * @device_name: The name of a UDC instance.
+ *
+ * The last two fields identify a UDC the gadget driver should bind to.
+ * For example, Dummy UDC has "dummy_udc" as its driver_name and "dummy_udc.N"
+ * as its device_name, where N in the index of the Dummy UDC instance.
+ * At the same time the dwc2 driver that is used on Raspberry Pi Zero, has
+ * "20980000.usb" as both driver_name and device_name.
+ */
+struct usb_raw_init {
+       __u8    driver_name[UDC_NAME_LENGTH_MAX];
+       __u8    device_name[UDC_NAME_LENGTH_MAX];
+       __u8    speed;
+};
+
+/* The type of event fetched with the USB_RAW_IOCTL_EVENT_FETCH ioctl. */
+enum usb_raw_event_type {
+       USB_RAW_EVENT_INVALID = 0,
+
+       /* This event is queued when the driver has bound to a UDC. */
+       USB_RAW_EVENT_CONNECT = 1,
+
+       /* This event is queued when a new control request arrived to ep0. */
+       USB_RAW_EVENT_CONTROL = 2,
+
+       /* The list might grow in the future. */
+};
+
+/*
+ * struct usb_raw_event - argument for USB_RAW_IOCTL_EVENT_FETCH ioctl.
+ * @type: The type of the fetched event.
+ * @length: Length of the data buffer. Updated by the driver and set to the
+ *     actual length of the fetched event data.
+ * @data: A buffer to store the fetched event data.
+ *
+ * Currently the fetched data buffer is empty for USB_RAW_EVENT_CONNECT,
+ * and contains struct usb_ctrlrequest for USB_RAW_EVENT_CONTROL.
+ */
+struct usb_raw_event {
+       __u32           type;
+       __u32           length;
+       __u8            data[0];
+};
+
+#define USB_RAW_IO_FLAGS_ZERO  0x0001
+#define USB_RAW_IO_FLAGS_MASK  0x0001
+
+static inline int usb_raw_io_flags_valid(__u16 flags)
+{
+       return (flags & ~USB_RAW_IO_FLAGS_MASK) == 0;
+}
+
+static inline int usb_raw_io_flags_zero(__u16 flags)
+{
+       return (flags & USB_RAW_IO_FLAGS_ZERO);
+}
+
+/*
+ * struct usb_raw_ep_io - argument for USB_RAW_IOCTL_EP0/EP_WRITE/READ ioctls.
+ * @ep: Endpoint handle as returned by USB_RAW_IOCTL_EP_ENABLE for
+ *     USB_RAW_IOCTL_EP_WRITE/READ. Ignored for USB_RAW_IOCTL_EP0_WRITE/READ.
+ * @flags: When USB_RAW_IO_FLAGS_ZERO is specified, the zero flag is set on
+ *     the submitted USB request, see include/linux/usb/gadget.h for details.
+ * @length: Length of data.
+ * @data: Data to send for USB_RAW_IOCTL_EP0/EP_WRITE. Buffer to store received
+ *     data for USB_RAW_IOCTL_EP0/EP_READ.
+ */
+struct usb_raw_ep_io {
+       __u16           ep;
+       __u16           flags;
+       __u32           length;
+       __u8            data[0];
+};
+
+/*
+ * Initializes a Raw Gadget instance.
+ * Accepts a pointer to the usb_raw_init struct as an argument.
+ * Returns 0 on success or negative error code on failure.
+ */
+#define USB_RAW_IOCTL_INIT             _IOW('U', 0, struct usb_raw_init)
+
+/*
+ * Instructs Raw Gadget to bind to a UDC and start emulating a USB device.
+ * Returns 0 on success or negative error code on failure.
+ */
+#define USB_RAW_IOCTL_RUN              _IO('U', 1)
+
+/*
+ * A blocking ioctl that waits for an event and returns fetched event data to
+ * the user.
+ * Accepts a pointer to the usb_raw_event struct.
+ * Returns 0 on success or negative error code on failure.
+ */
+#define USB_RAW_IOCTL_EVENT_FETCH      _IOR('U', 2, struct usb_raw_event)
+
+/*
+ * Queues an IN (OUT for READ) urb as a response to the last control request
+ * received on endpoint 0, provided that was an IN (OUT for READ) request and
+ * waits until the urb is completed. Copies received data to user for READ.
+ * Accepts a pointer to the usb_raw_ep_io struct as an argument.
+ * Returns length of trasferred data on success or negative error code on
+ * failure.
+ */
+#define USB_RAW_IOCTL_EP0_WRITE                _IOW('U', 3, struct usb_raw_ep_io)
+#define USB_RAW_IOCTL_EP0_READ         _IOWR('U', 4, struct usb_raw_ep_io)
+
+/*
+ * Finds an endpoint that supports the transfer type specified in the
+ * descriptor and enables it.
+ * Accepts a pointer to the usb_endpoint_descriptor struct as an argument.
+ * Returns enabled endpoint handle on success or negative error code on failure.
+ */
+#define USB_RAW_IOCTL_EP_ENABLE                _IOW('U', 5, struct usb_endpoint_descriptor)
+
+/* Disables specified endpoint.
+ * Accepts endpoint handle as an argument.
+ * Returns 0 on success or negative error code on failure.
+ */
+#define USB_RAW_IOCTL_EP_DISABLE       _IOW('U', 6, __u32)
+
+/*
+ * Queues an IN (OUT for READ) urb as a response to the last control request
+ * received on endpoint usb_raw_ep_io.ep, provided that was an IN (OUT for READ)
+ * request and waits until the urb is completed. Copies received data to user
+ * for READ.
+ * Accepts a pointer to the usb_raw_ep_io struct as an argument.
+ * Returns length of trasferred data on success or negative error code on
+ * failure.
+ */
+#define USB_RAW_IOCTL_EP_WRITE         _IOW('U', 7, struct usb_raw_ep_io)
+#define USB_RAW_IOCTL_EP_READ          _IOWR('U', 8, struct usb_raw_ep_io)
+
+/*
+ * Switches the gadget into the configured state.
+ * Returns 0 on success or negative error code on failure.
+ */
+#define USB_RAW_IOCTL_CONFIGURE                _IO('U', 9)
+
+/*
+ * Constrains UDC VBUS power usage.
+ * Accepts current limit in 2 mA units as an argument.
+ * Returns 0 on success or negative error code on failure.
+ */
+#define USB_RAW_IOCTL_VBUS_DRAW                _IOW('U', 10, __u32)
+
+#endif /* _UAPI__LINUX_USB_RAW_GADGET_H */