OSDN Git Service

Merge tag 'phy-for-5.6_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Jan 2020 06:52:26 +0000 (07:52 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 17 Jan 2020 06:52:26 +0000 (07:52 +0100)
Kishon writes:

phy: for 5.6

*) Add support in PHY core to create link between PHY consumer and PHY
   provider
*) Add DisplayPort PHY configuration set to be used for negotiating the
   configurations to be used between DisplayPort controller and
   DisplayPort PHY
*) Add PHY wrapper driver (configure inputs to Cadence Sierra PHY) for
   TI's J721E SoC and adapt Cadence Sierra PHY driver to be used for
   J721E SoC (Supports USB and PCIe)
*) Add PHY driver for eMMC PHY in Intel LGM SoC
*) Add PHY support for 7216 and 7211 Broadcom SoCs which uses the new
   Synopsys USB Controller
*) Add support for 16nm SATA PHY present in Broadcom 7216 SoC
*) Fix lost packet issue, fix MDIO from getting inaccessible, fix
   occasional transaction failures, fix USB driver from crashing in
   Broadcom USB PHY driver
*) Fix missing PCS SW reset in UFS PHY of Qualcomm SM8150
*) Use "struct phy_configure_opts_mipi_dphy" to pass parameters from
   display controller to rockchip-inno-dsidphy
*) Other cleanups including compile testing for some of the PHY drivers,
   fixing Kconfig indentation, duplicate writes in drivers etc.,

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
* tag 'phy-for-5.6_v2' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux-phy: (54 commits)
  dt-bindings: phy: Add PHY_TYPE_DP definition
  phy: ti: j721e-wiz: Fix return value check in wiz_probe()
  dt-bindings: usb: Convert Allwinner A80 USB PHY controller to a schema
  phy: intel-lgm-emmc: Fix warning by adding missing MODULE_LICENSE
  phy: ti: j721e-wiz: Manage typec-gpio-dir
  dt-bindings: phy: ti,phy-j721e-wiz: Add Type-C dir GPIO
  phy: cadence: Sierra: add phy_reset hook
  phy: cadence: Sierra: remove redundant initialization of pointer regmap
  phy: Add DisplayPort configuration options
  phy: Enable compile testing for some of drivers
  phy: mediatek: Fix Kconfig indentation
  phy: intel-lgm-emmc: Add support for eMMC PHY
  dt-bindings: phy: intel-emmc-phy: Add YAML schema for LGM eMMC PHY
  phy: ti: j721e-wiz: Add support for WIZ module present in TI J721E SoC
  dt-bindings: phy: Document WIZ (SERDES wrapper) bindings
  phy: cadence: Sierra: Use correct dev pointer in cdns_sierra_phy_remove()
  phy: cadence: Sierra: Set cmn_refclk_dig_div/cmn_refclk1_dig_div frequency to 25MHz
  phy: cadence: Sierra: Change MAX_LANES of Sierra to 16
  phy: cadence: Sierra: Check for PLL lock during PHY power on
  phy: cadence: Sierra: Get reset control "array" for each link
  ...

38 files changed:
Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/brcm,brcmstb-usb-phy.txt
Documentation/devicetree/bindings/phy/brcm-sata-phy.txt
Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt
Documentation/devicetree/bindings/phy/rockchip,px30-dsi-dphy.yaml
Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt [deleted file]
Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml [new file with mode: 0644]
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/broadcom/Makefile
drivers/phy/broadcom/phy-brcm-sata.c
drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c [new file with mode: 0644]
drivers/phy/broadcom/phy-brcm-usb-init.c
drivers/phy/broadcom/phy-brcm-usb-init.h
drivers/phy/broadcom/phy-brcm-usb.c
drivers/phy/cadence/phy-cadence-sierra.c
drivers/phy/hisilicon/Kconfig
drivers/phy/intel/Kconfig [new file with mode: 0644]
drivers/phy/intel/Makefile [new file with mode: 0644]
drivers/phy/intel/phy-intel-emmc.c [new file with mode: 0644]
drivers/phy/lantiq/phy-lantiq-vrx200-pcie.c
drivers/phy/mediatek/Kconfig
drivers/phy/phy-core.c
drivers/phy/qualcomm/phy-qcom-apq8064-sata.c
drivers/phy/qualcomm/phy-qcom-qmp.c
drivers/phy/qualcomm/phy-qcom-qmp.h
drivers/phy/rockchip/Kconfig
drivers/phy/rockchip/phy-rockchip-inno-dsidphy.c
drivers/phy/ti/Kconfig
drivers/phy/ti/Makefile
drivers/phy/ti/phy-j721e-wiz.c [new file with mode: 0644]
drivers/phy/ti/phy-ti-pipe3.c
drivers/usb/renesas_usbhs/rcar2.c
drivers/usb/renesas_usbhs/rza2.c
include/dt-bindings/phy/phy.h
include/linux/phy/phy-dp.h [new file with mode: 0644]
include/linux/phy/phy.h

diff --git a/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml b/Documentation/devicetree/bindings/phy/allwinner,sun9i-a80-usb-phy.yaml
new file mode 100644 (file)
index 0000000..ded7d6f
--- /dev/null
@@ -0,0 +1,135 @@
+# SPDX-License-Identifier: GPL-2.0
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/allwinner,sun9i-a80-usb-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Allwinner A80 USB PHY Device Tree Bindings
+
+maintainers:
+  - Chen-Yu Tsai <wens@csie.org>
+  - Maxime Ripard <mripard@kernel.org>
+
+properties:
+  "#phy-cells":
+    const: 0
+
+  compatible:
+    const: allwinner,sun9i-a80-usb-phy
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    anyOf:
+      - description: Main PHY Clock
+
+      - items:
+          - description: Main PHY clock
+          - description: HSIC 12MHz clock
+          - description: HSIC 480MHz clock
+
+  clock-names:
+    oneOf:
+      - const: phy
+
+      - items:
+          - const: phy
+          - const: hsic_12M
+          - const: hsic_480M
+
+  resets:
+    anyOf:
+      - description: Normal USB PHY reset
+
+      - items:
+          - description: Normal USB PHY reset
+          - description: HSIC Reset
+
+  reset-names:
+    oneOf:
+      - const: phy
+
+      - items:
+          - const: phy
+          - const: hsic
+
+  phy_type:
+    const: hsic
+    description:
+      When absent, the PHY type will be assumed to be normal USB.
+
+  phy-supply:
+    description:
+      Regulator that powers VBUS
+
+required:
+  - "#phy-cells"
+  - compatible
+  - reg
+  - clocks
+  - clock-names
+  - resets
+  - reset-names
+
+additionalProperties: false
+
+if:
+  properties:
+    phy_type:
+      const: hsic
+
+  required:
+    - phy_type
+
+then:
+  properties:
+    clocks:
+      maxItems: 3
+
+    clock-names:
+      maxItems: 3
+
+    resets:
+      maxItems: 2
+
+    reset-names:
+      maxItems: 2
+
+examples:
+  - |
+    #include <dt-bindings/clock/sun9i-a80-usb.h>
+    #include <dt-bindings/reset/sun9i-a80-usb.h>
+
+    usbphy1: phy@a00800 {
+        compatible = "allwinner,sun9i-a80-usb-phy";
+        reg = <0x00a00800 0x4>;
+        clocks = <&usb_clocks CLK_USB0_PHY>;
+        clock-names = "phy";
+        resets = <&usb_clocks RST_USB0_PHY>;
+        reset-names = "phy";
+        phy-supply = <&reg_usb1_vbus>;
+        #phy-cells = <0>;
+    };
+
+  - |
+    #include <dt-bindings/clock/sun9i-a80-usb.h>
+    #include <dt-bindings/reset/sun9i-a80-usb.h>
+
+    usbphy3: phy@a02800 {
+        compatible = "allwinner,sun9i-a80-usb-phy";
+        reg = <0x00a02800 0x4>;
+        clocks = <&usb_clocks CLK_USB2_PHY>,
+                 <&usb_clocks CLK_USB_HSIC>,
+                 <&usb_clocks CLK_USB2_HSIC>;
+        clock-names = "phy",
+                      "hsic_12M",
+                      "hsic_480M";
+        resets = <&usb_clocks RST_USB2_PHY>,
+                 <&usb_clocks RST_USB2_HSIC>;
+        reset-names = "phy",
+                      "hsic";
+        phy_type = "hsic";
+        phy-supply = <&reg_usb3_vbus>;
+        #phy-cells = <0>;
+    };
index 24a0d06..698aacb 100644 (file)
@@ -1,30 +1,49 @@
 Broadcom STB USB PHY
 
 Required properties:
- - compatible: brcm,brcmstb-usb-phy
- - reg: two offset and length pairs.
-       The first pair specifies a manditory set of memory mapped
-       registers used for general control of the PHY.
-       The second pair specifies optional registers used by some of
-       the SoCs that support USB 3.x
- - #phy-cells: Shall be 1 as it expects one argument for setting
-              the type of the PHY. Possible values are:
-              - PHY_TYPE_USB2 for USB1.1/2.0 PHY
-              - PHY_TYPE_USB3 for USB3.x PHY
+- compatible: should be one of
+       "brcm,brcmstb-usb-phy"
+       "brcm,bcm7216-usb-phy"
+       "brcm,bcm7211-usb-phy"
+
+- reg and reg-names properties requirements are specific to the
+  compatible string.
+  "brcm,brcmstb-usb-phy":
+    - reg: 1 or 2 offset and length pairs. One for the base CTRL registers
+           and an optional pair for systems with USB 3.x support
+    - reg-names: not specified
+  "brcm,bcm7216-usb-phy":
+    - reg: 3 offset and length pairs for CTRL, XHCI_EC and XHCI_GBL
+           registers
+    - reg-names: "ctrl", "xhci_ec", "xhci_gbl"
+  "brcm,bcm7211-usb-phy":
+    - reg: 5 offset and length pairs for CTRL, XHCI_EC, XHCI_GBL,
+           USB_PHY and USB_MDIO registers and an optional pair
+          for the BDC registers
+    - reg-names: "ctrl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
+
+- #phy-cells: Shall be 1 as it expects one argument for setting
+             the type of the PHY. Possible values are:
+             - PHY_TYPE_USB2 for USB1.1/2.0 PHY
+             - PHY_TYPE_USB3 for USB3.x PHY
 
 Optional Properties:
 - clocks : clock phandles.
 - clock-names: String, clock name.
+- interrupts: wakeup interrupt
+- interrupt-names: "wakeup"
 - brcm,ipp: Boolean, Invert Port Power.
   Possible values are: 0 (Don't invert), 1 (Invert)
 - brcm,ioc: Boolean, Invert Over Current detection.
   Possible values are: 0 (Don't invert), 1 (Invert)
-NOTE: one or both of the following two properties must be set
-- brcm,has-xhci: Boolean indicating the phy has an XHCI phy.
-- brcm,has-eohci: Boolean indicating the phy has an EHCI/OHCI phy.
 - dr_mode: String, PHY Device mode.
   Possible values are: "host", "peripheral ", "drd" or "typec-pd"
   If this property is not defined, the phy will default to "host" mode.
+- brcm,syscon-piarbctl: phandle to syscon for handling config registers
+NOTE: one or both of the following two properties must be set
+- brcm,has-xhci: Boolean indicating the phy has an XHCI phy.
+- brcm,has-eohci: Boolean indicating the phy has an EHCI/OHCI phy.
+
 
 Example:
 
@@ -41,3 +60,27 @@ usbphy_0: usb-phy@f0470200 {
        clocks = <&usb20>, <&usb30>;
        clock-names = "sw_usb", "sw_usb3";
 };
+
+usb-phy@29f0200 {
+       reg = <0x29f0200 0x200>,
+               <0x29c0880 0x30>,
+               <0x29cc100 0x534>,
+               <0x2808000 0x24>,
+               <0x2980080 0x8>;
+       reg-names = "ctrl",
+               "xhci_ec",
+               "xhci_gbl",
+               "usb_phy",
+               "usb_mdio";
+       brcm,ioc = <0x0>;
+       brcm,ipp = <0x0>;
+       compatible = "brcm,bcm7211-usb-phy";
+       interrupts = <0x30>;
+       interrupt-parent = <&vpu_intr1_nosec_intc>;
+       interrupt-names = "wake";
+       #phy-cells = <0x1>;
+       brcm,has-xhci;
+       syscon-piarbctl = <&syscon_piarbctl>;
+       clocks = <&scmi_clk 256>;
+       clock-names = "sw_usb";
+};
index b640845..c03ad21 100644 (file)
@@ -2,6 +2,7 @@
 
 Required properties:
 - compatible: should be one or more of
+     "brcm,bcm7216-sata-phy"
      "brcm,bcm7425-sata-phy"
      "brcm,bcm7445-sata-phy"
      "brcm,iproc-ns2-sata-phy"
diff --git a/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml b/Documentation/devicetree/bindings/phy/intel,lgm-emmc-phy.yaml
new file mode 100644 (file)
index 0000000..ff7959c
--- /dev/null
@@ -0,0 +1,56 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/phy/intel,lgm-emmc-phy.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Intel Lightning Mountain(LGM) eMMC PHY Device Tree Bindings
+
+maintainers:
+  - Ramuthevar Vadivel Murugan <vadivel.muruganx.ramuthevar@linux.intel.com>
+
+description: |+
+  Bindings for eMMC PHY on Intel's Lightning Mountain SoC, syscon
+  node is used to reference the base address of eMMC phy registers.
+
+  The eMMC PHY node should be the child of a syscon node with the
+  required property:
+
+  - compatible:         Should be one of the following:
+                        "intel,lgm-syscon", "syscon"
+  - reg:
+      maxItems: 1
+
+properties:
+  compatible:
+      const: intel,lgm-emmc-phy
+
+  "#phy-cells":
+    const: 0
+
+  reg:
+    maxItems: 1
+
+  clocks:
+    maxItems: 1
+
+required:
+  - "#phy-cells"
+  - compatible
+  - reg
+  - clocks
+
+examples:
+  - |
+    sysconf: chiptop@e0200000 {
+      compatible = "intel,lgm-syscon", "syscon";
+      reg = <0xe0200000 0x100>;
+
+      emmc-phy: emmc-phy@a8 {
+        compatible = "intel,lgm-emmc-phy";
+        reg = <0x00a8 0x10>;
+        clocks = <&emmc>;
+        #phy-cells = <0>;
+      };
+    };
+...
index 6e1b47b..03f5939 100644 (file)
@@ -2,21 +2,24 @@ Cadence Sierra PHY
 -----------------------
 
 Required properties:
-- compatible:  cdns,sierra-phy-t0
-- clocks:      Must contain an entry in clock-names.
-               See ../clocks/clock-bindings.txt for details.
-- clock-names: Must be "phy_clk"
+- compatible:  Must be "cdns,sierra-phy-t0" for Sierra in Cadence platform
+               Must be "ti,sierra-phy-t0" for Sierra in TI's J721E SoC.
 - resets:      Must contain an entry for each in reset-names.
                See ../reset/reset.txt for details.
 - reset-names: Must include "sierra_reset" and "sierra_apb".
                "sierra_reset" must control the reset line to the PHY.
                "sierra_apb" must control the reset line to the APB PHY
-               interface.
+               interface ("sierra_apb" is optional).
 - reg:         register range for the PHY.
 - #address-cells: Must be 1
 - #size-cells: Must be 0
 
 Optional properties:
+- clocks:              Must contain an entry in clock-names.
+                       See ../clocks/clock-bindings.txt for details.
+- clock-names:         Must contain "cmn_refclk_dig_div" and
+                       "cmn_refclk1_dig_div" for configuring the frequency of
+                       the clock to the lanes. "phy_clk" is deprecated.
 - cdns,autoconf:       A boolean property whose presence indicates that the
                        PHY registers will be configured by hardware. If not
                        present, all sub-node optional properties must be
index bb0da87..476c56a 100644 (file)
@@ -13,9 +13,6 @@ properties:
   "#phy-cells":
     const: 0
 
-  "#clock-cells":
-    const: 0
-
   compatible:
     enum:
       - rockchip,px30-dsi-dphy
@@ -49,7 +46,6 @@ properties:
 
 required:
   - "#phy-cells"
-  - "#clock-cells"
   - compatible
   - reg
   - clocks
@@ -66,7 +62,6 @@ examples:
         reg = <0x0 0xff2e0000 0x0 0x10000>;
         clocks = <&pmucru 13>, <&cru 12>;
         clock-names = "ref", "pclk";
-        #clock-cells = <0>;
         resets = <&cru 12>;
         reset-names = "apb";
         #phy-cells = <0>;
diff --git a/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt
deleted file mode 100644 (file)
index 64f7109..0000000
+++ /dev/null
@@ -1,37 +0,0 @@
-Allwinner sun9i USB PHY
------------------------
-
-Required properties:
-- compatible : should be one of
-  * allwinner,sun9i-a80-usb-phy
-- reg : a list of offset + length pairs
-- #phy-cells : from the generic phy bindings, must be 0
-- phy_type : "hsic" for HSIC usage;
-            other values or absence of this property indicates normal USB
-- clocks : phandle + clock specifier for the phy clocks
-- clock-names : depending on the "phy_type" property,
-  * "phy" for normal USB
-  * "hsic_480M", "hsic_12M" for HSIC
-- resets : a list of phandle + reset specifier pairs
-- reset-names : depending on the "phy_type" property,
-  * "phy" for normal USB
-  * "hsic" for HSIC
-
-Optional Properties:
-- phy-supply : from the generic phy bindings, a phandle to a regulator that
-              provides power to VBUS.
-
-It is recommended to list all clocks and resets available.
-The driver will only use those matching the phy_type.
-
-Example:
-       usbphy1: phy@a01800 {
-               compatible = "allwinner,sun9i-a80-usb-phy";
-               reg = <0x00a01800 0x4>;
-               clocks = <&usb_phy_clk 2>, <&usb_phy_clk 10>,
-                      <&usb_phy_clk 3>;
-               clock-names = "hsic_480M", "hsic_12M", "phy";
-               resets = <&usb_phy_clk 18>, <&usb_phy_clk 19>;
-               reset-names = "hsic", "phy";
-               #phy-cells = <0>;
-       };
diff --git a/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml b/Documentation/devicetree/bindings/phy/ti,phy-j721e-wiz.yaml
new file mode 100644 (file)
index 0000000..452cee1
--- /dev/null
@@ -0,0 +1,221 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+%YAML 1.2
+---
+$id: "http://devicetree.org/schemas/phy/ti,phy-j721e-wiz.yaml#"
+$schema: "http://devicetree.org/meta-schemas/core.yaml#"
+
+title: TI J721E WIZ (SERDES Wrapper)
+
+maintainers:
+  - Kishon Vijay Abraham I <kishon@ti.com>
+
+properties:
+  compatible:
+    enum:
+      - ti,j721e-wiz-16g
+      - ti,j721e-wiz-10g
+
+  power-domains:
+    maxItems: 1
+
+  clocks:
+    maxItems: 3
+    description: clock-specifier to represent input to the WIZ
+
+  clock-names:
+    items:
+      - const: fck
+      - const: core_ref_clk
+      - const: ext_ref_clk
+
+  num-lanes:
+    minimum: 1
+    maximum: 4
+
+  "#address-cells":
+    const: 1
+
+  "#size-cells":
+    const: 1
+
+  "#reset-cells":
+    const: 1
+
+  ranges: true
+
+  assigned-clocks:
+    maxItems: 2
+
+  assigned-clock-parents:
+    maxItems: 2
+
+  typec-dir-gpios:
+    maxItems: 1
+    description:
+      GPIO to signal Type-C cable orientation for lane swap.
+      If GPIO is active, lane 0 and lane 1 of SERDES will be swapped to
+      achieve the funtionality of an external type-C plug flip mux.
+
+  typec-dir-debounce-ms:
+    minimum: 100
+    maximum: 1000
+    default: 100
+    description:
+      Number of milliseconds to wait before sampling typec-dir-gpio.
+      If not specified, the default debounce of 100ms will be used.
+      Type-C spec states minimum CC pin debounce of 100 ms and maximum
+      of 200 ms. However, some solutions might need more than 200 ms.
+
+patternProperties:
+  "^pll[0|1]-refclk$":
+    type: object
+    description: |
+      WIZ node should have subnodes for each of the PLLs present in
+      the SERDES.
+    properties:
+      clocks:
+        maxItems: 2
+        description: Phandle to clock nodes representing the two inputs to PLL.
+
+      "#clock-cells":
+        const: 0
+
+      assigned-clocks:
+        maxItems: 1
+
+      assigned-clock-parents:
+        maxItems: 1
+
+    required:
+      - clocks
+      - "#clock-cells"
+      - assigned-clocks
+      - assigned-clock-parents
+
+  "^cmn-refclk1?-dig-div$":
+    type: object
+    description:
+      WIZ node should have subnodes for each of the PMA common refclock
+      provided by the SERDES.
+    properties:
+      clocks:
+        maxItems: 1
+        description: Phandle to the clock node representing the input to the
+          divider clock.
+
+      "#clock-cells":
+        const: 0
+
+    required:
+      - clocks
+      - "#clock-cells"
+
+  "^refclk-dig$":
+    type: object
+    description: |
+      WIZ node should have subnode for refclk_dig to select the reference
+      clock source for the reference clock used in the PHY and PMA digital
+      logic.
+    properties:
+      clocks:
+        maxItems: 4
+        description: Phandle to four clock nodes representing the inputs to
+          refclk_dig
+
+      "#clock-cells":
+        const: 0
+
+      assigned-clocks:
+        maxItems: 1
+
+      assigned-clock-parents:
+        maxItems: 1
+
+    required:
+      - clocks
+      - "#clock-cells"
+      - assigned-clocks
+      - assigned-clock-parents
+
+  "^serdes@[0-9a-f]+$":
+    type: object
+    description: |
+      WIZ node should have '1' subnode for the SERDES. It could be either
+      Sierra SERDES or Torrent SERDES. Sierra SERDES should follow the
+      bindings specified in
+      Documentation/devicetree/bindings/phy/phy-cadence-sierra.txt
+      Torrent SERDES should follow the bindings specified in
+      Documentation/devicetree/bindings/phy/phy-cadence-dp.txt
+
+required:
+  - compatible
+  - power-domains
+  - clocks
+  - clock-names
+  - num-lanes
+  - "#address-cells"
+  - "#size-cells"
+  - "#reset-cells"
+  - ranges
+
+examples:
+  - |
+    #include <dt-bindings/soc/ti,sci_pm_domain.h>
+
+    wiz@5000000 {
+           compatible = "ti,j721e-wiz-16g";
+           #address-cells = <1>;
+           #size-cells = <1>;
+           power-domains = <&k3_pds 292 TI_SCI_PD_EXCLUSIVE>;
+           clocks = <&k3_clks 292 5>, <&k3_clks 292 11>, <&dummy_cmn_refclk>;
+           clock-names = "fck", "core_ref_clk", "ext_ref_clk";
+           assigned-clocks = <&k3_clks 292 11>, <&k3_clks 292 0>;
+           assigned-clock-parents = <&k3_clks 292 15>, <&k3_clks 292 4>;
+           num-lanes = <2>;
+           #reset-cells = <1>;
+           ranges = <0x5000000 0x5000000 0x10000>;
+
+           pll0-refclk {
+                  clocks = <&k3_clks 293 13>, <&dummy_cmn_refclk>;
+                  #clock-cells = <0>;
+                  assigned-clocks = <&wiz1_pll0_refclk>;
+                  assigned-clock-parents = <&k3_clks 293 13>;
+           };
+
+           pll1-refclk {
+                  clocks = <&k3_clks 293 0>, <&dummy_cmn_refclk1>;
+                  #clock-cells = <0>;
+                  assigned-clocks = <&wiz1_pll1_refclk>;
+                  assigned-clock-parents = <&k3_clks 293 0>;
+           };
+
+           cmn-refclk-dig-div {
+                  clocks = <&wiz1_refclk_dig>;
+                  #clock-cells = <0>;
+           };
+
+           cmn-refclk1-dig-div {
+                  clocks = <&wiz1_pll1_refclk>;
+                  #clock-cells = <0>;
+           };
+
+           refclk-dig {
+                  clocks = <&k3_clks 292 11>, <&k3_clks 292 0>, <&dummy_cmn_refclk>, <&dummy_cmn_refclk1>;
+                  #clock-cells = <0>;
+                  assigned-clocks = <&wiz0_refclk_dig>;
+                  assigned-clock-parents = <&k3_clks 292 11>;
+           };
+
+           serdes@5000000 {
+                  compatible = "cdns,ti,sierra-phy-t0";
+                  reg-names = "serdes";
+                  reg = <0x5000000 0x10000>;
+                  #address-cells = <1>;
+                  #size-cells = <0>;
+                  resets = <&serdes_wiz0 0>;
+                  reset-names = "sierra_reset";
+                  clocks = <&wiz0_cmn_refclk_dig_div>, <&wiz0_cmn_refclk1_dig_div>;
+                  clock-names = "cmn_refclk_dig_div", "cmn_refclk1_dig_div";
+           };
+    };
index 0263db2..b3ed94b 100644 (file)
@@ -69,5 +69,6 @@ source "drivers/phy/socionext/Kconfig"
 source "drivers/phy/st/Kconfig"
 source "drivers/phy/tegra/Kconfig"
 source "drivers/phy/ti/Kconfig"
+source "drivers/phy/intel/Kconfig"
 
 endmenu
index c96a1af..310c149 100644 (file)
@@ -18,6 +18,7 @@ obj-y                                 += broadcom/    \
                                           cadence/     \
                                           freescale/   \
                                           hisilicon/   \
+                                          intel/       \
                                           lantiq/      \
                                           marvell/     \
                                           motorola/    \
index f453c7d..c78de54 100644 (file)
@@ -8,7 +8,7 @@ obj-$(CONFIG_PHY_NS2_USB_DRD)           += phy-bcm-ns2-usbdrd.o
 obj-$(CONFIG_PHY_BRCM_SATA)            += phy-brcm-sata.o
 obj-$(CONFIG_PHY_BRCM_USB)             += phy-brcm-usb-dvr.o
 
-phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o
+phy-brcm-usb-dvr-objs := phy-brcm-usb.o phy-brcm-usb-init.o phy-brcm-usb-init-synopsys.o
 
 obj-$(CONFIG_PHY_BCM_SR_PCIE)          += phy-bcm-sr-pcie.o
 obj-$(CONFIG_PHY_BCM_SR_USB)           += phy-bcm-sr-usb.o
index 50ac75b..4710cfc 100644 (file)
@@ -33,6 +33,7 @@
 #define SATA_PHY_CTRL_REG_28NM_SPACE_SIZE              0x8
 
 enum brcm_sata_phy_version {
+       BRCM_SATA_PHY_STB_16NM,
        BRCM_SATA_PHY_STB_28NM,
        BRCM_SATA_PHY_STB_40NM,
        BRCM_SATA_PHY_IPROC_NS2,
@@ -104,10 +105,13 @@ enum sata_phy_regs {
        PLL1_ACTRL5                             = 0x85,
        PLL1_ACTRL6                             = 0x86,
        PLL1_ACTRL7                             = 0x87,
+       PLL1_ACTRL8                             = 0x88,
 
        TX_REG_BANK                             = 0x070,
        TX_ACTRL0                               = 0x80,
        TX_ACTRL0_TXPOL_FLIP                    = BIT(6),
+       TX_ACTRL5                               = 0x85,
+       TX_ACTRL5_SSC_EN                        = BIT(11),
 
        AEQRX_REG_BANK_0                        = 0xd0,
        AEQ_CONTROL1                            = 0x81,
@@ -116,6 +120,7 @@ enum sata_phy_regs {
        AEQ_FRC_EQ                              = 0x83,
        AEQ_FRC_EQ_FORCE                        = BIT(0),
        AEQ_FRC_EQ_FORCE_VAL                    = BIT(1),
+       AEQ_RFZ_FRC_VAL                         = BIT(8),
        AEQRX_REG_BANK_1                        = 0xe0,
        AEQRX_SLCAL0_CTRL0                      = 0x82,
        AEQRX_SLCAL1_CTRL0                      = 0x86,
@@ -152,7 +157,28 @@ enum sata_phy_regs {
        TXPMD_TX_FREQ_CTRL_CONTROL3_FMAX_MASK   = 0x3ff,
 
        RXPMD_REG_BANK                          = 0x1c0,
+       RXPMD_RX_CDR_CONTROL1                   = 0x81,
+       RXPMD_RX_PPM_VAL_MASK                   = 0x1ff,
+       RXPMD_RXPMD_EN_FRC                      = BIT(12),
+       RXPMD_RXPMD_EN_FRC_VAL                  = BIT(13),
+       RXPMD_RX_CDR_CDR_PROP_BW                = 0x82,
+       RXPMD_G_CDR_PROP_BW_MASK                = 0x7,
+       RXPMD_G1_CDR_PROP_BW_SHIFT              = 0,
+       RXPMD_G2_CDR_PROP_BW_SHIFT              = 3,
+       RXPMD_G3_CDR_PROB_BW_SHIFT              = 6,
+       RXPMD_RX_CDR_CDR_ACQ_INTEG_BW           = 0x83,
+       RXPMD_G_CDR_ACQ_INT_BW_MASK             = 0x7,
+       RXPMD_G1_CDR_ACQ_INT_BW_SHIFT           = 0,
+       RXPMD_G2_CDR_ACQ_INT_BW_SHIFT           = 3,
+       RXPMD_G3_CDR_ACQ_INT_BW_SHIFT           = 6,
+       RXPMD_RX_CDR_CDR_LOCK_INTEG_BW          = 0x84,
+       RXPMD_G_CDR_LOCK_INT_BW_MASK            = 0x7,
+       RXPMD_G1_CDR_LOCK_INT_BW_SHIFT          = 0,
+       RXPMD_G2_CDR_LOCK_INT_BW_SHIFT          = 3,
+       RXPMD_G3_CDR_LOCK_INT_BW_SHIFT          = 6,
        RXPMD_RX_FREQ_MON_CONTROL1              = 0x87,
+       RXPMD_MON_CORRECT_EN                    = BIT(8),
+       RXPMD_MON_MARGIN_VAL_MASK               = 0xff,
 };
 
 enum sata_phy_ctrl_regs {
@@ -166,6 +192,7 @@ static inline void __iomem *brcm_sata_pcb_base(struct brcm_sata_port *port)
        u32 size = 0;
 
        switch (priv->version) {
+       case BRCM_SATA_PHY_STB_16NM:
        case BRCM_SATA_PHY_STB_28NM:
        case BRCM_SATA_PHY_IPROC_NS2:
        case BRCM_SATA_PHY_DSL_28NM:
@@ -287,6 +314,94 @@ static int brcm_stb_sata_init(struct brcm_sata_port *port)
        return brcm_stb_sata_rxaeq_init(port);
 }
 
+static int brcm_stb_sata_16nm_ssc_init(struct brcm_sata_port *port)
+{
+       void __iomem *base = brcm_sata_pcb_base(port);
+       u32 tmp, value;
+
+       /* Reduce CP tail current to 1/16th of its default value */
+       brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL6, 0, 0x141);
+
+       /* Turn off CP tail current boost */
+       brcm_sata_phy_wr(base, PLL1_REG_BANK, PLL1_ACTRL8, 0, 0xc006);
+
+       /* Set a specific AEQ equalizer value */
+       tmp = AEQ_FRC_EQ_FORCE_VAL | AEQ_FRC_EQ_FORCE;
+       brcm_sata_phy_wr(base, AEQRX_REG_BANK_0, AEQ_FRC_EQ,
+                        ~(tmp | AEQ_RFZ_FRC_VAL |
+                          AEQ_FRC_EQ_VAL_MASK << AEQ_FRC_EQ_VAL_SHIFT),
+                        tmp | 32 << AEQ_FRC_EQ_VAL_SHIFT);
+
+       /* Set RX PPM val center frequency */
+       if (port->ssc_en)
+               value = 0x52;
+       else
+               value = 0;
+       brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CONTROL1,
+                        ~RXPMD_RX_PPM_VAL_MASK, value);
+
+       /* Set proportional loop bandwith Gen1/2/3 */
+       tmp = RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G1_CDR_PROP_BW_SHIFT |
+             RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G2_CDR_PROP_BW_SHIFT |
+             RXPMD_G_CDR_PROP_BW_MASK << RXPMD_G3_CDR_PROB_BW_SHIFT;
+       if (port->ssc_en)
+               value = 2 << RXPMD_G1_CDR_PROP_BW_SHIFT |
+                       2 << RXPMD_G2_CDR_PROP_BW_SHIFT |
+                       2 << RXPMD_G3_CDR_PROB_BW_SHIFT;
+       else
+               value = 1 << RXPMD_G1_CDR_PROP_BW_SHIFT |
+                       1 << RXPMD_G2_CDR_PROP_BW_SHIFT |
+                       1 << RXPMD_G3_CDR_PROB_BW_SHIFT;
+       brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_PROP_BW, ~tmp,
+                        value);
+
+       /* Set CDR integral loop acquisition bandwidth for Gen1/2/3 */
+       tmp = RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G1_CDR_ACQ_INT_BW_SHIFT |
+             RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G2_CDR_ACQ_INT_BW_SHIFT |
+             RXPMD_G_CDR_ACQ_INT_BW_MASK << RXPMD_G3_CDR_ACQ_INT_BW_SHIFT;
+       if (port->ssc_en)
+               value = 1 << RXPMD_G1_CDR_ACQ_INT_BW_SHIFT |
+                       1 << RXPMD_G2_CDR_ACQ_INT_BW_SHIFT |
+                       1 << RXPMD_G3_CDR_ACQ_INT_BW_SHIFT;
+       else
+               value = 0;
+       brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_ACQ_INTEG_BW,
+                        ~tmp, value);
+
+       /* Set CDR integral loop locking bandwidth to 1 for Gen 1/2/3 */
+       tmp = RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G1_CDR_LOCK_INT_BW_SHIFT |
+             RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G2_CDR_LOCK_INT_BW_SHIFT |
+             RXPMD_G_CDR_LOCK_INT_BW_MASK << RXPMD_G3_CDR_LOCK_INT_BW_SHIFT;
+       if (port->ssc_en)
+               value = 1 << RXPMD_G1_CDR_LOCK_INT_BW_SHIFT |
+                       1 << RXPMD_G2_CDR_LOCK_INT_BW_SHIFT |
+                       1 << RXPMD_G3_CDR_LOCK_INT_BW_SHIFT;
+       else
+               value = 0;
+       brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_CDR_CDR_LOCK_INTEG_BW,
+                        ~tmp, value);
+
+       /* Set no guard band and clamp CDR */
+       tmp = RXPMD_MON_CORRECT_EN | RXPMD_MON_MARGIN_VAL_MASK;
+       if (port->ssc_en)
+               value = 0x51;
+       else
+               value = 0;
+       brcm_sata_phy_wr(base, RXPMD_REG_BANK, RXPMD_RX_FREQ_MON_CONTROL1,
+                        ~tmp, RXPMD_MON_CORRECT_EN | value);
+
+       /* Turn on/off SSC */
+       brcm_sata_phy_wr(base, TX_REG_BANK, TX_ACTRL5, ~TX_ACTRL5_SSC_EN,
+                        port->ssc_en ? TX_ACTRL5_SSC_EN : 0);
+
+       return 0;
+}
+
+static int brcm_stb_sata_16nm_init(struct brcm_sata_port *port)
+{
+       return brcm_stb_sata_16nm_ssc_init(port);
+}
+
 /* NS2 SATA PLL1 defaults were characterized by H/W group */
 #define NS2_PLL1_ACTRL2_MAGIC  0x1df8
 #define NS2_PLL1_ACTRL3_MAGIC  0x2b00
@@ -544,6 +659,9 @@ static int brcm_sata_phy_init(struct phy *phy)
        struct brcm_sata_port *port = phy_get_drvdata(phy);
 
        switch (port->phy_priv->version) {
+       case BRCM_SATA_PHY_STB_16NM:
+               rc = brcm_stb_sata_16nm_init(port);
+               break;
        case BRCM_SATA_PHY_STB_28NM:
        case BRCM_SATA_PHY_STB_40NM:
                rc = brcm_stb_sata_init(port);
@@ -601,6 +719,8 @@ static const struct phy_ops phy_ops = {
 };
 
 static const struct of_device_id brcm_sata_phy_of_match[] = {
+       { .compatible   = "brcm,bcm7216-sata-phy",
+         .data = (void *)BRCM_SATA_PHY_STB_16NM },
        { .compatible   = "brcm,bcm7445-sata-phy",
          .data = (void *)BRCM_SATA_PHY_STB_28NM },
        { .compatible   = "brcm,bcm7425-sata-phy",
diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c
new file mode 100644 (file)
index 0000000..456dc4a
--- /dev/null
@@ -0,0 +1,414 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2018, Broadcom */
+
+/*
+ * This module contains USB PHY initialization for power up and S3 resume
+ * for newer Synopsys based USB hardware first used on the bcm7216.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+
+#include <linux/soc/brcmstb/brcmstb.h>
+#include "phy-brcm-usb-init.h"
+
+#define PHY_LOCK_TIMEOUT_MS 200
+
+/* Register definitions for syscon piarbctl registers */
+#define PIARBCTL_CAM                   0x00
+#define PIARBCTL_SPLITTER              0x04
+#define PIARBCTL_MISC                  0x08
+#define   PIARBCTL_MISC_SECURE_MASK                    0x80000000
+#define   PIARBCTL_MISC_USB_SELECT_MASK                        0x40000000
+#define   PIARBCTL_MISC_USB_4G_SDRAM_MASK              0x20000000
+#define   PIARBCTL_MISC_USB_PRIORITY_MASK              0x000f0000
+#define   PIARBCTL_MISC_USB_MEM_PAGE_MASK              0x0000f000
+#define   PIARBCTL_MISC_CAM1_MEM_PAGE_MASK             0x00000f00
+#define   PIARBCTL_MISC_CAM0_MEM_PAGE_MASK             0x000000f0
+#define   PIARBCTL_MISC_SATA_PRIORITY_MASK             0x0000000f
+
+#define PIARBCTL_MISC_USB_ONLY_MASK            \
+       (PIARBCTL_MISC_USB_SELECT_MASK |        \
+        PIARBCTL_MISC_USB_4G_SDRAM_MASK |      \
+        PIARBCTL_MISC_USB_PRIORITY_MASK |      \
+        PIARBCTL_MISC_USB_MEM_PAGE_MASK)
+
+/* Register definitions for the USB CTRL block */
+#define USB_CTRL_SETUP                 0x00
+#define   USB_CTRL_SETUP_STRAP_IPP_SEL_MASK            0x02000000
+#define   USB_CTRL_SETUP_SCB2_EN_MASK                  0x00008000
+#define   USB_CTRL_SETUP_tca_drv_sel_MASK              0x01000000
+#define   USB_CTRL_SETUP_SCB1_EN_MASK                  0x00004000
+#define   USB_CTRL_SETUP_SOFT_SHUTDOWN_MASK            0x00000200
+#define   USB_CTRL_SETUP_IPP_MASK                      0x00000020
+#define   USB_CTRL_SETUP_IOC_MASK                      0x00000010
+#define USB_CTRL_USB_PM                        0x04
+#define   USB_CTRL_USB_PM_USB_PWRDN_MASK               0x80000000
+#define   USB_CTRL_USB_PM_SOFT_RESET_MASK              0x40000000
+#define   USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK         0x00800000
+#define   USB_CTRL_USB_PM_XHC_SOFT_RESETB_MASK         0x00400000
+#define USB_CTRL_USB_PM_STATUS         0x08
+#define USB_CTRL_USB_DEVICE_CTL1       0x10
+#define   USB_CTRL_USB_DEVICE_CTL1_PORT_MODE_MASK      0x00000003
+#define USB_CTRL_TEST_PORT_CTL         0x30
+#define   USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_MASK        0x000000ff
+#define   USB_CTRL_TEST_PORT_CTL_TPOUT_SEL_PME_GEN_MASK        0x0000002e
+#define USB_CTRL_TP_DIAG1              0x34
+#define   USB_CTLR_TP_DIAG1_wake_MASK  0x00000002
+#define USB_CTRL_CTLR_CSHCR            0x50
+#define   USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK  0x00040000
+
+/* Register definitions for the USB_PHY block in 7211b0 */
+#define USB_PHY_PLL_CTL                        0x00
+#define   USB_PHY_PLL_CTL_PLL_RESETB_MASK              0x40000000
+#define USB_PHY_PLL_LDO_CTL            0x08
+#define   USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK         0x00000004
+#define   USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK     0x00000002
+#define   USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK      0x00000001
+#define USB_PHY_UTMI_CTL_1             0x04
+#define   USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK      0x00000800
+#define   USB_PHY_UTMI_CTL_1_PHY_MODE_MASK             0x0000000c
+#define   USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT            2
+#define USB_PHY_IDDQ                   0x1c
+#define   USB_PHY_IDDQ_phy_iddq_MASK                   0x00000001
+#define USB_PHY_STATUS                 0x20
+#define   USB_PHY_STATUS_pll_lock_MASK                 0x00000001
+
+/* Register definitions for the MDIO registers in the DWC2 block of
+ * the 7211b0.
+ * NOTE: The PHY's MDIO registers are only accessible through the
+ * legacy DesignWare USB controller even though it's not being used.
+ */
+#define USB_GMDIOCSR   0
+#define USB_GMDIOGEN   4
+
+/* Register definitions for the BDC EC block in 7211b0 */
+#define BDC_EC_AXIRDA                  0x0c
+#define   BDC_EC_AXIRDA_RTS_MASK                       0xf0000000
+#define   BDC_EC_AXIRDA_RTS_SHIFT                      28
+
+
+static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params,
+                                 uint8_t addr, uint16_t data)
+{
+       void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO];
+
+       addr &= 0x1f; /* 5-bit address */
+       brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN);
+       while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
+               ;
+       brcm_usb_writel(0x59020000 | (addr << 18) | data,
+                       usb_mdio + USB_GMDIOGEN);
+       while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
+               ;
+       brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN);
+       while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
+               ;
+}
+
+static uint16_t __maybe_unused usb_mdio_read_7211b0(
+       struct brcm_usb_init_params *params, uint8_t addr)
+{
+       void __iomem *usb_mdio = params->regs[BRCM_REGS_USB_MDIO];
+
+       addr &= 0x1f; /* 5-bit address */
+       brcm_usb_writel(0xffffffff, usb_mdio + USB_GMDIOGEN);
+       while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
+               ;
+       brcm_usb_writel(0x69020000 | (addr << 18), usb_mdio + USB_GMDIOGEN);
+       while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
+               ;
+       brcm_usb_writel(0x00000000, usb_mdio + USB_GMDIOGEN);
+       while (brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & (1<<31))
+               ;
+       return brcm_usb_readl(usb_mdio + USB_GMDIOCSR) & 0xffff;
+}
+
+static void usb2_eye_fix_7211b0(struct brcm_usb_init_params *params)
+{
+       /* select bank */
+       usb_mdio_write_7211b0(params, 0x1f, 0x80a0);
+
+       /* Set the eye */
+       usb_mdio_write_7211b0(params, 0x0a, 0xc6a0);
+}
+
+static void xhci_soft_reset(struct brcm_usb_init_params *params,
+                       int on_off)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+       /* Assert reset */
+       if (on_off)
+               USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB);
+       /* De-assert reset */
+       else
+               USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB);
+}
+
+static void usb_init_ipp(struct brcm_usb_init_params *params)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+       u32 reg;
+       u32 orig_reg;
+
+       pr_debug("%s\n", __func__);
+
+       orig_reg = reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
+       if (params->ipp != 2)
+               /* override ipp strap pin (if it exits) */
+               reg &= ~(USB_CTRL_MASK(SETUP, STRAP_IPP_SEL));
+
+       /* Override the default OC and PP polarity */
+       reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
+       if (params->ioc)
+               reg |= USB_CTRL_MASK(SETUP, IOC);
+       if (params->ipp == 1)
+               reg |= USB_CTRL_MASK(SETUP, IPP);
+       brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
+
+       /*
+        * If we're changing IPP, make sure power is off long enough
+        * to turn off any connected devices.
+        */
+       if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP))
+               msleep(50);
+}
+
+static void syscon_piarbctl_init(struct regmap *rmap)
+{
+       /* Switch from legacy USB OTG controller to new STB USB controller */
+       regmap_update_bits(rmap, PIARBCTL_MISC, PIARBCTL_MISC_USB_ONLY_MASK,
+                          PIARBCTL_MISC_USB_SELECT_MASK |
+                          PIARBCTL_MISC_USB_4G_SDRAM_MASK);
+}
+
+static void usb_init_common(struct brcm_usb_init_params *params)
+{
+       u32 reg;
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+       pr_debug("%s\n", __func__);
+
+       USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
+       /* 1 millisecond - for USB clocks to settle down */
+       usleep_range(1000, 2000);
+
+       if (USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE)) {
+               reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+               reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
+               reg |= params->mode;
+               brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+       }
+       switch (params->mode) {
+       case USB_CTLR_MODE_HOST:
+               USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
+               break;
+       default:
+               USB_CTRL_UNSET(ctrl, USB_PM, BDC_SOFT_RESETB);
+               USB_CTRL_SET(ctrl, USB_PM, BDC_SOFT_RESETB);
+               break;
+       }
+}
+
+static void usb_wake_enable_7211b0(struct brcm_usb_init_params *params,
+                                  bool enable)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+       if (enable)
+               USB_CTRL_SET(ctrl, CTLR_CSHCR, ctl_pme_en);
+       else
+               USB_CTRL_UNSET(ctrl, CTLR_CSHCR, ctl_pme_en);
+}
+
+static void usb_init_common_7211b0(struct brcm_usb_init_params *params)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+       void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY];
+       void __iomem *bdc_ec = params->regs[BRCM_REGS_BDC_EC];
+       int timeout_ms = PHY_LOCK_TIMEOUT_MS;
+       u32 reg;
+
+       if (params->syscon_piarbctl)
+               syscon_piarbctl_init(params->syscon_piarbctl);
+
+       USB_CTRL_UNSET(ctrl, USB_PM, USB_PWRDN);
+
+       usb_wake_enable_7211b0(params, false);
+       if (!params->wake_enabled) {
+
+               /* undo possible suspend settings */
+               brcm_usb_writel(0, usb_phy + USB_PHY_IDDQ);
+               reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL);
+               reg |= USB_PHY_PLL_CTL_PLL_RESETB_MASK;
+               brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL);
+
+               /* temporarily enable FSM so PHY comes up properly */
+               reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
+               reg |= USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK;
+               brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
+       }
+
+       /* Init the PHY */
+       reg = USB_PHY_PLL_LDO_CTL_AFE_CORERDY_MASK |
+               USB_PHY_PLL_LDO_CTL_AFE_LDO_PWRDWNB_MASK |
+               USB_PHY_PLL_LDO_CTL_AFE_BG_PWRDWNB_MASK;
+       brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_LDO_CTL);
+
+       /* wait for lock */
+       while (timeout_ms-- > 0) {
+               reg = brcm_usb_readl(usb_phy + USB_PHY_STATUS);
+               if (reg & USB_PHY_STATUS_pll_lock_MASK)
+                       break;
+               usleep_range(1000, 2000);
+       }
+
+       /* Set the PHY_MODE */
+       reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
+       reg &= ~USB_PHY_UTMI_CTL_1_PHY_MODE_MASK;
+       reg |= params->mode << USB_PHY_UTMI_CTL_1_PHY_MODE_SHIFT;
+       brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
+
+       /* Fix the incorrect default */
+       reg = brcm_usb_readl(ctrl + USB_CTRL_SETUP);
+       reg &= ~USB_CTRL_SETUP_tca_drv_sel_MASK;
+       brcm_usb_writel(reg, ctrl + USB_CTRL_SETUP);
+
+       usb_init_common(params);
+
+       /*
+        * The BDC controller will get occasional failures with
+        * the default "Read Transaction Size" of 6 (1024 bytes).
+        * Set it to 4 (256 bytes).
+        */
+       if ((params->mode != USB_CTLR_MODE_HOST) && bdc_ec) {
+               reg = brcm_usb_readl(bdc_ec + BDC_EC_AXIRDA);
+               reg &= ~BDC_EC_AXIRDA_RTS_MASK;
+               reg |= (0x4 << BDC_EC_AXIRDA_RTS_SHIFT);
+               brcm_usb_writel(reg, bdc_ec + BDC_EC_AXIRDA);
+       }
+
+       /*
+        * Disable FSM, otherwise the PHY will auto suspend when no
+        * device is connected and will be reset on resume.
+        */
+       reg = brcm_usb_readl(usb_phy + USB_PHY_UTMI_CTL_1);
+       reg &= ~USB_PHY_UTMI_CTL_1_POWER_UP_FSM_EN_MASK;
+       brcm_usb_writel(reg, usb_phy + USB_PHY_UTMI_CTL_1);
+
+       usb2_eye_fix_7211b0(params);
+}
+
+static void usb_init_xhci(struct brcm_usb_init_params *params)
+{
+       pr_debug("%s\n", __func__);
+
+       xhci_soft_reset(params, 0);
+}
+
+static void usb_uninit_common(struct brcm_usb_init_params *params)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+
+       pr_debug("%s\n", __func__);
+
+       USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
+
+}
+
+static void usb_uninit_common_7211b0(struct brcm_usb_init_params *params)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+       void __iomem *usb_phy = params->regs[BRCM_REGS_USB_PHY];
+       u32 reg;
+
+       pr_debug("%s\n", __func__);
+
+       if (params->wake_enabled) {
+               USB_CTRL_SET(ctrl, TEST_PORT_CTL, TPOUT_SEL_PME_GEN);
+               usb_wake_enable_7211b0(params, true);
+       } else {
+               USB_CTRL_SET(ctrl, USB_PM, USB_PWRDN);
+               brcm_usb_writel(0, usb_phy + USB_PHY_PLL_LDO_CTL);
+               reg = brcm_usb_readl(usb_phy + USB_PHY_PLL_CTL);
+               reg &= ~USB_PHY_PLL_CTL_PLL_RESETB_MASK;
+               brcm_usb_writel(reg, usb_phy + USB_PHY_PLL_CTL);
+               brcm_usb_writel(USB_PHY_IDDQ_phy_iddq_MASK,
+                               usb_phy + USB_PHY_IDDQ);
+       }
+
+}
+
+static void usb_uninit_xhci(struct brcm_usb_init_params *params)
+{
+
+       pr_debug("%s\n", __func__);
+
+       if (!params->wake_enabled)
+               xhci_soft_reset(params, 1);
+}
+
+static int usb_get_dual_select(struct brcm_usb_init_params *params)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+       u32 reg = 0;
+
+       pr_debug("%s\n", __func__);
+
+       reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+       reg &= USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
+       return reg;
+}
+
+static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+       u32 reg;
+
+       pr_debug("%s\n", __func__);
+
+       reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+       reg &= ~USB_CTRL_MASK(USB_DEVICE_CTL1, PORT_MODE);
+       reg |= mode;
+       brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+}
+
+static const struct brcm_usb_init_ops bcm7216_ops = {
+       .init_ipp = usb_init_ipp,
+       .init_common = usb_init_common,
+       .init_xhci = usb_init_xhci,
+       .uninit_common = usb_uninit_common,
+       .uninit_xhci = usb_uninit_xhci,
+       .get_dual_select = usb_get_dual_select,
+       .set_dual_select = usb_set_dual_select,
+};
+
+static const struct brcm_usb_init_ops bcm7211b0_ops = {
+       .init_ipp = usb_init_ipp,
+       .init_common = usb_init_common_7211b0,
+       .init_xhci = usb_init_xhci,
+       .uninit_common = usb_uninit_common_7211b0,
+       .uninit_xhci = usb_uninit_xhci,
+       .get_dual_select = usb_get_dual_select,
+       .set_dual_select = usb_set_dual_select,
+};
+
+void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params)
+{
+
+       pr_debug("%s\n", __func__);
+
+       params->family_name = "7216";
+       params->ops = &bcm7216_ops;
+}
+
+void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params)
+{
+
+       pr_debug("%s\n", __func__);
+
+       params->family_name = "7211";
+       params->ops = &bcm7211b0_ops;
+       params->suspend_with_clocks = true;
+}
index 91b5b09..9391ab4 100644 (file)
@@ -42,6 +42,7 @@
 #define   USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK         0x80000000 /* option */
 #define USB_CTRL_EBRIDGE               0x0c
 #define   USB_CTRL_EBRIDGE_ESTOP_SCB_REQ_MASK          0x00020000 /* option */
+#define   USB_CTRL_EBRIDGE_EBR_SCB_SIZE_MASK           0x00000f80 /* option */
 #define USB_CTRL_OBRIDGE               0x10
 #define   USB_CTRL_OBRIDGE_LS_KEEP_ALIVE_MASK          0x08000000
 #define USB_CTRL_MDIO                  0x14
@@ -57,6 +58,8 @@
 #define   USB_CTRL_USB_PM_SOFT_RESET_MASK              0x40000000 /* option */
 #define   USB_CTRL_USB_PM_USB20_HC_RESETB_MASK         0x30000000 /* option */
 #define   USB_CTRL_USB_PM_USB20_HC_RESETB_VAR_MASK     0x00300000 /* option */
+#define   USB_CTRL_USB_PM_RMTWKUP_EN_MASK              0x00000001
+#define USB_CTRL_USB_PM_STATUS         0x38
 #define USB_CTRL_USB30_CTL1            0x60
 #define   USB_CTRL_USB30_CTL1_PHY3_PLL_SEQ_START_MASK  0x00000010
 #define   USB_CTRL_USB30_CTL1_PHY3_RESETB_MASK         0x00010000
@@ -126,10 +129,6 @@ enum {
        USB_CTRL_SELECTOR_COUNT,
 };
 
-#define USB_CTRL_REG(base, reg)        ((void __iomem *)base + USB_CTRL_##reg)
-#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg)
-#define USB_CTRL_MASK(reg, field) \
-       USB_CTRL_##reg##_##field##_MASK
 #define USB_CTRL_MASK_FAMILY(params, reg, field)                       \
        (params->usb_reg_bits_map[USB_CTRL_##reg##_##field##_SELECTOR])
 
@@ -140,13 +139,6 @@ enum {
        usb_ctrl_unset_family(params, USB_CTRL_##reg,   \
                USB_CTRL_##reg##_##field##_SELECTOR)
 
-#define USB_CTRL_SET(base, reg, field) \
-       usb_ctrl_set(USB_CTRL_REG(base, reg),           \
-                    USB_CTRL_##reg##_##field##_MASK)
-#define USB_CTRL_UNSET(base, reg, field)       \
-       usb_ctrl_unset(USB_CTRL_REG(base, reg),         \
-                      USB_CTRL_##reg##_##field##_MASK)
-
 #define MDIO_USB2      0
 #define MDIO_USB3      BIT(31)
 
@@ -176,6 +168,7 @@ static const struct id_to_type id_to_type_table[] = {
        { 0x33900000, BRCM_FAMILY_3390A0 },
        { 0x72500010, BRCM_FAMILY_7250B0 },
        { 0x72600000, BRCM_FAMILY_7260A0 },
+       { 0x72550000, BRCM_FAMILY_7260A0 },
        { 0x72680000, BRCM_FAMILY_7271A0 },
        { 0x72710000, BRCM_FAMILY_7271A0 },
        { 0x73640000, BRCM_FAMILY_7364A0 },
@@ -401,26 +394,14 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = {
        },
 };
 
-static inline u32 brcmusb_readl(void __iomem *addr)
-{
-       return readl(addr);
-}
-
-static inline void brcmusb_writel(u32 val, void __iomem *addr)
-{
-       writel(val, addr);
-}
-
 static inline
 void usb_ctrl_unset_family(struct brcm_usb_init_params *params,
                           u32 reg_offset, u32 field)
 {
        u32 mask;
-       void __iomem *reg;
 
        mask = params->usb_reg_bits_map[field];
-       reg = params->ctrl_regs + reg_offset;
-       brcmusb_writel(brcmusb_readl(reg) & ~mask, reg);
+       brcm_usb_ctrl_unset(params->regs[BRCM_REGS_CTRL] + reg_offset, mask);
 };
 
 static inline
@@ -428,45 +409,27 @@ void usb_ctrl_set_family(struct brcm_usb_init_params *params,
                         u32 reg_offset, u32 field)
 {
        u32 mask;
-       void __iomem *reg;
 
        mask = params->usb_reg_bits_map[field];
-       reg = params->ctrl_regs + reg_offset;
-       brcmusb_writel(brcmusb_readl(reg) | mask, reg);
+       brcm_usb_ctrl_set(params->regs[BRCM_REGS_CTRL] + reg_offset, mask);
 };
 
-static inline void usb_ctrl_set(void __iomem *reg, u32 field)
-{
-       u32 value;
-
-       value = brcmusb_readl(reg);
-       brcmusb_writel(value | field, reg);
-}
-
-static inline void usb_ctrl_unset(void __iomem *reg, u32 field)
-{
-       u32 value;
-
-       value = brcmusb_readl(reg);
-       brcmusb_writel(value & ~field, reg);
-}
-
 static u32 brcmusb_usb_mdio_read(void __iomem *ctrl_base, u32 reg, int mode)
 {
        u32 data;
 
        data = (reg << 16) | mode;
-       brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
+       brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
        data |= (1 << 24);
-       brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
+       brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
        data &= ~(1 << 24);
        /* wait for the 60MHz parallel to serial shifter */
        usleep_range(10, 20);
-       brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
+       brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
        /* wait for the 60MHz parallel to serial shifter */
        usleep_range(10, 20);
 
-       return brcmusb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff;
+       return brcm_usb_readl(USB_CTRL_REG(ctrl_base, MDIO2)) & 0xffff;
 }
 
 static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg,
@@ -475,14 +438,14 @@ static void brcmusb_usb_mdio_write(void __iomem *ctrl_base, u32 reg,
        u32 data;
 
        data = (reg << 16) | val | mode;
-       brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
+       brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
        data |= (1 << 25);
-       brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
+       brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
        data &= ~(1 << 25);
 
        /* wait for the 60MHz parallel to serial shifter */
        usleep_range(10, 20);
-       brcmusb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
+       brcm_usb_writel(data, USB_CTRL_REG(ctrl_base, MDIO));
        /* wait for the 60MHz parallel to serial shifter */
        usleep_range(10, 20);
 }
@@ -581,7 +544,7 @@ static void brcmusb_usb3_pll_54mhz(struct brcm_usb_init_params *params)
 {
        u32 ofs;
        int ii;
-       void __iomem *ctrl_base = params->ctrl_regs;
+       void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL];
 
        /*
         * On newer B53 based SoC's, the reference clock for the
@@ -662,7 +625,7 @@ static void brcmusb_usb3_ssc_enable(void __iomem *ctrl_base)
 
 static void brcmusb_usb3_phy_workarounds(struct brcm_usb_init_params *params)
 {
-       void __iomem *ctrl_base = params->ctrl_regs;
+       void __iomem *ctrl_base = params->regs[BRCM_REGS_CTRL];
 
        brcmusb_usb3_pll_fix(ctrl_base);
        brcmusb_usb3_pll_54mhz(params);
@@ -704,21 +667,21 @@ static void brcmusb_memc_fix(struct brcm_usb_init_params *params)
 
 static void brcmusb_usb3_otp_fix(struct brcm_usb_init_params *params)
 {
-       void __iomem *xhci_ec_base = params->xhci_ec_regs;
+       void __iomem *xhci_ec_base = params->regs[BRCM_REGS_XHCI_EC];
        u32 val;
 
        if (params->family_id != 0x74371000 || !xhci_ec_base)
                return;
-       brcmusb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR));
-       val = brcmusb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
+       brcm_usb_writel(0xa20c, USB_XHCI_EC_REG(xhci_ec_base, IRAADR));
+       val = brcm_usb_readl(USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
 
        /* set cfg_pick_ss_lock */
        val |= (1 << 27);
-       brcmusb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
+       brcm_usb_writel(val, USB_XHCI_EC_REG(xhci_ec_base, IRADAT));
 
        /* Reset USB 3.0 PHY for workaround to take effect */
-       USB_CTRL_UNSET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
-       USB_CTRL_SET(params->ctrl_regs, USB30_CTL1, PHY3_RESETB);
+       USB_CTRL_UNSET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB);
+       USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_CTL1, PHY3_RESETB);
 }
 
 static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params,
@@ -747,7 +710,7 @@ static void brcmusb_xhci_soft_reset(struct brcm_usb_init_params *params,
  *   - default chip/rev.
  * NOTE: The minor rev is always ignored.
  */
-static enum brcm_family_type brcmusb_get_family_type(
+static enum brcm_family_type get_family_type(
        struct brcm_usb_init_params *params)
 {
        int last_type = -1;
@@ -775,9 +738,9 @@ static enum brcm_family_type brcmusb_get_family_type(
        return last_type;
 }
 
-void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
+static void usb_init_ipp(struct brcm_usb_init_params *params)
 {
-       void __iomem *ctrl = params->ctrl_regs;
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
        u32 reg;
        u32 orig_reg;
 
@@ -791,7 +754,7 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
                        USB_CTRL_SET_FAMILY(params, USB30_CTL1, USB3_IPP);
        }
 
-       reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
+       reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
        orig_reg = reg;
        if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_CC_DRD_MODE_ENABLE_SEL))
                /* Never use the strap, it's going away. */
@@ -799,8 +762,8 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
                                              SETUP,
                                              STRAP_CC_DRD_MODE_ENABLE_SEL));
        if (USB_CTRL_MASK_FAMILY(params, SETUP, STRAP_IPP_SEL))
+               /* override ipp strap pin (if it exits) */
                if (params->ipp != 2)
-                       /* override ipp strap pin (if it exits) */
                        reg &= ~(USB_CTRL_MASK_FAMILY(params, SETUP,
                                                      STRAP_IPP_SEL));
 
@@ -808,50 +771,38 @@ void brcm_usb_init_ipp(struct brcm_usb_init_params *params)
        reg &= ~(USB_CTRL_MASK(SETUP, IPP) | USB_CTRL_MASK(SETUP, IOC));
        if (params->ioc)
                reg |= USB_CTRL_MASK(SETUP, IOC);
-       if (params->ipp == 1 && ((reg & USB_CTRL_MASK(SETUP, IPP)) == 0))
+       if (params->ipp == 1)
                reg |= USB_CTRL_MASK(SETUP, IPP);
-       brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
+       brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
 
        /*
         * If we're changing IPP, make sure power is off long enough
         * to turn off any connected devices.
         */
-       if (reg != orig_reg)
+       if ((reg ^ orig_reg) & USB_CTRL_MASK(SETUP, IPP))
                msleep(50);
 }
 
-int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params)
+static void usb_wake_enable(struct brcm_usb_init_params *params,
+                         bool enable)
 {
-       void __iomem *ctrl = params->ctrl_regs;
-       u32 reg = 0;
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
 
-       if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
-               reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
-               reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
-                                       PORT_MODE);
-       }
-       return reg;
+       if (enable)
+               USB_CTRL_SET(ctrl, USB_PM, RMTWKUP_EN);
+       else
+               USB_CTRL_UNSET(ctrl, USB_PM, RMTWKUP_EN);
 }
 
-void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
-                                  int mode)
+static void usb_init_common(struct brcm_usb_init_params *params)
 {
-       void __iomem *ctrl = params->ctrl_regs;
        u32 reg;
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
 
-       if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
-               reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
-               reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
-                                       PORT_MODE);
-               reg |= mode;
-               brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
-       }
-}
-
-void brcm_usb_init_common(struct brcm_usb_init_params *params)
-{
-       u32 reg;
-       void __iomem *ctrl = params->ctrl_regs;
+       /* Clear any pending wake conditions */
+       usb_wake_enable(params, false);
+       reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_PM_STATUS));
+       brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_PM_STATUS));
 
        /* Take USB out of power down */
        if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN)) {
@@ -877,7 +828,7 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
        /* Block auto PLL suspend by USB2 PHY (Sasi) */
        USB_CTRL_SET(ctrl, PLL_CTL, PLL_SUSPEND_EN);
 
-       reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
+       reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
        if (params->selected_family == BRCM_FAMILY_7364A0)
                /* Suppress overcurrent indication from USB30 ports for A0 */
                reg |= USB_CTRL_MASK_FAMILY(params, SETUP, OC3_DISABLE);
@@ -893,16 +844,16 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
                reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB1_EN);
        if (USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN))
                reg |= USB_CTRL_MASK_FAMILY(params, SETUP, SCB2_EN);
-       brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
+       brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
 
        brcmusb_memc_fix(params);
 
        if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
-               reg = brcmusb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+               reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
                reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
                                        PORT_MODE);
                reg |= params->mode;
-               brcmusb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+               brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
        }
        if (USB_CTRL_MASK_FAMILY(params, USB_PM, BDC_SOFT_RESETB)) {
                switch (params->mode) {
@@ -924,10 +875,10 @@ void brcm_usb_init_common(struct brcm_usb_init_params *params)
        }
 }
 
-void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
+static void usb_init_eohci(struct brcm_usb_init_params *params)
 {
        u32 reg;
-       void __iomem *ctrl = params->ctrl_regs;
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
 
        if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
                USB_CTRL_SET_FAMILY(params, USB_PM, USB20_HC_RESETB);
@@ -940,19 +891,30 @@ void brcm_usb_init_eohci(struct brcm_usb_init_params *params)
                USB_CTRL_SET(ctrl, EBRIDGE, ESTOP_SCB_REQ);
 
        /* Setup the endian bits */
-       reg = brcmusb_readl(USB_CTRL_REG(ctrl, SETUP));
+       reg = brcm_usb_readl(USB_CTRL_REG(ctrl, SETUP));
        reg &= ~USB_CTRL_SETUP_ENDIAN_BITS;
        reg |= USB_CTRL_MASK_FAMILY(params, SETUP, ENDIAN);
-       brcmusb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
+       brcm_usb_writel(reg, USB_CTRL_REG(ctrl, SETUP));
 
        if (params->selected_family == BRCM_FAMILY_7271A0)
                /* Enable LS keep alive fix for certain keyboards */
                USB_CTRL_SET(ctrl, OBRIDGE, LS_KEEP_ALIVE);
+
+       if (params->family_id == 0x72550000) {
+               /*
+                * Make the burst size 512 bytes to fix a hardware bug
+                * on the 7255a0. See HW7255-24.
+                */
+               reg = brcm_usb_readl(USB_CTRL_REG(ctrl, EBRIDGE));
+               reg &= ~USB_CTRL_MASK(EBRIDGE, EBR_SCB_SIZE);
+               reg |= 0x800;
+               brcm_usb_writel(reg, USB_CTRL_REG(ctrl, EBRIDGE));
+       }
 }
 
-void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
+static void usb_init_xhci(struct brcm_usb_init_params *params)
 {
-       void __iomem *ctrl = params->ctrl_regs;
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
 
        USB_CTRL_UNSET(ctrl, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
        /* 1 millisecond - for USB clocks to settle down */
@@ -978,34 +940,80 @@ void brcm_usb_init_xhci(struct brcm_usb_init_params *params)
        brcmusb_usb3_otp_fix(params);
 }
 
-void brcm_usb_uninit_common(struct brcm_usb_init_params *params)
+static void usb_uninit_common(struct brcm_usb_init_params *params)
 {
        if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB_PWRDN))
                USB_CTRL_SET_FAMILY(params, USB_PM, USB_PWRDN);
 
        if (USB_CTRL_MASK_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN))
                USB_CTRL_SET_FAMILY(params, PLL_CTL, PLL_IDDQ_PWRDN);
+       if (params->wake_enabled)
+               usb_wake_enable(params, true);
 }
 
-void brcm_usb_uninit_eohci(struct brcm_usb_init_params *params)
+static void usb_uninit_eohci(struct brcm_usb_init_params *params)
 {
-       if (USB_CTRL_MASK_FAMILY(params, USB_PM, USB20_HC_RESETB))
-               USB_CTRL_UNSET_FAMILY(params, USB_PM, USB20_HC_RESETB);
 }
 
-void brcm_usb_uninit_xhci(struct brcm_usb_init_params *params)
+static void usb_uninit_xhci(struct brcm_usb_init_params *params)
 {
        brcmusb_xhci_soft_reset(params, 1);
-       USB_CTRL_SET(params->ctrl_regs, USB30_PCTL, PHY3_IDDQ_OVERRIDE);
+       USB_CTRL_SET(params->regs[BRCM_REGS_CTRL], USB30_PCTL,
+                    PHY3_IDDQ_OVERRIDE);
+}
+
+static int usb_get_dual_select(struct brcm_usb_init_params *params)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+       u32 reg = 0;
+
+       pr_debug("%s\n", __func__);
+       if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
+               reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+               reg &= USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
+                                       PORT_MODE);
+       }
+       return reg;
 }
 
-void brcm_usb_set_family_map(struct brcm_usb_init_params *params)
+static void usb_set_dual_select(struct brcm_usb_init_params *params, int mode)
+{
+       void __iomem *ctrl = params->regs[BRCM_REGS_CTRL];
+       u32 reg;
+
+       pr_debug("%s\n", __func__);
+
+       if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) {
+               reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+               reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1,
+                                       PORT_MODE);
+               reg |= mode;
+               brcm_usb_writel(reg, USB_CTRL_REG(ctrl, USB_DEVICE_CTL1));
+       }
+}
+
+static const struct brcm_usb_init_ops bcm7445_ops = {
+       .init_ipp = usb_init_ipp,
+       .init_common = usb_init_common,
+       .init_eohci = usb_init_eohci,
+       .init_xhci = usb_init_xhci,
+       .uninit_common = usb_uninit_common,
+       .uninit_eohci = usb_uninit_eohci,
+       .uninit_xhci = usb_uninit_xhci,
+       .get_dual_select = usb_get_dual_select,
+       .set_dual_select = usb_set_dual_select,
+};
+
+void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params)
 {
        int fam;
 
-       fam = brcmusb_get_family_type(params);
+       pr_debug("%s\n", __func__);
+
+       fam = get_family_type(params);
        params->selected_family = fam;
        params->usb_reg_bits_map =
                &usb_reg_bits_map_table[fam][0];
        params->family_name = family_names[fam];
+       params->ops = &bcm7445_ops;
 }
index f4f4f6d..899b9eb 100644 (file)
@@ -6,16 +6,50 @@
 #ifndef _USB_BRCM_COMMON_INIT_H
 #define _USB_BRCM_COMMON_INIT_H
 
+#include <linux/regmap.h>
+
 #define USB_CTLR_MODE_HOST 0
 #define USB_CTLR_MODE_DEVICE 1
 #define USB_CTLR_MODE_DRD 2
 #define USB_CTLR_MODE_TYPEC_PD 3
 
+enum brcmusb_reg_sel {
+       BRCM_REGS_CTRL = 0,
+       BRCM_REGS_XHCI_EC,
+       BRCM_REGS_XHCI_GBL,
+       BRCM_REGS_USB_PHY,
+       BRCM_REGS_USB_MDIO,
+       BRCM_REGS_BDC_EC,
+       BRCM_REGS_MAX
+};
+
+#define USB_CTRL_REG(base, reg)        ((void __iomem *)base + USB_CTRL_##reg)
+#define USB_XHCI_EC_REG(base, reg) ((void __iomem *)base + USB_XHCI_EC_##reg)
+#define USB_CTRL_MASK(reg, field) \
+       USB_CTRL_##reg##_##field##_MASK
+#define USB_CTRL_SET(base, reg, field) \
+       brcm_usb_ctrl_set(USB_CTRL_REG(base, reg),      \
+                         USB_CTRL_##reg##_##field##_MASK)
+#define USB_CTRL_UNSET(base, reg, field)       \
+       brcm_usb_ctrl_unset(USB_CTRL_REG(base, reg),            \
+                           USB_CTRL_##reg##_##field##_MASK)
+
 struct  brcm_usb_init_params;
 
+struct brcm_usb_init_ops {
+       void (*init_ipp)(struct brcm_usb_init_params *params);
+       void (*init_common)(struct brcm_usb_init_params *params);
+       void (*init_eohci)(struct brcm_usb_init_params *params);
+       void (*init_xhci)(struct brcm_usb_init_params *params);
+       void (*uninit_common)(struct brcm_usb_init_params *params);
+       void (*uninit_eohci)(struct brcm_usb_init_params *params);
+       void (*uninit_xhci)(struct brcm_usb_init_params *params);
+       int  (*get_dual_select)(struct brcm_usb_init_params *params);
+       void (*set_dual_select)(struct brcm_usb_init_params *params, int mode);
+};
+
 struct  brcm_usb_init_params {
-       void __iomem *ctrl_regs;
-       void __iomem *xhci_ec_regs;
+       void __iomem *regs[BRCM_REGS_MAX];
        int ioc;
        int ipp;
        int mode;
@@ -24,19 +58,105 @@ struct  brcm_usb_init_params {
        int selected_family;
        const char *family_name;
        const u32 *usb_reg_bits_map;
+       const struct brcm_usb_init_ops *ops;
+       struct regmap *syscon_piarbctl;
+       bool wake_enabled;
+       bool suspend_with_clocks;
+};
+
+void brcm_usb_dvr_init_7445(struct brcm_usb_init_params *params);
+void brcm_usb_dvr_init_7216(struct brcm_usb_init_params *params);
+void brcm_usb_dvr_init_7211b0(struct brcm_usb_init_params *params);
+
+static inline u32 brcm_usb_readl(void __iomem *addr)
+{
+       /*
+        * MIPS endianness is configured by boot strap, which also reverses all
+        * bus endianness (i.e., big-endian CPU + big endian bus ==> native
+        * endian I/O).
+        *
+        * Other architectures (e.g., ARM) either do not support big endian, or
+        * else leave I/O in little endian mode.
+        */
+       if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+               return __raw_readl(addr);
+       else
+               return readl_relaxed(addr);
+}
+
+static inline void brcm_usb_writel(u32 val, void __iomem *addr)
+{
+       /* See brcmnand_readl() comments */
+       if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN))
+               __raw_writel(val, addr);
+       else
+               writel_relaxed(val, addr);
+}
+
+static inline void brcm_usb_ctrl_unset(void __iomem *reg, u32 mask)
+{
+       brcm_usb_writel(brcm_usb_readl(reg) & ~(mask), reg);
 };
 
-void brcm_usb_set_family_map(struct brcm_usb_init_params *params);
-int brcm_usb_init_get_dual_select(struct brcm_usb_init_params *params);
-void brcm_usb_init_set_dual_select(struct brcm_usb_init_params *params,
-                                  int mode);
-
-void brcm_usb_init_ipp(struct brcm_usb_init_params *ini);
-void brcm_usb_init_common(struct brcm_usb_init_params *ini);
-void brcm_usb_init_eohci(struct brcm_usb_init_params *ini);
-void brcm_usb_init_xhci(struct brcm_usb_init_params *ini);
-void brcm_usb_uninit_common(struct brcm_usb_init_params *ini);
-void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini);
-void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini);
+static inline void brcm_usb_ctrl_set(void __iomem *reg, u32 mask)
+{
+       brcm_usb_writel(brcm_usb_readl(reg) | (mask), reg);
+};
+
+static inline void brcm_usb_init_ipp(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->init_ipp)
+               ini->ops->init_ipp(ini);
+}
+
+static inline void brcm_usb_init_common(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->init_common)
+               ini->ops->init_common(ini);
+}
+
+static inline void brcm_usb_init_eohci(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->init_eohci)
+               ini->ops->init_eohci(ini);
+}
+
+static inline void brcm_usb_init_xhci(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->init_xhci)
+               ini->ops->init_xhci(ini);
+}
+
+static inline void brcm_usb_uninit_common(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->uninit_common)
+               ini->ops->uninit_common(ini);
+}
+
+static inline void brcm_usb_uninit_eohci(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->uninit_eohci)
+               ini->ops->uninit_eohci(ini);
+}
+
+static inline void brcm_usb_uninit_xhci(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->uninit_xhci)
+               ini->ops->uninit_xhci(ini);
+}
+
+static inline int brcm_usb_get_dual_select(struct brcm_usb_init_params *ini)
+{
+       if (ini->ops->get_dual_select)
+               return ini->ops->get_dual_select(ini);
+       return 0;
+}
+
+static inline void brcm_usb_set_dual_select(struct brcm_usb_init_params *ini,
+       int mode)
+{
+       if (ini->ops->set_dual_select)
+               ini->ops->set_dual_select(ini, mode);
+}
 
 #endif /* _USB_BRCM_COMMON_INIT_H */
index f5c1f29..491bbd4 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/interrupt.h>
 #include <linux/soc/brcmstb/brcmstb.h>
 #include <dt-bindings/phy/phy.h>
+#include <linux/mfd/syscon.h>
 
 #include "phy-brcm-usb-init.h"
 
@@ -32,6 +33,12 @@ struct value_to_name_map {
        const char *name;
 };
 
+struct match_chip_info {
+       void *init_func;
+       u8 required_regs[BRCM_REGS_MAX + 1];
+       u8 optional_reg;
+};
+
 static struct value_to_name_map brcm_dr_mode_to_name[] = {
        { USB_CTLR_MODE_HOST, "host" },
        { USB_CTLR_MODE_DEVICE, "peripheral" },
@@ -57,11 +64,26 @@ struct brcm_usb_phy_data {
        bool                    has_xhci;
        struct clk              *usb_20_clk;
        struct clk              *usb_30_clk;
+       struct clk              *suspend_clk;
        struct mutex            mutex;  /* serialize phy init */
        int                     init_count;
+       int                     wake_irq;
        struct brcm_usb_phy     phys[BRCM_USB_PHY_ID_MAX];
 };
 
+static s8 *node_reg_names[BRCM_REGS_MAX] = {
+       "crtl", "xhci_ec", "xhci_gbl", "usb_phy", "usb_mdio", "bdc_ec"
+};
+
+static irqreturn_t brcm_usb_phy_wake_isr(int irq, void *dev_id)
+{
+       struct phy *gphy = dev_id;
+
+       pm_wakeup_event(&gphy->dev, 0);
+
+       return IRQ_HANDLED;
+}
+
 static int brcm_usb_phy_init(struct phy *gphy)
 {
        struct brcm_usb_phy *phy = phy_get_drvdata(gphy);
@@ -74,8 +96,9 @@ static int brcm_usb_phy_init(struct phy *gphy)
         */
        mutex_lock(&priv->mutex);
        if (priv->init_count++ == 0) {
-               clk_enable(priv->usb_20_clk);
-               clk_enable(priv->usb_30_clk);
+               clk_prepare_enable(priv->usb_20_clk);
+               clk_prepare_enable(priv->usb_30_clk);
+               clk_prepare_enable(priv->suspend_clk);
                brcm_usb_init_common(&priv->ini);
        }
        mutex_unlock(&priv->mutex);
@@ -106,8 +129,9 @@ static int brcm_usb_phy_exit(struct phy *gphy)
        mutex_lock(&priv->mutex);
        if (--priv->init_count == 0) {
                brcm_usb_uninit_common(&priv->ini);
-               clk_disable(priv->usb_20_clk);
-               clk_disable(priv->usb_30_clk);
+               clk_disable_unprepare(priv->usb_20_clk);
+               clk_disable_unprepare(priv->usb_30_clk);
+               clk_disable_unprepare(priv->suspend_clk);
        }
        mutex_unlock(&priv->mutex);
        phy->inited = false;
@@ -194,7 +218,7 @@ static ssize_t dual_select_store(struct device *dev,
        res = name_to_value(&brcm_dual_mode_to_name[0],
                            ARRAY_SIZE(brcm_dual_mode_to_name), buf, &value);
        if (!res) {
-               brcm_usb_init_set_dual_select(&priv->ini, value);
+               brcm_usb_set_dual_select(&priv->ini, value);
                res = len;
        }
        mutex_unlock(&sysfs_lock);
@@ -209,7 +233,7 @@ static ssize_t dual_select_show(struct device *dev,
        int value;
 
        mutex_lock(&sysfs_lock);
-       value = brcm_usb_init_get_dual_select(&priv->ini);
+       value = brcm_usb_get_dual_select(&priv->ini);
        mutex_unlock(&sysfs_lock);
        return sprintf(buf, "%s\n",
                value_to_name(&brcm_dual_mode_to_name[0],
@@ -228,15 +252,106 @@ static const struct attribute_group brcm_usb_phy_group = {
        .attrs = brcm_usb_phy_attrs,
 };
 
-static int brcm_usb_phy_dvr_init(struct device *dev,
+static struct match_chip_info chip_info_7216 = {
+       .init_func = &brcm_usb_dvr_init_7216,
+       .required_regs = {
+               BRCM_REGS_CTRL,
+               BRCM_REGS_XHCI_EC,
+               BRCM_REGS_XHCI_GBL,
+               -1,
+       },
+};
+
+static struct match_chip_info chip_info_7211b0 = {
+       .init_func = &brcm_usb_dvr_init_7211b0,
+       .required_regs = {
+               BRCM_REGS_CTRL,
+               BRCM_REGS_XHCI_EC,
+               BRCM_REGS_XHCI_GBL,
+               BRCM_REGS_USB_PHY,
+               BRCM_REGS_USB_MDIO,
+               -1,
+       },
+       .optional_reg = BRCM_REGS_BDC_EC,
+};
+
+static struct match_chip_info chip_info_7445 = {
+       .init_func = &brcm_usb_dvr_init_7445,
+       .required_regs = {
+               BRCM_REGS_CTRL,
+               BRCM_REGS_XHCI_EC,
+               -1,
+       },
+};
+
+static const struct of_device_id brcm_usb_dt_ids[] = {
+       {
+               .compatible = "brcm,bcm7216-usb-phy",
+               .data = &chip_info_7216,
+       },
+       {
+               .compatible = "brcm,bcm7211-usb-phy",
+               .data = &chip_info_7211b0,
+       },
+       {
+               .compatible = "brcm,brcmstb-usb-phy",
+               .data = &chip_info_7445,
+       },
+       { /* sentinel */ }
+};
+
+static int brcm_usb_get_regs(struct platform_device *pdev,
+                            enum brcmusb_reg_sel regs,
+                            struct  brcm_usb_init_params *ini,
+                            bool optional)
+{
+       struct resource *res;
+
+       /* Older DT nodes have ctrl and optional xhci_ec by index only */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                               node_reg_names[regs]);
+       if (res == NULL) {
+               if (regs == BRCM_REGS_CTRL) {
+                       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               } else if (regs == BRCM_REGS_XHCI_EC) {
+                       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+                       /* XHCI_EC registers are optional */
+                       if (res == NULL)
+                               return 0;
+               }
+               if (res == NULL) {
+                       if (optional) {
+                               dev_dbg(&pdev->dev,
+                                       "Optional reg %s not found\n",
+                                       node_reg_names[regs]);
+                               return 0;
+                       }
+                       dev_err(&pdev->dev, "can't get %s base addr\n",
+                               node_reg_names[regs]);
+                       return 1;
+               }
+       }
+       ini->regs[regs] = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ini->regs[regs])) {
+               dev_err(&pdev->dev, "can't map %s register space\n",
+                       node_reg_names[regs]);
+               return 1;
+       }
+       return 0;
+}
+
+static int brcm_usb_phy_dvr_init(struct platform_device *pdev,
                                 struct brcm_usb_phy_data *priv,
                                 struct device_node *dn)
 {
-       struct phy *gphy;
+       struct device *dev = &pdev->dev;
+       struct phy *gphy = NULL;
        int err;
 
        priv->usb_20_clk = of_clk_get_by_name(dn, "sw_usb");
        if (IS_ERR(priv->usb_20_clk)) {
+               if (PTR_ERR(priv->usb_20_clk) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
                dev_info(dev, "Clock not found in Device Tree\n");
                priv->usb_20_clk = NULL;
        }
@@ -267,6 +382,8 @@ static int brcm_usb_phy_dvr_init(struct device *dev,
 
                priv->usb_30_clk = of_clk_get_by_name(dn, "sw_usb3");
                if (IS_ERR(priv->usb_30_clk)) {
+                       if (PTR_ERR(priv->usb_30_clk) == -EPROBE_DEFER)
+                               return -EPROBE_DEFER;
                        dev_info(dev,
                                 "USB3.0 clock not found in Device Tree\n");
                        priv->usb_30_clk = NULL;
@@ -275,18 +392,46 @@ static int brcm_usb_phy_dvr_init(struct device *dev,
                if (err)
                        return err;
        }
+
+       priv->suspend_clk = clk_get(dev, "usb0_freerun");
+       if (IS_ERR(priv->suspend_clk)) {
+               if (PTR_ERR(priv->suspend_clk) == -EPROBE_DEFER)
+                       return -EPROBE_DEFER;
+               dev_err(dev, "Suspend Clock not found in Device Tree\n");
+               priv->suspend_clk = NULL;
+       }
+
+       priv->wake_irq = platform_get_irq_byname(pdev, "wake");
+       if (priv->wake_irq < 0)
+               priv->wake_irq = platform_get_irq_byname(pdev, "wakeup");
+       if (priv->wake_irq >= 0) {
+               err = devm_request_irq(dev, priv->wake_irq,
+                                      brcm_usb_phy_wake_isr, 0,
+                                      dev_name(dev), gphy);
+               if (err < 0)
+                       return err;
+               device_set_wakeup_capable(dev, 1);
+       } else {
+               dev_info(dev,
+                        "Wake interrupt missing, system wake not supported\n");
+       }
+
        return 0;
 }
 
 static int brcm_usb_phy_probe(struct platform_device *pdev)
 {
-       struct resource *res;
        struct device *dev = &pdev->dev;
        struct brcm_usb_phy_data *priv;
        struct phy_provider *phy_provider;
        struct device_node *dn = pdev->dev.of_node;
        int err;
        const char *mode;
+       const struct of_device_id *match;
+       void (*dvr_init)(struct brcm_usb_init_params *params);
+       const struct match_chip_info *info;
+       struct regmap *rmap;
+       int x;
 
        priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
@@ -295,30 +440,14 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
 
        priv->ini.family_id = brcmstb_get_family_id();
        priv->ini.product_id = brcmstb_get_product_id();
-       brcm_usb_set_family_map(&priv->ini);
+
+       match = of_match_node(brcm_usb_dt_ids, dev->of_node);
+       info = match->data;
+       dvr_init = info->init_func;
+       (*dvr_init)(&priv->ini);
+
        dev_dbg(dev, "Best mapping table is for %s\n",
                priv->ini.family_name);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(dev, "can't get USB_CTRL base address\n");
-               return -EINVAL;
-       }
-       priv->ini.ctrl_regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(priv->ini.ctrl_regs)) {
-               dev_err(dev, "can't map CTRL register space\n");
-               return -EINVAL;
-       }
-
-       /* The XHCI EC registers are optional */
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
-       if (res) {
-               priv->ini.xhci_ec_regs =
-                       devm_ioremap_resource(dev, res);
-               if (IS_ERR(priv->ini.xhci_ec_regs)) {
-                       dev_err(dev, "can't map XHCI EC register space\n");
-                       return -EINVAL;
-               }
-       }
 
        of_property_read_u32(dn, "brcm,ipp", &priv->ini.ipp);
        of_property_read_u32(dn, "brcm,ioc", &priv->ini.ioc);
@@ -335,7 +464,23 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
        if (of_property_read_bool(dn, "brcm,has-eohci"))
                priv->has_eohci = true;
 
-       err = brcm_usb_phy_dvr_init(dev, priv, dn);
+       for (x = 0; x < BRCM_REGS_MAX; x++) {
+               if (info->required_regs[x] >= BRCM_REGS_MAX)
+                       break;
+
+               err = brcm_usb_get_regs(pdev, info->required_regs[x],
+                                       &priv->ini, false);
+               if (err)
+                       return -EINVAL;
+       }
+       if (info->optional_reg) {
+               err = brcm_usb_get_regs(pdev, info->optional_reg,
+                                       &priv->ini, true);
+               if (err)
+                       return -EINVAL;
+       }
+
+       err = brcm_usb_phy_dvr_init(pdev, priv, dn);
        if (err)
                return err;
 
@@ -354,14 +499,23 @@ static int brcm_usb_phy_probe(struct platform_device *pdev)
        if (err)
                dev_warn(dev, "Error creating sysfs attributes\n");
 
+       /* Get piarbctl syscon if it exists */
+       rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                "syscon-piarbctl");
+       if (IS_ERR(rmap))
+               rmap = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                      "brcm,syscon-piarbctl");
+       if (!IS_ERR(rmap))
+               priv->ini.syscon_piarbctl = rmap;
+
        /* start with everything off */
        if (priv->has_xhci)
                brcm_usb_uninit_xhci(&priv->ini);
        if (priv->has_eohci)
                brcm_usb_uninit_eohci(&priv->ini);
        brcm_usb_uninit_common(&priv->ini);
-       clk_disable(priv->usb_20_clk);
-       clk_disable(priv->usb_30_clk);
+       clk_disable_unprepare(priv->usb_20_clk);
+       clk_disable_unprepare(priv->usb_30_clk);
 
        phy_provider = devm_of_phy_provider_register(dev, brcm_usb_phy_xlate);
 
@@ -381,8 +535,28 @@ static int brcm_usb_phy_suspend(struct device *dev)
        struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
 
        if (priv->init_count) {
-               clk_disable(priv->usb_20_clk);
-               clk_disable(priv->usb_30_clk);
+               priv->ini.wake_enabled = device_may_wakeup(dev);
+               if (priv->phys[BRCM_USB_PHY_3_0].inited)
+                       brcm_usb_uninit_xhci(&priv->ini);
+               if (priv->phys[BRCM_USB_PHY_2_0].inited)
+                       brcm_usb_uninit_eohci(&priv->ini);
+               brcm_usb_uninit_common(&priv->ini);
+
+               /*
+                * Handle the clocks unless needed for wake. This has
+                * to work for both older XHCI->3.0-clks, EOHCI->2.0-clks
+                * and newer XHCI->2.0-clks/3.0-clks.
+                */
+
+               if (!priv->ini.suspend_with_clocks) {
+                       if (priv->phys[BRCM_USB_PHY_3_0].inited)
+                               clk_disable_unprepare(priv->usb_30_clk);
+                       if (priv->phys[BRCM_USB_PHY_2_0].inited ||
+                           !priv->has_eohci)
+                               clk_disable_unprepare(priv->usb_20_clk);
+               }
+               if (priv->wake_irq >= 0)
+                       enable_irq_wake(priv->wake_irq);
        }
        return 0;
 }
@@ -391,8 +565,8 @@ static int brcm_usb_phy_resume(struct device *dev)
 {
        struct brcm_usb_phy_data *priv = dev_get_drvdata(dev);
 
-       clk_enable(priv->usb_20_clk);
-       clk_enable(priv->usb_30_clk);
+       clk_prepare_enable(priv->usb_20_clk);
+       clk_prepare_enable(priv->usb_30_clk);
        brcm_usb_init_ipp(&priv->ini);
 
        /*
@@ -400,18 +574,22 @@ static int brcm_usb_phy_resume(struct device *dev)
         * Uninitialize anything that wasn't previously initialized.
         */
        if (priv->init_count) {
+               if (priv->wake_irq >= 0)
+                       disable_irq_wake(priv->wake_irq);
                brcm_usb_init_common(&priv->ini);
                if (priv->phys[BRCM_USB_PHY_2_0].inited) {
                        brcm_usb_init_eohci(&priv->ini);
                } else if (priv->has_eohci) {
                        brcm_usb_uninit_eohci(&priv->ini);
-                       clk_disable(priv->usb_20_clk);
+                       clk_disable_unprepare(priv->usb_20_clk);
                }
                if (priv->phys[BRCM_USB_PHY_3_0].inited) {
                        brcm_usb_init_xhci(&priv->ini);
                } else if (priv->has_xhci) {
                        brcm_usb_uninit_xhci(&priv->ini);
-                       clk_disable(priv->usb_30_clk);
+                       clk_disable_unprepare(priv->usb_30_clk);
+                       if (!priv->has_eohci)
+                               clk_disable_unprepare(priv->usb_20_clk);
                }
        } else {
                if (priv->has_xhci)
@@ -419,10 +597,10 @@ static int brcm_usb_phy_resume(struct device *dev)
                if (priv->has_eohci)
                        brcm_usb_uninit_eohci(&priv->ini);
                brcm_usb_uninit_common(&priv->ini);
-               clk_disable(priv->usb_20_clk);
-               clk_disable(priv->usb_30_clk);
+               clk_disable_unprepare(priv->usb_20_clk);
+               clk_disable_unprepare(priv->usb_30_clk);
        }
-
+       priv->ini.wake_enabled = false;
        return 0;
 }
 #endif /* CONFIG_PM_SLEEP */
@@ -431,11 +609,6 @@ static const struct dev_pm_ops brcm_usb_phy_pm_ops = {
        SET_LATE_SYSTEM_SLEEP_PM_OPS(brcm_usb_phy_suspend, brcm_usb_phy_resume)
 };
 
-static const struct of_device_id brcm_usb_dt_ids[] = {
-       { .compatible = "brcm,brcmstb-usb-phy" },
-       { /* sentinel */ }
-};
-
 MODULE_DEVICE_TABLE(of, brcm_usb_dt_ids);
 
 static struct platform_driver brcm_usb_driver = {
index de10402..a5c08e5 100644 (file)
 #include <dt-bindings/phy/phy.h>
 
 /* PHY register offsets */
-#define SIERRA_PHY_PLL_CFG             (0xc00e << 2)
-#define SIERRA_DET_STANDEC_A           (0x4000 << 2)
-#define SIERRA_DET_STANDEC_B           (0x4001 << 2)
-#define SIERRA_DET_STANDEC_C           (0x4002 << 2)
-#define SIERRA_DET_STANDEC_D           (0x4003 << 2)
-#define SIERRA_DET_STANDEC_E           (0x4004 << 2)
-#define SIERRA_PSM_LANECAL             (0x4008 << 2)
-#define SIERRA_PSM_DIAG                        (0x4015 << 2)
-#define SIERRA_PSC_TX_A0               (0x4028 << 2)
-#define SIERRA_PSC_TX_A1               (0x4029 << 2)
-#define SIERRA_PSC_TX_A2               (0x402A << 2)
-#define SIERRA_PSC_TX_A3               (0x402B << 2)
-#define SIERRA_PSC_RX_A0               (0x4030 << 2)
-#define SIERRA_PSC_RX_A1               (0x4031 << 2)
-#define SIERRA_PSC_RX_A2               (0x4032 << 2)
-#define SIERRA_PSC_RX_A3               (0x4033 << 2)
-#define SIERRA_PLLCTRL_SUBRATE         (0x403A << 2)
-#define SIERRA_PLLCTRL_GEN_D           (0x403E << 2)
-#define SIERRA_DRVCTRL_ATTEN           (0x406A << 2)
-#define SIERRA_CLKPATHCTRL_TMR         (0x4081 << 2)
-#define SIERRA_RX_CREQ_FLTR_A_MODE1    (0x4087 << 2)
-#define SIERRA_RX_CREQ_FLTR_A_MODE0    (0x4088 << 2)
-#define SIERRA_CREQ_CCLKDET_MODE01     (0x408E << 2)
-#define SIERRA_RX_CTLE_MAINTENANCE     (0x4091 << 2)
-#define SIERRA_CREQ_FSMCLK_SEL         (0x4092 << 2)
-#define SIERRA_CTLELUT_CTRL            (0x4098 << 2)
-#define SIERRA_DFE_ECMP_RATESEL                (0x40C0 << 2)
-#define SIERRA_DFE_SMP_RATESEL         (0x40C1 << 2)
-#define SIERRA_DEQ_VGATUNE_CTRL                (0x40E1 << 2)
-#define SIERRA_TMRVAL_MODE3            (0x416E << 2)
-#define SIERRA_TMRVAL_MODE2            (0x416F << 2)
-#define SIERRA_TMRVAL_MODE1            (0x4170 << 2)
-#define SIERRA_TMRVAL_MODE0            (0x4171 << 2)
-#define SIERRA_PICNT_MODE1             (0x4174 << 2)
-#define SIERRA_CPI_OUTBUF_RATESEL      (0x417C << 2)
-#define SIERRA_LFPSFILT_NS             (0x418A << 2)
-#define SIERRA_LFPSFILT_RD             (0x418B << 2)
-#define SIERRA_LFPSFILT_MP             (0x418C << 2)
-#define SIERRA_SDFILT_H2L_A            (0x4191 << 2)
-
-#define SIERRA_MACRO_ID                        0x00007364
-#define SIERRA_MAX_LANES               4
+#define SIERRA_COMMON_CDB_OFFSET                       0x0
+#define SIERRA_MACRO_ID_REG                            0x0
+#define SIERRA_CMN_PLLLC_MODE_PREG                     0x48
+#define SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG           0x49
+#define SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG           0x4A
+#define SIERRA_CMN_PLLLC_LOCK_CNTSTART_PREG            0x4B
+#define SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG              0x4F
+#define SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG              0x50
+#define SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG    0x62
+
+#define SIERRA_LANE_CDB_OFFSET(ln, block_offset, reg_offset)   \
+                               ((0x4000 << (block_offset)) + \
+                                (((ln) << 9) << (reg_offset)))
+
+#define SIERRA_DET_STANDEC_A_PREG                      0x000
+#define SIERRA_DET_STANDEC_B_PREG                      0x001
+#define SIERRA_DET_STANDEC_C_PREG                      0x002
+#define SIERRA_DET_STANDEC_D_PREG                      0x003
+#define SIERRA_DET_STANDEC_E_PREG                      0x004
+#define SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG          0x008
+#define SIERRA_PSM_A0IN_TMR_PREG                       0x009
+#define SIERRA_PSM_DIAG_PREG                           0x015
+#define SIERRA_PSC_TX_A0_PREG                          0x028
+#define SIERRA_PSC_TX_A1_PREG                          0x029
+#define SIERRA_PSC_TX_A2_PREG                          0x02A
+#define SIERRA_PSC_TX_A3_PREG                          0x02B
+#define SIERRA_PSC_RX_A0_PREG                          0x030
+#define SIERRA_PSC_RX_A1_PREG                          0x031
+#define SIERRA_PSC_RX_A2_PREG                          0x032
+#define SIERRA_PSC_RX_A3_PREG                          0x033
+#define SIERRA_PLLCTRL_SUBRATE_PREG                    0x03A
+#define SIERRA_PLLCTRL_GEN_D_PREG                      0x03E
+#define SIERRA_PLLCTRL_CPGAIN_MODE_PREG                        0x03F
+#define SIERRA_PLLCTRL_STATUS_PREG                     0x044
+#define SIERRA_CLKPATH_BIASTRIM_PREG                   0x04B
+#define SIERRA_DFE_BIASTRIM_PREG                       0x04C
+#define SIERRA_DRVCTRL_ATTEN_PREG                      0x06A
+#define SIERRA_CLKPATHCTRL_TMR_PREG                    0x081
+#define SIERRA_RX_CREQ_FLTR_A_MODE3_PREG               0x085
+#define SIERRA_RX_CREQ_FLTR_A_MODE2_PREG               0x086
+#define SIERRA_RX_CREQ_FLTR_A_MODE1_PREG               0x087
+#define SIERRA_RX_CREQ_FLTR_A_MODE0_PREG               0x088
+#define SIERRA_CREQ_CCLKDET_MODE01_PREG                        0x08E
+#define SIERRA_RX_CTLE_MAINTENANCE_PREG                        0x091
+#define SIERRA_CREQ_FSMCLK_SEL_PREG                    0x092
+#define SIERRA_CREQ_EQ_CTRL_PREG                       0x093
+#define SIERRA_CREQ_SPARE_PREG                         0x096
+#define SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG            0x097
+#define SIERRA_CTLELUT_CTRL_PREG                       0x098
+#define SIERRA_DFE_ECMP_RATESEL_PREG                   0x0C0
+#define SIERRA_DFE_SMP_RATESEL_PREG                    0x0C1
+#define SIERRA_DEQ_PHALIGN_CTRL                                0x0C4
+#define SIERRA_DEQ_CONCUR_CTRL1_PREG                   0x0C8
+#define SIERRA_DEQ_CONCUR_CTRL2_PREG                   0x0C9
+#define SIERRA_DEQ_EPIPWR_CTRL2_PREG                   0x0CD
+#define SIERRA_DEQ_FAST_MAINT_CYCLES_PREG              0x0CE
+#define SIERRA_DEQ_ERRCMP_CTRL_PREG                    0x0D0
+#define SIERRA_DEQ_OFFSET_CTRL_PREG                    0x0D8
+#define SIERRA_DEQ_GAIN_CTRL_PREG                      0x0E0
+#define SIERRA_DEQ_VGATUNE_CTRL_PREG                   0x0E1
+#define SIERRA_DEQ_GLUT0                               0x0E8
+#define SIERRA_DEQ_GLUT1                               0x0E9
+#define SIERRA_DEQ_GLUT2                               0x0EA
+#define SIERRA_DEQ_GLUT3                               0x0EB
+#define SIERRA_DEQ_GLUT4                               0x0EC
+#define SIERRA_DEQ_GLUT5                               0x0ED
+#define SIERRA_DEQ_GLUT6                               0x0EE
+#define SIERRA_DEQ_GLUT7                               0x0EF
+#define SIERRA_DEQ_GLUT8                               0x0F0
+#define SIERRA_DEQ_GLUT9                               0x0F1
+#define SIERRA_DEQ_GLUT10                              0x0F2
+#define SIERRA_DEQ_GLUT11                              0x0F3
+#define SIERRA_DEQ_GLUT12                              0x0F4
+#define SIERRA_DEQ_GLUT13                              0x0F5
+#define SIERRA_DEQ_GLUT14                              0x0F6
+#define SIERRA_DEQ_GLUT15                              0x0F7
+#define SIERRA_DEQ_GLUT16                              0x0F8
+#define SIERRA_DEQ_ALUT0                               0x108
+#define SIERRA_DEQ_ALUT1                               0x109
+#define SIERRA_DEQ_ALUT2                               0x10A
+#define SIERRA_DEQ_ALUT3                               0x10B
+#define SIERRA_DEQ_ALUT4                               0x10C
+#define SIERRA_DEQ_ALUT5                               0x10D
+#define SIERRA_DEQ_ALUT6                               0x10E
+#define SIERRA_DEQ_ALUT7                               0x10F
+#define SIERRA_DEQ_ALUT8                               0x110
+#define SIERRA_DEQ_ALUT9                               0x111
+#define SIERRA_DEQ_ALUT10                              0x112
+#define SIERRA_DEQ_ALUT11                              0x113
+#define SIERRA_DEQ_ALUT12                              0x114
+#define SIERRA_DEQ_ALUT13                              0x115
+#define SIERRA_DEQ_DFETAP_CTRL_PREG                    0x128
+#define SIERRA_DFE_EN_1010_IGNORE_PREG                 0x134
+#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG           0x150
+#define SIERRA_DEQ_TAU_CTRL2_PREG                      0x151
+#define SIERRA_DEQ_PICTRL_PREG                         0x161
+#define SIERRA_CPICAL_TMRVAL_MODE1_PREG                        0x170
+#define SIERRA_CPICAL_TMRVAL_MODE0_PREG                        0x171
+#define SIERRA_CPICAL_PICNT_MODE1_PREG                 0x174
+#define SIERRA_CPI_OUTBUF_RATESEL_PREG                 0x17C
+#define SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG                0x183
+#define SIERRA_LFPSDET_SUPPORT_PREG                    0x188
+#define SIERRA_LFPSFILT_NS_PREG                                0x18A
+#define SIERRA_LFPSFILT_RD_PREG                                0x18B
+#define SIERRA_LFPSFILT_MP_PREG                                0x18C
+#define SIERRA_SIGDET_SUPPORT_PREG                     0x190
+#define SIERRA_SDFILT_H2L_A_PREG                       0x191
+#define SIERRA_SDFILT_L2H_PREG                         0x193
+#define SIERRA_RXBUFFER_CTLECTRL_PREG                  0x19E
+#define SIERRA_RXBUFFER_RCDFECTRL_PREG                 0x19F
+#define SIERRA_RXBUFFER_DFECTRL_PREG                   0x1A0
+#define SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG           0x14F
+#define SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG           0x150
+
+#define SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset)    \
+                                     (0xc000 << (block_offset))
+#define SIERRA_PHY_PLL_CFG                             0xe
+
+#define SIERRA_MACRO_ID                                        0x00007364
+#define SIERRA_MAX_LANES                               16
+#define PLL_LOCK_TIME                                  100000
+
+static const struct reg_field macro_id_type =
+                               REG_FIELD(SIERRA_MACRO_ID_REG, 0, 15);
+static const struct reg_field phy_pll_cfg_1 =
+                               REG_FIELD(SIERRA_PHY_PLL_CFG, 1, 1);
+static const struct reg_field pllctrl_lock =
+                               REG_FIELD(SIERRA_PLLCTRL_STATUS_PREG, 0, 0);
 
 struct cdns_sierra_inst {
        struct phy *phy;
@@ -80,53 +166,172 @@ struct cdns_reg_pairs {
 
 struct cdns_sierra_data {
                u32 id_value;
-               u32 pcie_regs;
-               u32 usb_regs;
-               struct cdns_reg_pairs *pcie_vals;
-               struct cdns_reg_pairs  *usb_vals;
+               u8 block_offset_shift;
+               u8 reg_offset_shift;
+               u32 pcie_cmn_regs;
+               u32 pcie_ln_regs;
+               u32 usb_cmn_regs;
+               u32 usb_ln_regs;
+               struct cdns_reg_pairs *pcie_cmn_vals;
+               struct cdns_reg_pairs *pcie_ln_vals;
+               struct cdns_reg_pairs *usb_cmn_vals;
+               struct cdns_reg_pairs *usb_ln_vals;
 };
 
-struct cdns_sierra_phy {
+struct cdns_regmap_cdb_context {
        struct device *dev;
        void __iomem *base;
+       u8 reg_offset_shift;
+};
+
+struct cdns_sierra_phy {
+       struct device *dev;
+       struct regmap *regmap;
        struct cdns_sierra_data *init_data;
        struct cdns_sierra_inst phys[SIERRA_MAX_LANES];
        struct reset_control *phy_rst;
        struct reset_control *apb_rst;
+       struct regmap *regmap_lane_cdb[SIERRA_MAX_LANES];
+       struct regmap *regmap_phy_config_ctrl;
+       struct regmap *regmap_common_cdb;
+       struct regmap_field *macro_id_type;
+       struct regmap_field *phy_pll_cfg_1;
+       struct regmap_field *pllctrl_lock[SIERRA_MAX_LANES];
        struct clk *clk;
+       struct clk *cmn_refclk_dig_div;
+       struct clk *cmn_refclk1_dig_div;
        int nsubnodes;
+       u32 num_lanes;
        bool autoconf;
 };
 
-static void cdns_sierra_phy_init(struct phy *gphy)
+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;
+}
+
+#define SIERRA_LANE_CDB_REGMAP_CONF(n) \
+{ \
+       .name = "sierra_lane" n "_cdb", \
+       .reg_stride = 1, \
+       .fast_io = true, \
+       .reg_write = cdns_regmap_write, \
+       .reg_read = cdns_regmap_read, \
+}
+
+static struct regmap_config cdns_sierra_lane_cdb_config[] = {
+       SIERRA_LANE_CDB_REGMAP_CONF("0"),
+       SIERRA_LANE_CDB_REGMAP_CONF("1"),
+       SIERRA_LANE_CDB_REGMAP_CONF("2"),
+       SIERRA_LANE_CDB_REGMAP_CONF("3"),
+       SIERRA_LANE_CDB_REGMAP_CONF("4"),
+       SIERRA_LANE_CDB_REGMAP_CONF("5"),
+       SIERRA_LANE_CDB_REGMAP_CONF("6"),
+       SIERRA_LANE_CDB_REGMAP_CONF("7"),
+       SIERRA_LANE_CDB_REGMAP_CONF("8"),
+       SIERRA_LANE_CDB_REGMAP_CONF("9"),
+       SIERRA_LANE_CDB_REGMAP_CONF("10"),
+       SIERRA_LANE_CDB_REGMAP_CONF("11"),
+       SIERRA_LANE_CDB_REGMAP_CONF("12"),
+       SIERRA_LANE_CDB_REGMAP_CONF("13"),
+       SIERRA_LANE_CDB_REGMAP_CONF("14"),
+       SIERRA_LANE_CDB_REGMAP_CONF("15"),
+};
+
+static struct regmap_config cdns_sierra_common_cdb_config = {
+       .name = "sierra_common_cdb",
+       .reg_stride = 1,
+       .fast_io = true,
+       .reg_write = cdns_regmap_write,
+       .reg_read = cdns_regmap_read,
+};
+
+static struct regmap_config cdns_sierra_phy_config_ctrl_config = {
+       .name = "sierra_phy_config_ctrl",
+       .reg_stride = 1,
+       .fast_io = true,
+       .reg_write = cdns_regmap_write,
+       .reg_read = cdns_regmap_read,
+};
+
+static int cdns_sierra_phy_init(struct phy *gphy)
 {
        struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
        struct cdns_sierra_phy *phy = dev_get_drvdata(gphy->dev.parent);
+       struct regmap *regmap;
        int i, j;
-       struct cdns_reg_pairs *vals;
-       u32 num_regs;
+       struct cdns_reg_pairs *cmn_vals, *ln_vals;
+       u32 num_cmn_regs, num_ln_regs;
+
+       /* Initialise the PHY registers, unless auto configured */
+       if (phy->autoconf)
+               return 0;
 
+       clk_set_rate(phy->cmn_refclk_dig_div, 25000000);
+       clk_set_rate(phy->cmn_refclk1_dig_div, 25000000);
        if (ins->phy_type == PHY_TYPE_PCIE) {
-               num_regs = phy->init_data->pcie_regs;
-               vals = phy->init_data->pcie_vals;
+               num_cmn_regs = phy->init_data->pcie_cmn_regs;
+               num_ln_regs = phy->init_data->pcie_ln_regs;
+               cmn_vals = phy->init_data->pcie_cmn_vals;
+               ln_vals = phy->init_data->pcie_ln_vals;
        } else if (ins->phy_type == PHY_TYPE_USB3) {
-               num_regs = phy->init_data->usb_regs;
-               vals = phy->init_data->usb_vals;
+               num_cmn_regs = phy->init_data->usb_cmn_regs;
+               num_ln_regs = phy->init_data->usb_ln_regs;
+               cmn_vals = phy->init_data->usb_cmn_vals;
+               ln_vals = phy->init_data->usb_ln_vals;
        } else {
-               return;
+               return -EINVAL;
        }
-       for (i = 0; i < ins->num_lanes; i++)
-               for (j = 0; j < num_regs ; j++)
-                       writel(vals[j].val, phy->base +
-                               vals[j].off + (i + ins->mlane) * 0x800);
+
+       regmap = phy->regmap_common_cdb;
+       for (j = 0; j < num_cmn_regs ; j++)
+               regmap_write(regmap, cmn_vals[j].off, cmn_vals[j].val);
+
+       for (i = 0; i < ins->num_lanes; i++) {
+               for (j = 0; j < num_ln_regs ; j++) {
+                       regmap = phy->regmap_lane_cdb[i + ins->mlane];
+                       regmap_write(regmap, ln_vals[j].off, ln_vals[j].val);
+               }
+       }
+
+       return 0;
 }
 
 static int cdns_sierra_phy_on(struct phy *gphy)
 {
+       struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent);
        struct cdns_sierra_inst *ins = phy_get_drvdata(gphy);
+       struct device *dev = sp->dev;
+       u32 val;
+       int ret;
 
        /* Take the PHY lane group out of reset */
-       return reset_control_deassert(ins->lnk_rst);
+       ret = reset_control_deassert(ins->lnk_rst);
+       if (ret) {
+               dev_err(dev, "Failed to take the PHY lane out of reset\n");
+               return ret;
+       }
+
+       ret = regmap_field_read_poll_timeout(sp->pllctrl_lock[ins->mlane],
+                                            val, val, 1000, PLL_LOCK_TIME);
+       if (ret < 0)
+               dev_err(dev, "PLL lock of lane failed\n");
+
+       return ret;
 }
 
 static int cdns_sierra_phy_off(struct phy *gphy)
@@ -136,9 +341,20 @@ static int cdns_sierra_phy_off(struct phy *gphy)
        return reset_control_assert(ins->lnk_rst);
 }
 
+static int cdns_sierra_phy_reset(struct phy *gphy)
+{
+       struct cdns_sierra_phy *sp = dev_get_drvdata(gphy->dev.parent);
+
+       reset_control_assert(sp->phy_rst);
+       reset_control_deassert(sp->phy_rst);
+       return 0;
+};
+
 static const struct phy_ops ops = {
+       .init           = cdns_sierra_phy_init,
        .power_on       = cdns_sierra_phy_on,
        .power_off      = cdns_sierra_phy_off,
+       .reset          = cdns_sierra_phy_reset,
        .owner          = THIS_MODULE,
 };
 
@@ -159,41 +375,152 @@ static int cdns_sierra_get_optional(struct cdns_sierra_inst *inst,
 
 static const struct of_device_id cdns_sierra_id_table[];
 
+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_sierra_phy *sp)
+{
+       struct device *dev = sp->dev;
+       struct regmap_field *field;
+       struct regmap *regmap;
+       int i;
+
+       regmap = sp->regmap_common_cdb;
+       field = devm_regmap_field_alloc(dev, regmap, macro_id_type);
+       if (IS_ERR(field)) {
+               dev_err(dev, "MACRO_ID_TYPE reg field init failed\n");
+               return PTR_ERR(field);
+       }
+       sp->macro_id_type = field;
+
+       regmap = sp->regmap_phy_config_ctrl;
+       field = devm_regmap_field_alloc(dev, regmap, phy_pll_cfg_1);
+       if (IS_ERR(field)) {
+               dev_err(dev, "PHY_PLL_CFG_1 reg field init failed\n");
+               return PTR_ERR(field);
+       }
+       sp->phy_pll_cfg_1 = field;
+
+       for (i = 0; i < SIERRA_MAX_LANES; i++) {
+               regmap = sp->regmap_lane_cdb[i];
+               field = devm_regmap_field_alloc(dev, regmap, pllctrl_lock);
+               if (IS_ERR(field)) {
+                       dev_err(dev, "P%d_ENABLE reg field init failed\n", i);
+                       return PTR_ERR(field);
+               }
+               sp->pllctrl_lock[i] =  field;
+       }
+
+       return 0;
+}
+
+static int cdns_regmap_init_blocks(struct cdns_sierra_phy *sp,
+                                  void __iomem *base, u8 block_offset_shift,
+                                  u8 reg_offset_shift)
+{
+       struct device *dev = sp->dev;
+       struct regmap *regmap;
+       u32 block_offset;
+       int i;
+
+       for (i = 0; i < SIERRA_MAX_LANES; i++) {
+               block_offset = SIERRA_LANE_CDB_OFFSET(i, block_offset_shift,
+                                                     reg_offset_shift);
+               regmap = cdns_regmap_init(dev, base, block_offset,
+                                         reg_offset_shift,
+                                         &cdns_sierra_lane_cdb_config[i]);
+               if (IS_ERR(regmap)) {
+                       dev_err(dev, "Failed to init lane CDB regmap\n");
+                       return PTR_ERR(regmap);
+               }
+               sp->regmap_lane_cdb[i] = regmap;
+       }
+
+       regmap = cdns_regmap_init(dev, base, SIERRA_COMMON_CDB_OFFSET,
+                                 reg_offset_shift,
+                                 &cdns_sierra_common_cdb_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to init common CDB regmap\n");
+               return PTR_ERR(regmap);
+       }
+       sp->regmap_common_cdb = regmap;
+
+       block_offset = SIERRA_PHY_CONFIG_CTRL_OFFSET(block_offset_shift);
+       regmap = cdns_regmap_init(dev, base, block_offset, reg_offset_shift,
+                                 &cdns_sierra_phy_config_ctrl_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to init PHY config and control regmap\n");
+               return PTR_ERR(regmap);
+       }
+       sp->regmap_phy_config_ctrl = regmap;
+
+       return 0;
+}
+
 static int cdns_sierra_phy_probe(struct platform_device *pdev)
 {
        struct cdns_sierra_phy *sp;
        struct phy_provider *phy_provider;
        struct device *dev = &pdev->dev;
        const struct of_device_id *match;
+       struct cdns_sierra_data *data;
+       unsigned int id_value;
        struct resource *res;
        int i, ret, node = 0;
+       void __iomem *base;
+       struct clk *clk;
        struct device_node *dn = dev->of_node, *child;
 
        if (of_get_child_count(dn) == 0)
                return -ENODEV;
 
+       /* Get init data for this PHY */
+       match = of_match_device(cdns_sierra_id_table, dev);
+       if (!match)
+               return -EINVAL;
+
+       data = (struct cdns_sierra_data *)match->data;
+
        sp = devm_kzalloc(dev, sizeof(*sp), GFP_KERNEL);
        if (!sp)
                return -ENOMEM;
        dev_set_drvdata(dev, sp);
        sp->dev = dev;
+       sp->init_data = data;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       sp->base = devm_ioremap_resource(dev, res);
-       if (IS_ERR(sp->base)) {
+       base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base)) {
                dev_err(dev, "missing \"reg\"\n");
-               return PTR_ERR(sp->base);
+               return PTR_ERR(base);
        }
 
-       /* Get init data for this PHY */
-       match = of_match_device(cdns_sierra_id_table, dev);
-       if (!match)
-               return -EINVAL;
-       sp->init_data = (struct cdns_sierra_data *)match->data;
+       ret = cdns_regmap_init_blocks(sp, base, data->block_offset_shift,
+                                     data->reg_offset_shift);
+       if (ret)
+               return ret;
+
+       ret = cdns_regfield_init(sp);
+       if (ret)
+               return ret;
 
        platform_set_drvdata(pdev, sp);
 
-       sp->clk = devm_clk_get(dev, "phy_clk");
+       sp->clk = devm_clk_get_optional(dev, "phy_clk");
        if (IS_ERR(sp->clk)) {
                dev_err(dev, "failed to get clock phy_clk\n");
                return PTR_ERR(sp->clk);
@@ -205,12 +532,28 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
                return PTR_ERR(sp->phy_rst);
        }
 
-       sp->apb_rst = devm_reset_control_get(dev, "sierra_apb");
+       sp->apb_rst = devm_reset_control_get_optional(dev, "sierra_apb");
        if (IS_ERR(sp->apb_rst)) {
                dev_err(dev, "failed to get apb reset\n");
                return PTR_ERR(sp->apb_rst);
        }
 
+       clk = devm_clk_get_optional(dev, "cmn_refclk_dig_div");
+       if (IS_ERR(clk)) {
+               dev_err(dev, "cmn_refclk_dig_div clock not found\n");
+               ret = PTR_ERR(clk);
+               return ret;
+       }
+       sp->cmn_refclk_dig_div = clk;
+
+       clk = devm_clk_get_optional(dev, "cmn_refclk1_dig_div");
+       if (IS_ERR(clk)) {
+               dev_err(dev, "cmn_refclk1_dig_div clock not found\n");
+               ret = PTR_ERR(clk);
+               return ret;
+       }
+       sp->cmn_refclk1_dig_div = clk;
+
        ret = clk_prepare_enable(sp->clk);
        if (ret)
                return ret;
@@ -219,7 +562,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
        reset_control_deassert(sp->apb_rst);
 
        /* Check that PHY is present */
-       if  (sp->init_data->id_value != readl(sp->base)) {
+       regmap_field_read(sp->macro_id_type, &id_value);
+       if  (sp->init_data->id_value != id_value) {
                ret = -EINVAL;
                goto clk_disable;
        }
@@ -230,7 +574,7 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
                struct phy *gphy;
 
                sp->phys[node].lnk_rst =
-                       of_reset_control_get_exclusive_by_index(child, 0);
+                       of_reset_control_array_get_exclusive(child);
 
                if (IS_ERR(sp->phys[node].lnk_rst)) {
                        dev_err(dev, "failed to get reset %s\n",
@@ -248,6 +592,8 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
                        }
                }
 
+               sp->num_lanes += sp->phys[node].num_lanes;
+
                gphy = devm_phy_create(dev, child, &ops);
 
                if (IS_ERR(gphy)) {
@@ -257,17 +603,18 @@ static int cdns_sierra_phy_probe(struct platform_device *pdev)
                sp->phys[node].phy = gphy;
                phy_set_drvdata(gphy, &sp->phys[node]);
 
-               /* Initialise the PHY registers, unless auto configured */
-               if (!sp->autoconf)
-                       cdns_sierra_phy_init(gphy);
-
                node++;
        }
        sp->nsubnodes = node;
 
+       if (sp->num_lanes > SIERRA_MAX_LANES) {
+               dev_err(dev, "Invalid lane configuration\n");
+               goto put_child2;
+       }
+
        /* If more than one subnode, configure the PHY as multilink */
        if (!sp->autoconf && sp->nsubnodes > 1)
-               writel(2, sp->base + SIERRA_PHY_PLL_CFG);
+               regmap_field_write(sp->phy_pll_cfg_1, 0x1);
 
        pm_runtime_enable(dev);
        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
@@ -288,7 +635,7 @@ clk_disable:
 
 static int cdns_sierra_phy_remove(struct platform_device *pdev)
 {
-       struct cdns_sierra_phy *phy = dev_get_drvdata(pdev->dev.parent);
+       struct cdns_sierra_phy *phy = platform_get_drvdata(pdev);
        int i;
 
        reset_control_assert(phy->phy_rst);
@@ -306,68 +653,158 @@ static int cdns_sierra_phy_remove(struct platform_device *pdev)
        return 0;
 }
 
-static struct cdns_reg_pairs cdns_usb_regs[] = {
-       /*
-        * Write USB configuration parameters to the PHY.
-        * These values are specific to this specific hardware
-        * configuration.
-        */
-       {0xFE0A, SIERRA_DET_STANDEC_A},
-       {0x000F, SIERRA_DET_STANDEC_B},
-       {0x55A5, SIERRA_DET_STANDEC_C},
-       {0x69AD, SIERRA_DET_STANDEC_D},
-       {0x0241, SIERRA_DET_STANDEC_E},
-       {0x0110, SIERRA_PSM_LANECAL},
-       {0xCF00, SIERRA_PSM_DIAG},
-       {0x001F, SIERRA_PSC_TX_A0},
-       {0x0007, SIERRA_PSC_TX_A1},
-       {0x0003, SIERRA_PSC_TX_A2},
-       {0x0003, SIERRA_PSC_TX_A3},
-       {0x0FFF, SIERRA_PSC_RX_A0},
-       {0x0003, SIERRA_PSC_RX_A1},
-       {0x0003, SIERRA_PSC_RX_A2},
-       {0x0001, SIERRA_PSC_RX_A3},
-       {0x0001, SIERRA_PLLCTRL_SUBRATE},
-       {0x0406, SIERRA_PLLCTRL_GEN_D},
-       {0x0000, SIERRA_DRVCTRL_ATTEN},
-       {0x823E, SIERRA_CLKPATHCTRL_TMR},
-       {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE1},
-       {0x078F, SIERRA_RX_CREQ_FLTR_A_MODE0},
-       {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01},
-       {0x023C, SIERRA_RX_CTLE_MAINTENANCE},
-       {0x3232, SIERRA_CREQ_FSMCLK_SEL},
-       {0x8452, SIERRA_CTLELUT_CTRL},
-       {0x4121, SIERRA_DFE_ECMP_RATESEL},
-       {0x4121, SIERRA_DFE_SMP_RATESEL},
-       {0x9999, SIERRA_DEQ_VGATUNE_CTRL},
-       {0x0330, SIERRA_TMRVAL_MODE0},
-       {0x01FF, SIERRA_PICNT_MODE1},
-       {0x0009, SIERRA_CPI_OUTBUF_RATESEL},
-       {0x000F, SIERRA_LFPSFILT_NS},
-       {0x0009, SIERRA_LFPSFILT_RD},
-       {0x0001, SIERRA_LFPSFILT_MP},
-       {0x8013, SIERRA_SDFILT_H2L_A},
-       {0x0400, SIERRA_TMRVAL_MODE1},
+/* refclk100MHz_32b_PCIe_cmn_pll_ext_ssc */
+static struct cdns_reg_pairs cdns_pcie_cmn_regs_ext_ssc[] = {
+       {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
+       {0x2106, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
+       {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE1_PREG},
+       {0x8A06, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
+       {0x1B1B, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}
 };
 
-static struct cdns_reg_pairs cdns_pcie_regs[] = {
-       /*
-        * Write PCIe configuration parameters to the PHY.
-        * These values are specific to this specific hardware
-        * configuration.
-        */
-       {0x891f, SIERRA_DET_STANDEC_D},
-       {0x0053, SIERRA_DET_STANDEC_E},
-       {0x0400, SIERRA_TMRVAL_MODE2},
-       {0x0200, SIERRA_TMRVAL_MODE3},
+/* refclk100MHz_32b_PCIe_ln_ext_ssc */
+static struct cdns_reg_pairs cdns_pcie_ln_regs_ext_ssc[] = {
+       {0x813E, SIERRA_CLKPATHCTRL_TMR_PREG},
+       {0x8047, SIERRA_RX_CREQ_FLTR_A_MODE3_PREG},
+       {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE2_PREG},
+       {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+       {0x808F, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+       {0x033C, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+       {0x44CC, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG}
+};
+
+/* refclk100MHz_20b_USB_cmn_pll_ext_ssc */
+static struct cdns_reg_pairs cdns_usb_cmn_regs_ext_ssc[] = {
+       {0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE1_PREG},
+       {0x2085, SIERRA_CMN_PLLLC_LF_COEFF_MODE0_PREG},
+       {0x0000, SIERRA_CMN_PLLLC_BWCAL_MODE0_PREG},
+       {0x0000, SIERRA_CMN_PLLLC_SS_TIME_STEPSIZE_MODE_PREG}
+};
+
+/* refclk100MHz_20b_USB_ln_ext_ssc */
+static struct cdns_reg_pairs cdns_usb_ln_regs_ext_ssc[] = {
+       {0xFE0A, SIERRA_DET_STANDEC_A_PREG},
+       {0x000F, SIERRA_DET_STANDEC_B_PREG},
+       {0x00A5, SIERRA_DET_STANDEC_C_PREG},
+       {0x69ad, SIERRA_DET_STANDEC_D_PREG},
+       {0x0241, SIERRA_DET_STANDEC_E_PREG},
+       {0x0010, SIERRA_PSM_LANECAL_DLY_A1_RESETS_PREG},
+       {0x0014, SIERRA_PSM_A0IN_TMR_PREG},
+       {0xCF00, SIERRA_PSM_DIAG_PREG},
+       {0x001F, SIERRA_PSC_TX_A0_PREG},
+       {0x0007, SIERRA_PSC_TX_A1_PREG},
+       {0x0003, SIERRA_PSC_TX_A2_PREG},
+       {0x0003, SIERRA_PSC_TX_A3_PREG},
+       {0x0FFF, SIERRA_PSC_RX_A0_PREG},
+       {0x0619, SIERRA_PSC_RX_A1_PREG},
+       {0x0003, SIERRA_PSC_RX_A2_PREG},
+       {0x0001, SIERRA_PSC_RX_A3_PREG},
+       {0x0001, SIERRA_PLLCTRL_SUBRATE_PREG},
+       {0x0406, SIERRA_PLLCTRL_GEN_D_PREG},
+       {0x5233, SIERRA_PLLCTRL_CPGAIN_MODE_PREG},
+       {0x00CA, SIERRA_CLKPATH_BIASTRIM_PREG},
+       {0x2512, SIERRA_DFE_BIASTRIM_PREG},
+       {0x0000, SIERRA_DRVCTRL_ATTEN_PREG},
+       {0x873E, SIERRA_CLKPATHCTRL_TMR_PREG},
+       {0x03CF, SIERRA_RX_CREQ_FLTR_A_MODE1_PREG},
+       {0x01CE, SIERRA_RX_CREQ_FLTR_A_MODE0_PREG},
+       {0x7B3C, SIERRA_CREQ_CCLKDET_MODE01_PREG},
+       {0x033F, SIERRA_RX_CTLE_MAINTENANCE_PREG},
+       {0x3232, SIERRA_CREQ_FSMCLK_SEL_PREG},
+       {0x0000, SIERRA_CREQ_EQ_CTRL_PREG},
+       {0x8000, SIERRA_CREQ_SPARE_PREG},
+       {0xCC44, SIERRA_CREQ_EQ_OPEN_EYE_THRESH_PREG},
+       {0x8453, SIERRA_CTLELUT_CTRL_PREG},
+       {0x4110, SIERRA_DFE_ECMP_RATESEL_PREG},
+       {0x4110, SIERRA_DFE_SMP_RATESEL_PREG},
+       {0x0002, SIERRA_DEQ_PHALIGN_CTRL},
+       {0x3200, SIERRA_DEQ_CONCUR_CTRL1_PREG},
+       {0x5064, SIERRA_DEQ_CONCUR_CTRL2_PREG},
+       {0x0030, SIERRA_DEQ_EPIPWR_CTRL2_PREG},
+       {0x0048, SIERRA_DEQ_FAST_MAINT_CYCLES_PREG},
+       {0x5A5A, SIERRA_DEQ_ERRCMP_CTRL_PREG},
+       {0x02F5, SIERRA_DEQ_OFFSET_CTRL_PREG},
+       {0x02F5, SIERRA_DEQ_GAIN_CTRL_PREG},
+       {0x9A8A, SIERRA_DEQ_VGATUNE_CTRL_PREG},
+       {0x0014, SIERRA_DEQ_GLUT0},
+       {0x0014, SIERRA_DEQ_GLUT1},
+       {0x0014, SIERRA_DEQ_GLUT2},
+       {0x0014, SIERRA_DEQ_GLUT3},
+       {0x0014, SIERRA_DEQ_GLUT4},
+       {0x0014, SIERRA_DEQ_GLUT5},
+       {0x0014, SIERRA_DEQ_GLUT6},
+       {0x0014, SIERRA_DEQ_GLUT7},
+       {0x0014, SIERRA_DEQ_GLUT8},
+       {0x0014, SIERRA_DEQ_GLUT9},
+       {0x0014, SIERRA_DEQ_GLUT10},
+       {0x0014, SIERRA_DEQ_GLUT11},
+       {0x0014, SIERRA_DEQ_GLUT12},
+       {0x0014, SIERRA_DEQ_GLUT13},
+       {0x0014, SIERRA_DEQ_GLUT14},
+       {0x0014, SIERRA_DEQ_GLUT15},
+       {0x0014, SIERRA_DEQ_GLUT16},
+       {0x0BAE, SIERRA_DEQ_ALUT0},
+       {0x0AEB, SIERRA_DEQ_ALUT1},
+       {0x0A28, SIERRA_DEQ_ALUT2},
+       {0x0965, SIERRA_DEQ_ALUT3},
+       {0x08A2, SIERRA_DEQ_ALUT4},
+       {0x07DF, SIERRA_DEQ_ALUT5},
+       {0x071C, SIERRA_DEQ_ALUT6},
+       {0x0659, SIERRA_DEQ_ALUT7},
+       {0x0596, SIERRA_DEQ_ALUT8},
+       {0x0514, SIERRA_DEQ_ALUT9},
+       {0x0492, SIERRA_DEQ_ALUT10},
+       {0x0410, SIERRA_DEQ_ALUT11},
+       {0x038E, SIERRA_DEQ_ALUT12},
+       {0x030C, SIERRA_DEQ_ALUT13},
+       {0x03F4, SIERRA_DEQ_DFETAP_CTRL_PREG},
+       {0x0001, SIERRA_DFE_EN_1010_IGNORE_PREG},
+       {0x3C01, SIERRA_DEQ_TAU_CTRL1_FAST_MAINT_PREG},
+       {0x3C40, SIERRA_DEQ_TAU_CTRL1_SLOW_MAINT_PREG},
+       {0x1C08, SIERRA_DEQ_TAU_CTRL2_PREG},
+       {0x0033, SIERRA_DEQ_PICTRL_PREG},
+       {0x0400, SIERRA_CPICAL_TMRVAL_MODE1_PREG},
+       {0x0330, SIERRA_CPICAL_TMRVAL_MODE0_PREG},
+       {0x01FF, SIERRA_CPICAL_PICNT_MODE1_PREG},
+       {0x0009, SIERRA_CPI_OUTBUF_RATESEL_PREG},
+       {0x3232, SIERRA_CPICAL_RES_STARTCODE_MODE23_PREG},
+       {0x0005, SIERRA_LFPSDET_SUPPORT_PREG},
+       {0x000F, SIERRA_LFPSFILT_NS_PREG},
+       {0x0009, SIERRA_LFPSFILT_RD_PREG},
+       {0x0001, SIERRA_LFPSFILT_MP_PREG},
+       {0x8013, SIERRA_SDFILT_H2L_A_PREG},
+       {0x8009, SIERRA_SDFILT_L2H_PREG},
+       {0x0024, SIERRA_RXBUFFER_CTLECTRL_PREG},
+       {0x0020, SIERRA_RXBUFFER_RCDFECTRL_PREG},
+       {0x4243, SIERRA_RXBUFFER_DFECTRL_PREG}
 };
 
 static const struct cdns_sierra_data cdns_map_sierra = {
        SIERRA_MACRO_ID,
-       ARRAY_SIZE(cdns_pcie_regs),
-       ARRAY_SIZE(cdns_usb_regs),
-       cdns_pcie_regs,
-       cdns_usb_regs
+       0x2,
+       0x2,
+       ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
+       ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
+       ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
+       ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
+       cdns_pcie_cmn_regs_ext_ssc,
+       cdns_pcie_ln_regs_ext_ssc,
+       cdns_usb_cmn_regs_ext_ssc,
+       cdns_usb_ln_regs_ext_ssc,
+};
+
+static const struct cdns_sierra_data cdns_ti_map_sierra = {
+       SIERRA_MACRO_ID,
+       0x0,
+       0x1,
+       ARRAY_SIZE(cdns_pcie_cmn_regs_ext_ssc),
+       ARRAY_SIZE(cdns_pcie_ln_regs_ext_ssc),
+       ARRAY_SIZE(cdns_usb_cmn_regs_ext_ssc),
+       ARRAY_SIZE(cdns_usb_ln_regs_ext_ssc),
+       cdns_pcie_cmn_regs_ext_ssc,
+       cdns_pcie_ln_regs_ext_ssc,
+       cdns_usb_cmn_regs_ext_ssc,
+       cdns_usb_ln_regs_ext_ssc,
 };
 
 static const struct of_device_id cdns_sierra_id_table[] = {
@@ -375,6 +812,10 @@ static const struct of_device_id cdns_sierra_id_table[] = {
                .compatible = "cdns,sierra-phy-t0",
                .data = &cdns_map_sierra,
        },
+       {
+               .compatible = "ti,sierra-phy-t0",
+               .data = &cdns_ti_map_sierra,
+       },
        {}
 };
 MODULE_DEVICE_TABLE(of, cdns_sierra_id_table);
index 534e393..1c73053 100644 (file)
@@ -33,14 +33,14 @@ config PHY_HISTB_COMBPHY
          If unsure, say N.
 
 config PHY_HISI_INNO_USB2
-       tristate "HiSilicon INNO USB2 PHY support"
-       depends on (ARCH_HISI && ARM64) || COMPILE_TEST
-       select GENERIC_PHY
-       select MFD_SYSCON
-       help
-         Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports
-         USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one
-         USB host port to accept one USB device.
+       tristate "HiSilicon INNO USB2 PHY support"
+       depends on (ARCH_HISI && ARM64) || COMPILE_TEST
+       select GENERIC_PHY
+       select MFD_SYSCON
+       help
+         Support for INNO USB2 PHY on HiSilicon SoCs. This Phy supports
+         USB 1.5Mb/s, USB 12Mb/s, USB 480Mb/s speeds. It supports one
+         USB host port to accept one USB device.
 
 config PHY_HIX5HD2_SATA
        tristate "HIX5HD2 SATA PHY Driver"
diff --git a/drivers/phy/intel/Kconfig b/drivers/phy/intel/Kconfig
new file mode 100644 (file)
index 0000000..4ea6a88
--- /dev/null
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Phy drivers for Intel Lightning Mountain(LGM) platform
+#
+config PHY_INTEL_EMMC
+       tristate "Intel EMMC PHY driver"
+       select GENERIC_PHY
+       help
+         Enable this to support the Intel EMMC PHY
diff --git a/drivers/phy/intel/Makefile b/drivers/phy/intel/Makefile
new file mode 100644 (file)
index 0000000..6b876a7
--- /dev/null
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_PHY_INTEL_EMMC)            += phy-intel-emmc.o
diff --git a/drivers/phy/intel/phy-intel-emmc.c b/drivers/phy/intel/phy-intel-emmc.c
new file mode 100644 (file)
index 0000000..703aeb1
--- /dev/null
@@ -0,0 +1,284 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Intel eMMC PHY driver
+ * Copyright (C) 2019 Intel, Corp.
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+/* eMMC phy register definitions */
+#define EMMC_PHYCTRL0_REG      0xa8
+#define DR_TY_MASK             GENMASK(30, 28)
+#define DR_TY_SHIFT(x)         (((x) << 28) & DR_TY_MASK)
+#define OTAPDLYENA             BIT(14)
+#define OTAPDLYSEL_MASK                GENMASK(13, 10)
+#define OTAPDLYSEL_SHIFT(x)    (((x) << 10) & OTAPDLYSEL_MASK)
+
+#define EMMC_PHYCTRL1_REG      0xac
+#define PDB_MASK               BIT(0)
+#define PDB_SHIFT(x)           (((x) << 0) & PDB_MASK)
+#define ENDLL_MASK             BIT(7)
+#define ENDLL_SHIFT(x)         (((x) << 7) & ENDLL_MASK)
+
+#define EMMC_PHYCTRL2_REG      0xb0
+#define FRQSEL_25M             0
+#define FRQSEL_50M             1
+#define FRQSEL_100M            2
+#define FRQSEL_150M            3
+#define FRQSEL_MASK            GENMASK(24, 22)
+#define FRQSEL_SHIFT(x)                (((x) << 22) & FRQSEL_MASK)
+
+#define EMMC_PHYSTAT_REG       0xbc
+#define CALDONE_MASK           BIT(9)
+#define DLLRDY_MASK            BIT(8)
+#define IS_CALDONE(x)  ((x) & CALDONE_MASK)
+#define IS_DLLRDY(x)   ((x) & DLLRDY_MASK)
+
+struct intel_emmc_phy {
+       struct regmap *syscfg;
+       struct clk *emmcclk;
+};
+
+static int intel_emmc_phy_power(struct phy *phy, bool on_off)
+{
+       struct intel_emmc_phy *priv = phy_get_drvdata(phy);
+       unsigned int caldone;
+       unsigned int dllrdy;
+       unsigned int freqsel;
+       unsigned long rate;
+       int ret, quot;
+
+       /*
+        * Keep phyctrl_pdb and phyctrl_endll low to allow
+        * initialization of CALIO state M/C DFFs
+        */
+       ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
+                                PDB_SHIFT(0));
+       if (ret) {
+               dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
+               return ret;
+       }
+
+       /* Already finish power_off above */
+       if (!on_off)
+               return 0;
+
+       rate = clk_get_rate(priv->emmcclk);
+       quot = DIV_ROUND_CLOSEST(rate, 50000000);
+       if (quot > FRQSEL_150M)
+               dev_warn(&phy->dev, "Unsupported rate: %lu\n", rate);
+       freqsel = clamp_t(int, quot, FRQSEL_25M, FRQSEL_150M);
+
+       /*
+        * According to the user manual, calpad calibration
+        * cycle takes more than 2us without the minimal recommended
+        * value, so we may need a little margin here
+        */
+       udelay(5);
+
+       ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, PDB_MASK,
+                                PDB_SHIFT(1));
+       if (ret) {
+               dev_err(&phy->dev, "CALIO power down bar failed: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * According to the user manual, it asks driver to wait 5us for
+        * calpad busy trimming. However it is documented that this value is
+        * PVT(A.K.A process,voltage and temperature) relevant, so some
+        * failure cases are found which indicates we should be more tolerant
+        * to calpad busy trimming.
+        */
+       ret = regmap_read_poll_timeout(priv->syscfg, EMMC_PHYSTAT_REG,
+                                      caldone, IS_CALDONE(caldone),
+                                      0, 50);
+       if (ret) {
+               dev_err(&phy->dev, "caldone failed, ret=%d\n", ret);
+               return ret;
+       }
+
+       /* Set the frequency of the DLL operation */
+       ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL2_REG, FRQSEL_MASK,
+                                FRQSEL_SHIFT(freqsel));
+       if (ret) {
+               dev_err(&phy->dev, "set the frequency of dll failed:%d\n", ret);
+               return ret;
+       }
+
+       /* Turn on the DLL */
+       ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL1_REG, ENDLL_MASK,
+                                ENDLL_SHIFT(1));
+       if (ret) {
+               dev_err(&phy->dev, "turn on the dll failed: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * After enabling analog DLL circuits docs say that we need 10.2 us if
+        * our source clock is at 50 MHz and that lock time scales linearly
+        * with clock speed.  If we are powering on the PHY and the card clock
+        * is super slow (like 100 kHZ) this could take as long as 5.1 ms as
+        * per the math: 10.2 us * (50000000 Hz / 100000 Hz) => 5.1 ms
+        * Hopefully we won't be running at 100 kHz, but we should still make
+        * sure we wait long enough.
+        *
+        * NOTE: There appear to be corner cases where the DLL seems to take
+        * extra long to lock for reasons that aren't understood.  In some
+        * extreme cases we've seen it take up to over 10ms (!).  We'll be
+        * generous and give it 50ms.
+        */
+       ret = regmap_read_poll_timeout(priv->syscfg,
+                                      EMMC_PHYSTAT_REG,
+                                      dllrdy, IS_DLLRDY(dllrdy),
+                                      0, 50 * USEC_PER_MSEC);
+       if (ret) {
+               dev_err(&phy->dev, "dllrdy failed. ret=%d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int intel_emmc_phy_init(struct phy *phy)
+{
+       struct intel_emmc_phy *priv = phy_get_drvdata(phy);
+
+       /*
+        * We purposely get the clock here and not in probe to avoid the
+        * circular dependency problem. We expect:
+        * - PHY driver to probe
+        * - SDHCI driver to start probe
+        * - SDHCI driver to register it's clock
+        * - SDHCI driver to get the PHY
+        * - SDHCI driver to init the PHY
+        *
+        * The clock is optional, so upon any error just return it like
+        * any other error to user.
+        *
+        */
+       priv->emmcclk = clk_get_optional(&phy->dev, "emmcclk");
+       if (IS_ERR(priv->emmcclk)) {
+               dev_err(&phy->dev, "ERROR: getting emmcclk\n");
+               return PTR_ERR(priv->emmcclk);
+       }
+
+       return 0;
+}
+
+static int intel_emmc_phy_exit(struct phy *phy)
+{
+       struct intel_emmc_phy *priv = phy_get_drvdata(phy);
+
+       clk_put(priv->emmcclk);
+
+       return 0;
+}
+
+static int intel_emmc_phy_power_on(struct phy *phy)
+{
+       struct intel_emmc_phy *priv = phy_get_drvdata(phy);
+       int ret;
+
+       /* Drive impedance: 50 Ohm */
+       ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, DR_TY_MASK,
+                                DR_TY_SHIFT(6));
+       if (ret) {
+               dev_err(&phy->dev, "ERROR set drive-impednce-50ohm: %d\n", ret);
+               return ret;
+       }
+
+       /* Output tap delay: disable */
+       ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG, OTAPDLYENA,
+                                0);
+       if (ret) {
+               dev_err(&phy->dev, "ERROR Set output tap delay : %d\n", ret);
+               return ret;
+       }
+
+       /* Output tap delay */
+       ret = regmap_update_bits(priv->syscfg, EMMC_PHYCTRL0_REG,
+                                OTAPDLYSEL_MASK, OTAPDLYSEL_SHIFT(4));
+       if (ret) {
+               dev_err(&phy->dev, "ERROR: output tap dly select: %d\n", ret);
+               return ret;
+       }
+
+       /* Power up eMMC phy analog blocks */
+       return intel_emmc_phy_power(phy, true);
+}
+
+static int intel_emmc_phy_power_off(struct phy *phy)
+{
+       /* Power down eMMC phy analog blocks */
+       return intel_emmc_phy_power(phy, false);
+}
+
+static const struct phy_ops ops = {
+       .init           = intel_emmc_phy_init,
+       .exit           = intel_emmc_phy_exit,
+       .power_on       = intel_emmc_phy_power_on,
+       .power_off      = intel_emmc_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int intel_emmc_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct intel_emmc_phy *priv;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       /* Get eMMC phy (accessed via chiptop) regmap */
+       priv->syscfg = syscon_regmap_lookup_by_phandle(np, "intel,syscon");
+       if (IS_ERR(priv->syscfg)) {
+               dev_err(dev, "failed to find syscon\n");
+               return PTR_ERR(priv->syscfg);
+       }
+
+       generic_phy = devm_phy_create(dev, np, &ops);
+       if (IS_ERR(generic_phy)) {
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(generic_phy);
+       }
+
+       phy_set_drvdata(generic_phy, priv);
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id intel_emmc_phy_dt_ids[] = {
+       { .compatible = "intel,lgm-emmc-phy" },
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, intel_emmc_phy_dt_ids);
+
+static struct platform_driver intel_emmc_driver = {
+       .probe          = intel_emmc_phy_probe,
+       .driver         = {
+               .name   = "intel-emmc-phy",
+               .of_match_table = intel_emmc_phy_dt_ids,
+       },
+};
+
+module_platform_driver(intel_emmc_driver);
+
+MODULE_AUTHOR("Peter Harliman Liem <peter.harliman.liem@intel.com>");
+MODULE_DESCRIPTION("Intel eMMC PHY driver");
+MODULE_LICENSE("GPL v2");
index 6e45796..2ff9a48 100644 (file)
@@ -386,7 +386,7 @@ static struct phy *ltq_vrx200_pcie_phy_xlate(struct device *dev,
        default:
                dev_err(dev, "invalid PHY mode %u\n", mode);
                return ERR_PTR(-EINVAL);
-       };
+       }
 
        return priv->phy;
 }
index 81cfea1..dee757c 100644 (file)
@@ -3,14 +3,13 @@
 # Phy drivers for Mediatek devices
 #
 config PHY_MTK_TPHY
-    tristate "MediaTek T-PHY Driver"
-       depends on ARCH_MEDIATEK && OF
+       tristate "MediaTek T-PHY Driver"
        depends on ARCH_MEDIATEK || COMPILE_TEST
        depends on OF
-    select GENERIC_PHY
-    help
-      Say 'Y' here to add support for MediaTek T-PHY driver,
-      it supports multiple usb2.0, usb3.0 ports, PCIe and
+       select GENERIC_PHY
+       help
+         Say 'Y' here to add support for MediaTek T-PHY driver,
+         it supports multiple usb2.0, usb3.0 ports, PCIe and
          SATA, and meanwhile supports two version T-PHY which have
          different banks layout, the T-PHY with shared banks between
          multi-ports is first version, otherwise is second veriosn,
@@ -28,11 +27,11 @@ config PHY_MTK_UFS
          specified M-PHYs.
 
 config PHY_MTK_XSPHY
-    tristate "MediaTek XS-PHY Driver"
+       tristate "MediaTek XS-PHY Driver"
        depends on ARCH_MEDIATEK || COMPILE_TEST
        depends on OF
-    select GENERIC_PHY
-    help
+       select GENERIC_PHY
+       help
          Enable this to support the SuperSpeedPlus XS-PHY transceiver for
          USB3.1 GEN2 controllers on MediaTek chips. The driver supports
          multiple USB2.0, USB3.1 GEN2 ports.
index b04f4fe..2eb28cc 100644 (file)
@@ -29,7 +29,7 @@ static void devm_phy_release(struct device *dev, void *res)
 {
        struct phy *phy = *(struct phy **)res;
 
-       phy_put(phy);
+       phy_put(dev, phy);
 }
 
 static void devm_phy_provider_release(struct device *dev, void *res)
@@ -566,12 +566,12 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id)
 EXPORT_SYMBOL_GPL(of_phy_get);
 
 /**
- * phy_put() - release the PHY
- * @phy: the phy returned by phy_get()
+ * of_phy_put() - release the PHY
+ * @phy: the phy returned by of_phy_get()
  *
- * Releases a refcount the caller received from phy_get().
+ * Releases a refcount the caller received from of_phy_get().
  */
-void phy_put(struct phy *phy)
+void of_phy_put(struct phy *phy)
 {
        if (!phy || IS_ERR(phy))
                return;
@@ -584,6 +584,20 @@ void phy_put(struct phy *phy)
        module_put(phy->ops->owner);
        put_device(&phy->dev);
 }
+EXPORT_SYMBOL_GPL(of_phy_put);
+
+/**
+ * phy_put() - release the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by phy_get()
+ *
+ * Releases a refcount the caller received from phy_get().
+ */
+void phy_put(struct device *dev, struct phy *phy)
+{
+       device_link_remove(dev, &phy->dev);
+       of_phy_put(phy);
+}
 EXPORT_SYMBOL_GPL(phy_put);
 
 /**
@@ -651,6 +665,7 @@ struct phy *phy_get(struct device *dev, const char *string)
 {
        int index = 0;
        struct phy *phy;
+       struct device_link *link;
 
        if (string == NULL) {
                dev_WARN(dev, "missing string\n");
@@ -672,6 +687,13 @@ struct phy *phy_get(struct device *dev, const char *string)
 
        get_device(&phy->dev);
 
+       link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
+       if (!link) {
+               dev_err(dev, "failed to create device link to %s\n",
+                       dev_name(phy->dev.parent));
+               return ERR_PTR(-EINVAL);
+       }
+
        return phy;
 }
 EXPORT_SYMBOL_GPL(phy_get);
@@ -765,6 +787,7 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
                            const char *con_id)
 {
        struct phy **ptr, *phy;
+       struct device_link *link;
 
        ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
        if (!ptr)
@@ -776,6 +799,14 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
                devres_add(dev, ptr);
        } else {
                devres_free(ptr);
+               return phy;
+       }
+
+       link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
+       if (!link) {
+               dev_err(dev, "failed to create device link to %s\n",
+                       dev_name(phy->dev.parent));
+               return ERR_PTR(-EINVAL);
        }
 
        return phy;
@@ -798,6 +829,7 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
                                     int index)
 {
        struct phy **ptr, *phy;
+       struct device_link *link;
 
        ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
        if (!ptr)
@@ -819,6 +851,13 @@ struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
        *ptr = phy;
        devres_add(dev, ptr);
 
+       link = device_link_add(dev, &phy->dev, DL_FLAG_STATELESS);
+       if (!link) {
+               dev_err(dev, "failed to create device link to %s\n",
+                       dev_name(phy->dev.parent));
+               return ERR_PTR(-EINVAL);
+       }
+
        return phy;
 }
 EXPORT_SYMBOL_GPL(devm_of_phy_get_by_index);
index 42bc515..febe0ae 100644 (file)
@@ -80,7 +80,7 @@ static int read_poll_timeout(void __iomem *addr, u32 mask)
                if (readl_relaxed(addr) & mask)
                        return 0;
 
-                usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
+               usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
        } while (!time_after(jiffies, timeout));
 
        return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
index 66f9172..7db2a94 100644 (file)
@@ -166,8 +166,9 @@ static const unsigned int sdm845_ufsphy_regs_layout[] = {
 };
 
 static const unsigned int sm8150_ufsphy_regs_layout[] = {
-       [QPHY_START_CTRL]               = 0x00,
-       [QPHY_PCS_READY_STATUS]         = 0x180,
+       [QPHY_START_CTRL]               = QPHY_V4_PHY_START,
+       [QPHY_PCS_READY_STATUS]         = QPHY_V4_PCS_READY_STATUS,
+       [QPHY_SW_RESET]                 = QPHY_V4_SW_RESET,
 };
 
 static const struct qmp_phy_init_tbl msm8996_pcie_serdes_tbl[] = {
@@ -885,7 +886,6 @@ static const struct qmp_phy_init_tbl msm8998_usb3_pcs_tbl[] = {
 };
 
 static const struct qmp_phy_init_tbl sm8150_ufsphy_serdes_tbl[] = {
-       QMP_PHY_INIT_CFG(QPHY_POWER_DOWN_CONTROL, 0x01),
        QMP_PHY_INIT_CFG(QSERDES_V4_COM_SYSCLK_EN_SEL, 0xd9),
        QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_SEL, 0x11),
        QMP_PHY_INIT_CFG(QSERDES_V4_COM_HSCLK_HS_SWITCH_SEL, 0x00),
@@ -1390,7 +1390,6 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = {
        .pwrdn_ctrl             = SW_PWRDN,
 
        .is_dual_lane_phy       = true,
-       .no_pcs_sw_reset        = true,
 };
 
 static void qcom_qmp_phy_configure(void __iomem *base,
index ab6ff9b..90f793c 100644 (file)
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+/* SPDX-License-Identifier: GPL-2.0 */
 /*
  * Copyright (c) 2017, The Linux Foundation. All rights reserved.
  */
index dbd2de4..0824b9d 100644 (file)
@@ -39,6 +39,7 @@ config PHY_ROCKCHIP_INNO_DSIDPHY
        tristate "Rockchip Innosilicon MIPI/LVDS/TTL PHY driver"
        depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
        select GENERIC_PHY
+       select GENERIC_PHY_MIPI_DPHY
        help
          Enable this to support the Rockchip MIPI/LVDS/TTL PHY with
          Innosilicon IP block.
index fc729ec..a7c6c94 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/platform_device.h>
 #include <linux/reset.h>
 #include <linux/phy/phy.h>
+#include <linux/phy/phy-mipi-dphy.h>
 #include <linux/pm_runtime.h>
 #include <linux/mfd/syscon.h>
 
 #define DSI_PHY_STATUS         0xb0
 #define PHY_LOCK               BIT(0)
 
-struct mipi_dphy_timing {
-       unsigned int clkmiss;
-       unsigned int clkpost;
-       unsigned int clkpre;
-       unsigned int clkprepare;
-       unsigned int clksettle;
-       unsigned int clktermen;
-       unsigned int clktrail;
-       unsigned int clkzero;
-       unsigned int dtermen;
-       unsigned int eot;
-       unsigned int hsexit;
-       unsigned int hsprepare;
-       unsigned int hszero;
-       unsigned int hssettle;
-       unsigned int hsskip;
-       unsigned int hstrail;
-       unsigned int init;
-       unsigned int lpx;
-       unsigned int taget;
-       unsigned int tago;
-       unsigned int tasure;
-       unsigned int wakeup;
-};
-
 struct inno_dsidphy {
        struct device *dev;
        struct clk *ref_clk;
@@ -201,7 +177,9 @@ struct inno_dsidphy {
        void __iomem *host_base;
        struct reset_control *rst;
        enum phy_mode mode;
+       struct phy_configure_opts_mipi_dphy dphy_cfg;
 
+       struct clk *pll_clk;
        struct {
                struct clk_hw hw;
                u8 prediv;
@@ -238,37 +216,79 @@ static void phy_update_bits(struct inno_dsidphy *inno,
        writel(tmp, inno->phy_base + reg);
 }
 
-static void mipi_dphy_timing_get_default(struct mipi_dphy_timing *timing,
-                                        unsigned long period)
+static unsigned long inno_dsidphy_pll_calc_rate(struct inno_dsidphy *inno,
+                                               unsigned long rate)
 {
-       /* Global Operation Timing Parameters */
-       timing->clkmiss = 0;
-       timing->clkpost = 70000 + 52 * period;
-       timing->clkpre = 8 * period;
-       timing->clkprepare = 65000;
-       timing->clksettle = 95000;
-       timing->clktermen = 0;
-       timing->clktrail = 80000;
-       timing->clkzero = 260000;
-       timing->dtermen = 0;
-       timing->eot = 0;
-       timing->hsexit = 120000;
-       timing->hsprepare = 65000 + 4 * period;
-       timing->hszero = 145000 + 6 * period;
-       timing->hssettle = 85000 + 6 * period;
-       timing->hsskip = 40000;
-       timing->hstrail = max(8 * period, 60000 + 4 * period);
-       timing->init = 100000000;
-       timing->lpx = 60000;
-       timing->taget = 5 * timing->lpx;
-       timing->tago = 4 * timing->lpx;
-       timing->tasure = 2 * timing->lpx;
-       timing->wakeup = 1000000000;
+       unsigned long prate = clk_get_rate(inno->ref_clk);
+       unsigned long best_freq = 0;
+       unsigned long fref, fout;
+       u8 min_prediv, max_prediv;
+       u8 _prediv, best_prediv = 1;
+       u16 _fbdiv, best_fbdiv = 1;
+       u32 min_delta = UINT_MAX;
+
+       /*
+        * The PLL output frequency can be calculated using a simple formula:
+        * PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
+        * PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
+        */
+       fref = prate / 2;
+       if (rate > 1000000000UL)
+               fout = 1000000000UL;
+       else
+               fout = rate;
+
+       /* 5Mhz < Fref / prediv < 40MHz */
+       min_prediv = DIV_ROUND_UP(fref, 40000000);
+       max_prediv = fref / 5000000;
+
+       for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
+               u64 tmp;
+               u32 delta;
+
+               tmp = (u64)fout * _prediv;
+               do_div(tmp, fref);
+               _fbdiv = tmp;
+
+               /*
+                * The possible settings of feedback divider are
+                * 12, 13, 14, 16, ~ 511
+                */
+               if (_fbdiv == 15)
+                       continue;
+
+               if (_fbdiv < 12 || _fbdiv > 511)
+                       continue;
+
+               tmp = (u64)_fbdiv * fref;
+               do_div(tmp, _prediv);
+
+               delta = abs(fout - tmp);
+               if (!delta) {
+                       best_prediv = _prediv;
+                       best_fbdiv = _fbdiv;
+                       best_freq = tmp;
+                       break;
+               } else if (delta < min_delta) {
+                       best_prediv = _prediv;
+                       best_fbdiv = _fbdiv;
+                       best_freq = tmp;
+                       min_delta = delta;
+               }
+       }
+
+       if (best_freq) {
+               inno->pll.prediv = best_prediv;
+               inno->pll.fbdiv = best_fbdiv;
+               inno->pll.rate = best_freq;
+       }
+
+       return best_freq;
 }
 
 static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
 {
-       struct mipi_dphy_timing gotp;
+       struct phy_configure_opts_mipi_dphy *cfg = &inno->dphy_cfg;
        const struct {
                unsigned long rate;
                u8 hs_prepare;
@@ -288,12 +308,14 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
                { 800000000, 0x21, 0x1f, 0x09, 0x29},
                {1000000000, 0x09, 0x20, 0x09, 0x27},
        };
-       u32 t_txbyteclkhs, t_txclkesc, ui;
+       u32 t_txbyteclkhs, t_txclkesc;
        u32 txbyteclkhs, txclkesc, esc_clk_div;
        u32 hs_exit, clk_post, clk_pre, wakeup, lpx, ta_go, ta_sure, ta_wait;
        u32 hs_prepare, hs_trail, hs_zero, clk_lane_hs_zero, data_lane_hs_zero;
        unsigned int i;
 
+       inno_dsidphy_pll_calc_rate(inno, cfg->hs_clk_rate);
+
        /* Select MIPI mode */
        phy_update_bits(inno, REGISTER_PART_LVDS, 0x03,
                        MODE_ENABLE_MASK, MIPI_MODE_ENABLE);
@@ -328,32 +350,27 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
        txclkesc = txbyteclkhs / esc_clk_div;
        t_txclkesc = div_u64(PSEC_PER_SEC, txclkesc);
 
-       ui = div_u64(PSEC_PER_SEC, inno->pll.rate);
-
-       memset(&gotp, 0, sizeof(gotp));
-       mipi_dphy_timing_get_default(&gotp, ui);
-
        /*
         * The value of counter for HS Ths-exit
         * Ths-exit = Tpin_txbyteclkhs * value
         */
-       hs_exit = DIV_ROUND_UP(gotp.hsexit, t_txbyteclkhs);
+       hs_exit = DIV_ROUND_UP(cfg->hs_exit, t_txbyteclkhs);
        /*
         * The value of counter for HS Tclk-post
         * Tclk-post = Tpin_txbyteclkhs * value
         */
-       clk_post = DIV_ROUND_UP(gotp.clkpost, t_txbyteclkhs);
+       clk_post = DIV_ROUND_UP(cfg->clk_post, t_txbyteclkhs);
        /*
         * The value of counter for HS Tclk-pre
         * Tclk-pre = Tpin_txbyteclkhs * value
         */
-       clk_pre = DIV_ROUND_UP(gotp.clkpre, t_txbyteclkhs);
+       clk_pre = DIV_ROUND_UP(cfg->clk_pre, t_txbyteclkhs);
 
        /*
         * The value of counter for HS Tlpx Time
         * Tlpx = Tpin_txbyteclkhs * (2 + value)
         */
-       lpx = DIV_ROUND_UP(gotp.lpx, t_txbyteclkhs);
+       lpx = DIV_ROUND_UP(cfg->lpx, t_txbyteclkhs);
        if (lpx >= 2)
                lpx -= 2;
 
@@ -362,19 +379,19 @@ static void inno_dsidphy_mipi_mode_enable(struct inno_dsidphy *inno)
         * Tta-go for turnaround
         * Tta-go = Ttxclkesc * value
         */
-       ta_go = DIV_ROUND_UP(gotp.tago, t_txclkesc);
+       ta_go = DIV_ROUND_UP(cfg->ta_go, t_txclkesc);
        /*
         * The value of counter for HS Tta-sure
         * Tta-sure for turnaround
         * Tta-sure = Ttxclkesc * value
         */
-       ta_sure = DIV_ROUND_UP(gotp.tasure, t_txclkesc);
+       ta_sure = DIV_ROUND_UP(cfg->ta_sure, t_txclkesc);
        /*
         * The value of counter for HS Tta-wait
         * Tta-wait for turnaround
         * Tta-wait = Ttxclkesc * value
         */
-       ta_wait = DIV_ROUND_UP(gotp.taget, t_txclkesc);
+       ta_wait = DIV_ROUND_UP(cfg->ta_get, t_txclkesc);
 
        for (i = 0; i < ARRAY_SIZE(timings); i++)
                if (inno->pll.rate <= timings[i].rate)
@@ -479,6 +496,7 @@ static int inno_dsidphy_power_on(struct phy *phy)
        struct inno_dsidphy *inno = phy_get_drvdata(phy);
 
        clk_prepare_enable(inno->pclk_phy);
+       clk_prepare_enable(inno->ref_clk);
        pm_runtime_get_sync(inno->dev);
 
        /* Bandgap power on */
@@ -524,6 +542,7 @@ static int inno_dsidphy_power_off(struct phy *phy)
                        LVDS_PLL_POWER_OFF | LVDS_BANDGAP_POWER_DOWN);
 
        pm_runtime_put(inno->dev);
+       clk_disable_unprepare(inno->ref_clk);
        clk_disable_unprepare(inno->pclk_phy);
 
        return 0;
@@ -546,168 +565,32 @@ static int inno_dsidphy_set_mode(struct phy *phy, enum phy_mode mode,
        return 0;
 }
 
-static const struct phy_ops inno_dsidphy_ops = {
-       .set_mode = inno_dsidphy_set_mode,
-       .power_on = inno_dsidphy_power_on,
-       .power_off = inno_dsidphy_power_off,
-       .owner = THIS_MODULE,
-};
-
-static unsigned long inno_dsidphy_pll_round_rate(struct inno_dsidphy *inno,
-                                                  unsigned long prate,
-                                                  unsigned long rate,
-                                                  u8 *prediv, u16 *fbdiv)
-{
-       unsigned long best_freq = 0;
-       unsigned long fref, fout;
-       u8 min_prediv, max_prediv;
-       u8 _prediv, best_prediv = 1;
-       u16 _fbdiv, best_fbdiv = 1;
-       u32 min_delta = UINT_MAX;
-
-       /*
-        * The PLL output frequency can be calculated using a simple formula:
-        * PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2
-        * PLL_Output_Frequency: it is equal to DDR-Clock-Frequency * 2
-        */
-       fref = prate / 2;
-       if (rate > 1000000000UL)
-               fout = 1000000000UL;
-       else
-               fout = rate;
-
-       /* 5Mhz < Fref / prediv < 40MHz */
-       min_prediv = DIV_ROUND_UP(fref, 40000000);
-       max_prediv = fref / 5000000;
-
-       for (_prediv = min_prediv; _prediv <= max_prediv; _prediv++) {
-               u64 tmp;
-               u32 delta;
-
-               tmp = (u64)fout * _prediv;
-               do_div(tmp, fref);
-               _fbdiv = tmp;
-
-               /*
-                * The possible settings of feedback divider are
-                * 12, 13, 14, 16, ~ 511
-                */
-               if (_fbdiv == 15)
-                       continue;
-
-               if (_fbdiv < 12 || _fbdiv > 511)
-                       continue;
-
-               tmp = (u64)_fbdiv * fref;
-               do_div(tmp, _prediv);
-
-               delta = abs(fout - tmp);
-               if (!delta) {
-                       best_prediv = _prediv;
-                       best_fbdiv = _fbdiv;
-                       best_freq = tmp;
-                       break;
-               } else if (delta < min_delta) {
-                       best_prediv = _prediv;
-                       best_fbdiv = _fbdiv;
-                       best_freq = tmp;
-                       min_delta = delta;
-               }
-       }
-
-       if (best_freq) {
-               *prediv = best_prediv;
-               *fbdiv = best_fbdiv;
-       }
-
-       return best_freq;
-}
-
-static long inno_dsidphy_pll_clk_round_rate(struct clk_hw *hw,
-                                             unsigned long rate,
-                                             unsigned long *prate)
+static int inno_dsidphy_configure(struct phy *phy,
+                                 union phy_configure_opts *opts)
 {
-       struct inno_dsidphy *inno = hw_to_inno(hw);
-       unsigned long fout;
-       u16 fbdiv = 1;
-       u8 prediv = 1;
-
-       fout = inno_dsidphy_pll_round_rate(inno, *prate, rate,
-                                            &prediv, &fbdiv);
-
-       return fout;
-}
-
-static int inno_dsidphy_pll_clk_set_rate(struct clk_hw *hw,
-                                          unsigned long rate,
-                                          unsigned long parent_rate)
-{
-       struct inno_dsidphy *inno = hw_to_inno(hw);
-       unsigned long fout;
-       u16 fbdiv = 1;
-       u8 prediv = 1;
+       struct inno_dsidphy *inno = phy_get_drvdata(phy);
+       int ret;
 
-       fout = inno_dsidphy_pll_round_rate(inno, parent_rate, rate,
-                                            &prediv, &fbdiv);
+       if (inno->mode != PHY_MODE_MIPI_DPHY)
+               return -EINVAL;
 
-       dev_dbg(inno->dev, "fin=%lu, fout=%lu, prediv=%u, fbdiv=%u\n",
-               parent_rate, fout, prediv, fbdiv);
+       ret = phy_mipi_dphy_config_validate(&opts->mipi_dphy);
+       if (ret)
+               return ret;
 
-       inno->pll.prediv = prediv;
-       inno->pll.fbdiv = fbdiv;
-       inno->pll.rate = fout;
+       memcpy(&inno->dphy_cfg, &opts->mipi_dphy, sizeof(inno->dphy_cfg));
 
        return 0;
 }
 
-static unsigned long
-inno_dsidphy_pll_clk_recalc_rate(struct clk_hw *hw, unsigned long prate)
-{
-       struct inno_dsidphy *inno = hw_to_inno(hw);
-
-       /* PLL_Output_Frequency = (FREF / PREDIV * FBDIV) / 2 */
-       return (prate / inno->pll.prediv * inno->pll.fbdiv) / 2;
-}
-
-static const struct clk_ops inno_dsidphy_pll_clk_ops = {
-       .round_rate = inno_dsidphy_pll_clk_round_rate,
-       .set_rate = inno_dsidphy_pll_clk_set_rate,
-       .recalc_rate = inno_dsidphy_pll_clk_recalc_rate,
+static const struct phy_ops inno_dsidphy_ops = {
+       .configure = inno_dsidphy_configure,
+       .set_mode = inno_dsidphy_set_mode,
+       .power_on = inno_dsidphy_power_on,
+       .power_off = inno_dsidphy_power_off,
+       .owner = THIS_MODULE,
 };
 
-static int inno_dsidphy_pll_register(struct inno_dsidphy *inno)
-{
-       struct device *dev = inno->dev;
-       struct clk *clk;
-       const char *parent_name;
-       struct clk_init_data init;
-       int ret;
-
-       parent_name = __clk_get_name(inno->ref_clk);
-
-       init.name = "mipi_dphy_pll";
-       ret = of_property_read_string(dev->of_node, "clock-output-names",
-                                     &init.name);
-       if (ret < 0)
-               dev_dbg(dev, "phy should set clock-output-names property\n");
-
-       init.ops = &inno_dsidphy_pll_clk_ops;
-       init.parent_names = &parent_name;
-       init.num_parents = 1;
-       init.flags = 0;
-
-       inno->pll.hw.init = &init;
-       clk = devm_clk_register(dev, &inno->pll.hw);
-       if (IS_ERR(clk)) {
-               ret = PTR_ERR(clk);
-               dev_err(dev, "failed to register PLL: %d\n", ret);
-               return ret;
-       }
-
-       return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
-                                          &inno->pll.hw);
-}
-
 static int inno_dsidphy_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
@@ -764,10 +647,6 @@ static int inno_dsidphy_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = inno_dsidphy_pll_register(inno);
-       if (ret)
-               return ret;
-
        pm_runtime_enable(dev);
 
        return 0;
index e231c0e..3a1d388 100644 (file)
@@ -33,6 +33,21 @@ config PHY_AM654_SERDES
          This option enables support for TI AM654 SerDes PHY used for
          PCIe.
 
+config PHY_J721E_WIZ
+       tristate "TI J721E WIZ (SERDES Wrapper) support"
+       depends on OF && ARCH_K3 || COMPILE_TEST
+       depends on COMMON_CLK
+       select GENERIC_PHY
+       select MULTIPLEXER
+       select REGMAP_MMIO
+       select MUX_MMIO
+       help
+         This option enables support for WIZ module present in TI's J721E
+         SoC. WIZ is a serdes wrapper used to configure some of the input
+         signals to the SERDES (Sierra/Torrent). This driver configures
+         three clock selects (pll0, pll1, dig) and resets for each of the
+         lanes.
+
 config OMAP_CONTROL_PHY
        tristate "OMAP CONTROL PHY Driver"
        depends on ARCH_OMAP2PLUS || COMPILE_TEST
index bff901e..dcba257 100644 (file)
@@ -8,3 +8,4 @@ obj-$(CONFIG_PHY_TUSB1210)              += phy-tusb1210.o
 obj-$(CONFIG_TWL4030_USB)              += phy-twl4030-usb.o
 obj-$(CONFIG_PHY_AM654_SERDES)         += phy-am654-serdes.o
 obj-$(CONFIG_PHY_TI_GMII_SEL)          += phy-gmii-sel.o
+obj-$(CONFIG_PHY_J721E_WIZ)            += phy-j721e-wiz.o
diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c
new file mode 100644 (file)
index 0000000..7b51045
--- /dev/null
@@ -0,0 +1,959 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Wrapper driver for SERDES used in J721E
+ *
+ * Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com/
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/mux/consumer.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/reset-controller.h>
+
+#define WIZ_SERDES_CTRL                0x404
+#define WIZ_SERDES_TOP_CTRL    0x408
+#define WIZ_SERDES_RST         0x40c
+#define WIZ_SERDES_TYPEC       0x410
+#define WIZ_LANECTL(n)         (0x480 + (0x40 * (n)))
+
+#define WIZ_MAX_LANES          4
+#define WIZ_MUX_NUM_CLOCKS     3
+#define WIZ_DIV_NUM_CLOCKS_16G 2
+#define WIZ_DIV_NUM_CLOCKS_10G 1
+
+#define WIZ_SERDES_TYPEC_LN10_SWAP     BIT(30)
+
+enum wiz_lane_standard_mode {
+       LANE_MODE_GEN1,
+       LANE_MODE_GEN2,
+       LANE_MODE_GEN3,
+       LANE_MODE_GEN4,
+};
+
+enum wiz_refclk_mux_sel {
+       PLL0_REFCLK,
+       PLL1_REFCLK,
+       REFCLK_DIG,
+};
+
+enum wiz_refclk_div_sel {
+       CMN_REFCLK_DIG_DIV,
+       CMN_REFCLK1_DIG_DIV,
+};
+
+static const struct reg_field por_en = REG_FIELD(WIZ_SERDES_CTRL, 31, 31);
+static const struct reg_field phy_reset_n = REG_FIELD(WIZ_SERDES_RST, 31, 31);
+static const struct reg_field pll1_refclk_mux_sel =
+                                       REG_FIELD(WIZ_SERDES_RST, 29, 29);
+static const struct reg_field pll0_refclk_mux_sel =
+                                       REG_FIELD(WIZ_SERDES_RST, 28, 28);
+static const struct reg_field refclk_dig_sel_16g =
+                                       REG_FIELD(WIZ_SERDES_RST, 24, 25);
+static const struct reg_field refclk_dig_sel_10g =
+                                       REG_FIELD(WIZ_SERDES_RST, 24, 24);
+static const struct reg_field pma_cmn_refclk_int_mode =
+                                       REG_FIELD(WIZ_SERDES_TOP_CTRL, 28, 29);
+static const struct reg_field pma_cmn_refclk_mode =
+                                       REG_FIELD(WIZ_SERDES_TOP_CTRL, 30, 31);
+static const struct reg_field pma_cmn_refclk_dig_div =
+                                       REG_FIELD(WIZ_SERDES_TOP_CTRL, 26, 27);
+static const struct reg_field pma_cmn_refclk1_dig_div =
+                                       REG_FIELD(WIZ_SERDES_TOP_CTRL, 24, 25);
+
+static const struct reg_field p_enable[WIZ_MAX_LANES] = {
+       REG_FIELD(WIZ_LANECTL(0), 30, 31),
+       REG_FIELD(WIZ_LANECTL(1), 30, 31),
+       REG_FIELD(WIZ_LANECTL(2), 30, 31),
+       REG_FIELD(WIZ_LANECTL(3), 30, 31),
+};
+
+static const struct reg_field p_align[WIZ_MAX_LANES] = {
+       REG_FIELD(WIZ_LANECTL(0), 29, 29),
+       REG_FIELD(WIZ_LANECTL(1), 29, 29),
+       REG_FIELD(WIZ_LANECTL(2), 29, 29),
+       REG_FIELD(WIZ_LANECTL(3), 29, 29),
+};
+
+static const struct reg_field p_raw_auto_start[WIZ_MAX_LANES] = {
+       REG_FIELD(WIZ_LANECTL(0), 28, 28),
+       REG_FIELD(WIZ_LANECTL(1), 28, 28),
+       REG_FIELD(WIZ_LANECTL(2), 28, 28),
+       REG_FIELD(WIZ_LANECTL(3), 28, 28),
+};
+
+static const struct reg_field p_standard_mode[WIZ_MAX_LANES] = {
+       REG_FIELD(WIZ_LANECTL(0), 24, 25),
+       REG_FIELD(WIZ_LANECTL(1), 24, 25),
+       REG_FIELD(WIZ_LANECTL(2), 24, 25),
+       REG_FIELD(WIZ_LANECTL(3), 24, 25),
+};
+
+static const struct reg_field typec_ln10_swap =
+                                       REG_FIELD(WIZ_SERDES_TYPEC, 30, 30);
+
+struct wiz_clk_mux {
+       struct clk_hw           hw;
+       struct regmap_field     *field;
+       u32                     *table;
+       struct clk_init_data    clk_data;
+};
+
+#define to_wiz_clk_mux(_hw) container_of(_hw, struct wiz_clk_mux, hw)
+
+struct wiz_clk_divider {
+       struct clk_hw           hw;
+       struct regmap_field     *field;
+       struct clk_div_table    *table;
+       struct clk_init_data    clk_data;
+};
+
+#define to_wiz_clk_div(_hw) container_of(_hw, struct wiz_clk_divider, hw)
+
+struct wiz_clk_mux_sel {
+       struct regmap_field     *field;
+       u32                     table[4];
+       const char              *node_name;
+};
+
+struct wiz_clk_div_sel {
+       struct regmap_field     *field;
+       struct clk_div_table    *table;
+       const char              *node_name;
+};
+
+static struct wiz_clk_mux_sel clk_mux_sel_16g[] = {
+       {
+               /*
+                * Mux value to be configured for each of the input clocks
+                * in the order populated in device tree
+                */
+               .table = { 1, 0 },
+               .node_name = "pll0-refclk",
+       },
+       {
+               .table = { 1, 0 },
+               .node_name = "pll1-refclk",
+       },
+       {
+               .table = { 1, 3, 0, 2 },
+               .node_name = "refclk-dig",
+       },
+};
+
+static struct wiz_clk_mux_sel clk_mux_sel_10g[] = {
+       {
+               /*
+                * Mux value to be configured for each of the input clocks
+                * in the order populated in device tree
+                */
+               .table = { 1, 0 },
+               .node_name = "pll0-refclk",
+       },
+       {
+               .table = { 1, 0 },
+               .node_name = "pll1-refclk",
+       },
+       {
+               .table = { 1, 0 },
+               .node_name = "refclk-dig",
+       },
+};
+
+static struct clk_div_table clk_div_table[] = {
+       { .val = 0, .div = 1, },
+       { .val = 1, .div = 2, },
+       { .val = 2, .div = 4, },
+       { .val = 3, .div = 8, },
+};
+
+static struct wiz_clk_div_sel clk_div_sel[] = {
+       {
+               .table = clk_div_table,
+               .node_name = "cmn-refclk-dig-div",
+       },
+       {
+               .table = clk_div_table,
+               .node_name = "cmn-refclk1-dig-div",
+       },
+};
+
+enum wiz_type {
+       J721E_WIZ_16G,
+       J721E_WIZ_10G,
+};
+
+#define WIZ_TYPEC_DIR_DEBOUNCE_MIN     100     /* ms */
+#define WIZ_TYPEC_DIR_DEBOUNCE_MAX     1000
+
+struct wiz {
+       struct regmap           *regmap;
+       enum wiz_type           type;
+       struct wiz_clk_mux_sel  *clk_mux_sel;
+       struct wiz_clk_div_sel  *clk_div_sel;
+       unsigned int            clk_div_sel_num;
+       struct regmap_field     *por_en;
+       struct regmap_field     *phy_reset_n;
+       struct regmap_field     *p_enable[WIZ_MAX_LANES];
+       struct regmap_field     *p_align[WIZ_MAX_LANES];
+       struct regmap_field     *p_raw_auto_start[WIZ_MAX_LANES];
+       struct regmap_field     *p_standard_mode[WIZ_MAX_LANES];
+       struct regmap_field     *pma_cmn_refclk_int_mode;
+       struct regmap_field     *pma_cmn_refclk_mode;
+       struct regmap_field     *pma_cmn_refclk_dig_div;
+       struct regmap_field     *pma_cmn_refclk1_dig_div;
+       struct regmap_field     *typec_ln10_swap;
+
+       struct device           *dev;
+       u32                     num_lanes;
+       struct platform_device  *serdes_pdev;
+       struct reset_controller_dev wiz_phy_reset_dev;
+       struct gpio_desc        *gpio_typec_dir;
+       int                     typec_dir_delay;
+};
+
+static int wiz_reset(struct wiz *wiz)
+{
+       int ret;
+
+       ret = regmap_field_write(wiz->por_en, 0x1);
+       if (ret)
+               return ret;
+
+       mdelay(1);
+
+       ret = regmap_field_write(wiz->por_en, 0x0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int wiz_mode_select(struct wiz *wiz)
+{
+       u32 num_lanes = wiz->num_lanes;
+       int ret;
+       int i;
+
+       for (i = 0; i < num_lanes; i++) {
+               ret = regmap_field_write(wiz->p_standard_mode[i],
+                                        LANE_MODE_GEN4);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int wiz_init_raw_interface(struct wiz *wiz, bool enable)
+{
+       u32 num_lanes = wiz->num_lanes;
+       int i;
+       int ret;
+
+       for (i = 0; i < num_lanes; i++) {
+               ret = regmap_field_write(wiz->p_align[i], enable);
+               if (ret)
+                       return ret;
+
+               ret = regmap_field_write(wiz->p_raw_auto_start[i], enable);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+
+static int wiz_init(struct wiz *wiz)
+{
+       struct device *dev = wiz->dev;
+       int ret;
+
+       ret = wiz_reset(wiz);
+       if (ret) {
+               dev_err(dev, "WIZ reset failed\n");
+               return ret;
+       }
+
+       ret = wiz_mode_select(wiz);
+       if (ret) {
+               dev_err(dev, "WIZ mode select failed\n");
+               return ret;
+       }
+
+       ret = wiz_init_raw_interface(wiz, true);
+       if (ret) {
+               dev_err(dev, "WIZ interface initialization failed\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int wiz_regfield_init(struct wiz *wiz)
+{
+       struct wiz_clk_mux_sel *clk_mux_sel;
+       struct wiz_clk_div_sel *clk_div_sel;
+       struct regmap *regmap = wiz->regmap;
+       int num_lanes = wiz->num_lanes;
+       struct device *dev = wiz->dev;
+       int i;
+
+       wiz->por_en = devm_regmap_field_alloc(dev, regmap, por_en);
+       if (IS_ERR(wiz->por_en)) {
+               dev_err(dev, "POR_EN reg field init failed\n");
+               return PTR_ERR(wiz->por_en);
+       }
+
+       wiz->phy_reset_n = devm_regmap_field_alloc(dev, regmap,
+                                                  phy_reset_n);
+       if (IS_ERR(wiz->phy_reset_n)) {
+               dev_err(dev, "PHY_RESET_N reg field init failed\n");
+               return PTR_ERR(wiz->phy_reset_n);
+       }
+
+       wiz->pma_cmn_refclk_int_mode =
+               devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_int_mode);
+       if (IS_ERR(wiz->pma_cmn_refclk_int_mode)) {
+               dev_err(dev, "PMA_CMN_REFCLK_INT_MODE reg field init failed\n");
+               return PTR_ERR(wiz->pma_cmn_refclk_int_mode);
+       }
+
+       wiz->pma_cmn_refclk_mode =
+               devm_regmap_field_alloc(dev, regmap, pma_cmn_refclk_mode);
+       if (IS_ERR(wiz->pma_cmn_refclk_mode)) {
+               dev_err(dev, "PMA_CMN_REFCLK_MODE reg field init failed\n");
+               return PTR_ERR(wiz->pma_cmn_refclk_mode);
+       }
+
+       clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK_DIG_DIV];
+       clk_div_sel->field = devm_regmap_field_alloc(dev, regmap,
+                                                    pma_cmn_refclk_dig_div);
+       if (IS_ERR(clk_div_sel->field)) {
+               dev_err(dev, "PMA_CMN_REFCLK_DIG_DIV reg field init failed\n");
+               return PTR_ERR(clk_div_sel->field);
+       }
+
+       if (wiz->type == J721E_WIZ_16G) {
+               clk_div_sel = &wiz->clk_div_sel[CMN_REFCLK1_DIG_DIV];
+               clk_div_sel->field =
+                       devm_regmap_field_alloc(dev, regmap,
+                                               pma_cmn_refclk1_dig_div);
+               if (IS_ERR(clk_div_sel->field)) {
+                       dev_err(dev, "PMA_CMN_REFCLK1_DIG_DIV reg field init failed\n");
+                       return PTR_ERR(clk_div_sel->field);
+               }
+       }
+
+       clk_mux_sel = &wiz->clk_mux_sel[PLL0_REFCLK];
+       clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
+                                                    pll0_refclk_mux_sel);
+       if (IS_ERR(clk_mux_sel->field)) {
+               dev_err(dev, "PLL0_REFCLK_SEL reg field init failed\n");
+               return PTR_ERR(clk_mux_sel->field);
+       }
+
+       clk_mux_sel = &wiz->clk_mux_sel[PLL1_REFCLK];
+       clk_mux_sel->field = devm_regmap_field_alloc(dev, regmap,
+                                                    pll1_refclk_mux_sel);
+       if (IS_ERR(clk_mux_sel->field)) {
+               dev_err(dev, "PLL1_REFCLK_SEL reg field init failed\n");
+               return PTR_ERR(clk_mux_sel->field);
+       }
+
+       clk_mux_sel = &wiz->clk_mux_sel[REFCLK_DIG];
+       if (wiz->type == J721E_WIZ_10G)
+               clk_mux_sel->field =
+                       devm_regmap_field_alloc(dev, regmap,
+                                               refclk_dig_sel_10g);
+       else
+               clk_mux_sel->field =
+                       devm_regmap_field_alloc(dev, regmap,
+                                               refclk_dig_sel_16g);
+
+       if (IS_ERR(clk_mux_sel->field)) {
+               dev_err(dev, "REFCLK_DIG_SEL reg field init failed\n");
+               return PTR_ERR(clk_mux_sel->field);
+       }
+
+       for (i = 0; i < num_lanes; i++) {
+               wiz->p_enable[i] = devm_regmap_field_alloc(dev, regmap,
+                                                          p_enable[i]);
+               if (IS_ERR(wiz->p_enable[i])) {
+                       dev_err(dev, "P%d_ENABLE reg field init failed\n", i);
+                       return PTR_ERR(wiz->p_enable[i]);
+               }
+
+               wiz->p_align[i] = devm_regmap_field_alloc(dev, regmap,
+                                                         p_align[i]);
+               if (IS_ERR(wiz->p_align[i])) {
+                       dev_err(dev, "P%d_ALIGN reg field init failed\n", i);
+                       return PTR_ERR(wiz->p_align[i]);
+               }
+
+               wiz->p_raw_auto_start[i] =
+                 devm_regmap_field_alloc(dev, regmap, p_raw_auto_start[i]);
+               if (IS_ERR(wiz->p_raw_auto_start[i])) {
+                       dev_err(dev, "P%d_RAW_AUTO_START reg field init fail\n",
+                               i);
+                       return PTR_ERR(wiz->p_raw_auto_start[i]);
+               }
+
+               wiz->p_standard_mode[i] =
+                 devm_regmap_field_alloc(dev, regmap, p_standard_mode[i]);
+               if (IS_ERR(wiz->p_standard_mode[i])) {
+                       dev_err(dev, "P%d_STANDARD_MODE reg field init fail\n",
+                               i);
+                       return PTR_ERR(wiz->p_standard_mode[i]);
+               }
+       }
+
+       wiz->typec_ln10_swap = devm_regmap_field_alloc(dev, regmap,
+                                                      typec_ln10_swap);
+       if (IS_ERR(wiz->typec_ln10_swap)) {
+               dev_err(dev, "LN10_SWAP reg field init failed\n");
+               return PTR_ERR(wiz->typec_ln10_swap);
+       }
+
+       return 0;
+}
+
+static u8 wiz_clk_mux_get_parent(struct clk_hw *hw)
+{
+       struct wiz_clk_mux *mux = to_wiz_clk_mux(hw);
+       struct regmap_field *field = mux->field;
+       unsigned int val;
+
+       regmap_field_read(field, &val);
+       return clk_mux_val_to_index(hw, mux->table, 0, val);
+}
+
+static int wiz_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+       struct wiz_clk_mux *mux = to_wiz_clk_mux(hw);
+       struct regmap_field *field = mux->field;
+       int val;
+
+       val = mux->table[index];
+       return regmap_field_write(field, val);
+}
+
+static const struct clk_ops wiz_clk_mux_ops = {
+       .set_parent = wiz_clk_mux_set_parent,
+       .get_parent = wiz_clk_mux_get_parent,
+};
+
+static int wiz_mux_clk_register(struct wiz *wiz, struct device_node *node,
+                               struct regmap_field *field, u32 *table)
+{
+       struct device *dev = wiz->dev;
+       struct clk_init_data *init;
+       const char **parent_names;
+       unsigned int num_parents;
+       struct wiz_clk_mux *mux;
+       char clk_name[100];
+       struct clk *clk;
+       int ret;
+
+       mux = devm_kzalloc(dev, sizeof(*mux), GFP_KERNEL);
+       if (!mux)
+               return -ENOMEM;
+
+       num_parents = of_clk_get_parent_count(node);
+       if (num_parents < 2) {
+               dev_err(dev, "SERDES clock must have parents\n");
+               return -EINVAL;
+       }
+
+       parent_names = devm_kzalloc(dev, (sizeof(char *) * num_parents),
+                                   GFP_KERNEL);
+       if (!parent_names)
+               return -ENOMEM;
+
+       of_clk_parent_fill(node, parent_names, num_parents);
+
+       snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+                node->name);
+
+       init = &mux->clk_data;
+
+       init->ops = &wiz_clk_mux_ops;
+       init->flags = CLK_SET_RATE_NO_REPARENT;
+       init->parent_names = parent_names;
+       init->num_parents = num_parents;
+       init->name = clk_name;
+
+       mux->field = field;
+       mux->table = table;
+       mux->hw.init = init;
+
+       clk = devm_clk_register(dev, &mux->hw);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+       if (ret)
+               dev_err(dev, "Failed to add clock provider: %s\n", clk_name);
+
+       return ret;
+}
+
+static unsigned long wiz_clk_div_recalc_rate(struct clk_hw *hw,
+                                            unsigned long parent_rate)
+{
+       struct wiz_clk_divider *div = to_wiz_clk_div(hw);
+       struct regmap_field *field = div->field;
+       int val;
+
+       regmap_field_read(field, &val);
+
+       return divider_recalc_rate(hw, parent_rate, val, div->table, 0x0, 2);
+}
+
+static long wiz_clk_div_round_rate(struct clk_hw *hw, unsigned long rate,
+                                  unsigned long *prate)
+{
+       struct wiz_clk_divider *div = to_wiz_clk_div(hw);
+
+       return divider_round_rate(hw, rate, prate, div->table, 2, 0x0);
+}
+
+static int wiz_clk_div_set_rate(struct clk_hw *hw, unsigned long rate,
+                               unsigned long parent_rate)
+{
+       struct wiz_clk_divider *div = to_wiz_clk_div(hw);
+       struct regmap_field *field = div->field;
+       int val;
+
+       val = divider_get_val(rate, parent_rate, div->table, 2, 0x0);
+       if (val < 0)
+               return val;
+
+       return regmap_field_write(field, val);
+}
+
+static const struct clk_ops wiz_clk_div_ops = {
+       .recalc_rate = wiz_clk_div_recalc_rate,
+       .round_rate = wiz_clk_div_round_rate,
+       .set_rate = wiz_clk_div_set_rate,
+};
+
+static int wiz_div_clk_register(struct wiz *wiz, struct device_node *node,
+                               struct regmap_field *field,
+                               struct clk_div_table *table)
+{
+       struct device *dev = wiz->dev;
+       struct wiz_clk_divider *div;
+       struct clk_init_data *init;
+       const char **parent_names;
+       char clk_name[100];
+       struct clk *clk;
+       int ret;
+
+       div = devm_kzalloc(dev, sizeof(*div), GFP_KERNEL);
+       if (!div)
+               return -ENOMEM;
+
+       snprintf(clk_name, sizeof(clk_name), "%s_%s", dev_name(dev),
+                node->name);
+
+       parent_names = devm_kzalloc(dev, sizeof(char *), GFP_KERNEL);
+       if (!parent_names)
+               return -ENOMEM;
+
+       of_clk_parent_fill(node, parent_names, 1);
+
+       init = &div->clk_data;
+
+       init->ops = &wiz_clk_div_ops;
+       init->flags = 0;
+       init->parent_names = parent_names;
+       init->num_parents = 1;
+       init->name = clk_name;
+
+       div->field = field;
+       div->table = table;
+       div->hw.init = init;
+
+       clk = devm_clk_register(dev, &div->hw);
+       if (IS_ERR(clk))
+               return PTR_ERR(clk);
+
+       ret = of_clk_add_provider(node, of_clk_src_simple_get, clk);
+       if (ret)
+               dev_err(dev, "Failed to add clock provider: %s\n", clk_name);
+
+       return ret;
+}
+
+static void wiz_clock_cleanup(struct wiz *wiz, struct device_node *node)
+{
+       struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
+       struct device_node *clk_node;
+       int i;
+
+       for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
+               clk_node = of_get_child_by_name(node, clk_mux_sel[i].node_name);
+               of_clk_del_provider(clk_node);
+               of_node_put(clk_node);
+       }
+}
+
+static int wiz_clock_init(struct wiz *wiz, struct device_node *node)
+{
+       struct wiz_clk_mux_sel *clk_mux_sel = wiz->clk_mux_sel;
+       struct device *dev = wiz->dev;
+       struct device_node *clk_node;
+       const char *node_name;
+       unsigned long rate;
+       struct clk *clk;
+       int ret;
+       int i;
+
+       clk = devm_clk_get(dev, "core_ref_clk");
+       if (IS_ERR(clk)) {
+               dev_err(dev, "core_ref_clk clock not found\n");
+               ret = PTR_ERR(clk);
+               return ret;
+       }
+
+       rate = clk_get_rate(clk);
+       if (rate >= 100000000)
+               regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x1);
+       else
+               regmap_field_write(wiz->pma_cmn_refclk_int_mode, 0x3);
+
+       clk = devm_clk_get(dev, "ext_ref_clk");
+       if (IS_ERR(clk)) {
+               dev_err(dev, "ext_ref_clk clock not found\n");
+               ret = PTR_ERR(clk);
+               return ret;
+       }
+
+       rate = clk_get_rate(clk);
+       if (rate >= 100000000)
+               regmap_field_write(wiz->pma_cmn_refclk_mode, 0x0);
+       else
+               regmap_field_write(wiz->pma_cmn_refclk_mode, 0x2);
+
+       for (i = 0; i < WIZ_MUX_NUM_CLOCKS; i++) {
+               node_name = clk_mux_sel[i].node_name;
+               clk_node = of_get_child_by_name(node, node_name);
+               if (!clk_node) {
+                       dev_err(dev, "Unable to get %s node\n", node_name);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ret = wiz_mux_clk_register(wiz, clk_node, clk_mux_sel[i].field,
+                                          clk_mux_sel[i].table);
+               if (ret) {
+                       dev_err(dev, "Failed to register %s clock\n",
+                               node_name);
+                       of_node_put(clk_node);
+                       goto err;
+               }
+
+               of_node_put(clk_node);
+       }
+
+       for (i = 0; i < wiz->clk_div_sel_num; i++) {
+               node_name = clk_div_sel[i].node_name;
+               clk_node = of_get_child_by_name(node, node_name);
+               if (!clk_node) {
+                       dev_err(dev, "Unable to get %s node\n", node_name);
+                       ret = -EINVAL;
+                       goto err;
+               }
+
+               ret = wiz_div_clk_register(wiz, clk_node, clk_div_sel[i].field,
+                                          clk_div_sel[i].table);
+               if (ret) {
+                       dev_err(dev, "Failed to register %s clock\n",
+                               node_name);
+                       of_node_put(clk_node);
+                       goto err;
+               }
+
+               of_node_put(clk_node);
+       }
+
+       return 0;
+err:
+       wiz_clock_cleanup(wiz, node);
+
+       return ret;
+}
+
+static int wiz_phy_reset_assert(struct reset_controller_dev *rcdev,
+                               unsigned long id)
+{
+       struct device *dev = rcdev->dev;
+       struct wiz *wiz = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (id == 0) {
+               ret = regmap_field_write(wiz->phy_reset_n, false);
+               return ret;
+       }
+
+       ret = regmap_field_write(wiz->p_enable[id - 1], false);
+       return ret;
+}
+
+static int wiz_phy_reset_deassert(struct reset_controller_dev *rcdev,
+                                 unsigned long id)
+{
+       struct device *dev = rcdev->dev;
+       struct wiz *wiz = dev_get_drvdata(dev);
+       int ret;
+
+       /* if typec-dir gpio was specified, set LN10 SWAP bit based on that */
+       if (id == 0 && wiz->gpio_typec_dir) {
+               if (wiz->typec_dir_delay)
+                       msleep_interruptible(wiz->typec_dir_delay);
+
+               if (gpiod_get_value_cansleep(wiz->gpio_typec_dir))
+                       regmap_field_write(wiz->typec_ln10_swap, 1);
+               else
+                       regmap_field_write(wiz->typec_ln10_swap, 0);
+       }
+
+       if (id == 0) {
+               ret = regmap_field_write(wiz->phy_reset_n, true);
+               return ret;
+       }
+
+       ret = regmap_field_write(wiz->p_enable[id - 1], true);
+       return ret;
+}
+
+static const struct reset_control_ops wiz_phy_reset_ops = {
+       .assert = wiz_phy_reset_assert,
+       .deassert = wiz_phy_reset_deassert,
+};
+
+static struct regmap_config wiz_regmap_config = {
+       .reg_bits = 32,
+       .val_bits = 32,
+       .reg_stride = 4,
+       .fast_io = true,
+};
+
+static const struct of_device_id wiz_id_table[] = {
+       {
+               .compatible = "ti,j721e-wiz-16g", .data = (void *)J721E_WIZ_16G
+       },
+       {
+               .compatible = "ti,j721e-wiz-10g", .data = (void *)J721E_WIZ_10G
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, wiz_id_table);
+
+static int wiz_probe(struct platform_device *pdev)
+{
+       struct reset_controller_dev *phy_reset_dev;
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct platform_device *serdes_pdev;
+       struct device_node *child_node;
+       struct regmap *regmap;
+       struct resource res;
+       void __iomem *base;
+       struct wiz *wiz;
+       u32 num_lanes;
+       int ret;
+
+       wiz = devm_kzalloc(dev, sizeof(*wiz), GFP_KERNEL);
+       if (!wiz)
+               return -ENOMEM;
+
+       wiz->type = (enum wiz_type)of_device_get_match_data(dev);
+
+       child_node = of_get_child_by_name(node, "serdes");
+       if (!child_node) {
+               dev_err(dev, "Failed to get SERDES child DT node\n");
+               return -ENODEV;
+       }
+
+       ret = of_address_to_resource(child_node, 0, &res);
+       if (ret) {
+               dev_err(dev, "Failed to get memory resource\n");
+               goto err_addr_to_resource;
+       }
+
+       base = devm_ioremap(dev, res.start, resource_size(&res));
+       if (!base)
+               goto err_addr_to_resource;
+
+       regmap = devm_regmap_init_mmio(dev, base, &wiz_regmap_config);
+       if (IS_ERR(regmap)) {
+               dev_err(dev, "Failed to initialize regmap\n");
+               ret = PTR_ERR(regmap);
+               goto err_addr_to_resource;
+       }
+
+       ret = of_property_read_u32(node, "num-lanes", &num_lanes);
+       if (ret) {
+               dev_err(dev, "Failed to read num-lanes property\n");
+               goto err_addr_to_resource;
+       }
+
+       if (num_lanes > WIZ_MAX_LANES) {
+               dev_err(dev, "Cannot support %d lanes\n", num_lanes);
+               goto err_addr_to_resource;
+       }
+
+       wiz->gpio_typec_dir = devm_gpiod_get_optional(dev, "typec-dir",
+                                                     GPIOD_IN);
+       if (IS_ERR(wiz->gpio_typec_dir)) {
+               ret = PTR_ERR(wiz->gpio_typec_dir);
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "Failed to request typec-dir gpio: %d\n",
+                               ret);
+               goto err_addr_to_resource;
+       }
+
+       if (wiz->gpio_typec_dir) {
+               ret = of_property_read_u32(node, "typec-dir-debounce-ms",
+                                          &wiz->typec_dir_delay);
+               if (ret && ret != -EINVAL) {
+                       dev_err(dev, "Invalid typec-dir-debounce property\n");
+                       goto err_addr_to_resource;
+               }
+
+               /* use min. debounce from Type-C spec if not provided in DT  */
+               if (ret == -EINVAL)
+                       wiz->typec_dir_delay = WIZ_TYPEC_DIR_DEBOUNCE_MIN;
+
+               if (wiz->typec_dir_delay < WIZ_TYPEC_DIR_DEBOUNCE_MIN ||
+                   wiz->typec_dir_delay > WIZ_TYPEC_DIR_DEBOUNCE_MAX) {
+                       dev_err(dev, "Invalid typec-dir-debounce property\n");
+                       goto err_addr_to_resource;
+               }
+       }
+
+       wiz->dev = dev;
+       wiz->regmap = regmap;
+       wiz->num_lanes = num_lanes;
+       if (wiz->type == J721E_WIZ_10G)
+               wiz->clk_mux_sel = clk_mux_sel_10g;
+       else
+               wiz->clk_mux_sel = clk_mux_sel_16g;
+
+       wiz->clk_div_sel = clk_div_sel;
+
+       if (wiz->type == J721E_WIZ_10G)
+               wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_10G;
+       else
+               wiz->clk_div_sel_num = WIZ_DIV_NUM_CLOCKS_16G;
+
+       platform_set_drvdata(pdev, wiz);
+
+       ret = wiz_regfield_init(wiz);
+       if (ret) {
+               dev_err(dev, "Failed to initialize regfields\n");
+               goto err_addr_to_resource;
+       }
+
+       phy_reset_dev = &wiz->wiz_phy_reset_dev;
+       phy_reset_dev->dev = dev;
+       phy_reset_dev->ops = &wiz_phy_reset_ops,
+       phy_reset_dev->owner = THIS_MODULE,
+       phy_reset_dev->of_node = node;
+       /* Reset for each of the lane and one for the entire SERDES */
+       phy_reset_dev->nr_resets = num_lanes + 1;
+
+       ret = devm_reset_controller_register(dev, phy_reset_dev);
+       if (ret < 0) {
+               dev_warn(dev, "Failed to register reset controller\n");
+               goto err_addr_to_resource;
+       }
+
+       pm_runtime_enable(dev);
+       ret = pm_runtime_get_sync(dev);
+       if (ret < 0) {
+               dev_err(dev, "pm_runtime_get_sync failed\n");
+               goto err_get_sync;
+       }
+
+       ret = wiz_clock_init(wiz, node);
+       if (ret < 0) {
+               dev_warn(dev, "Failed to initialize clocks\n");
+               goto err_get_sync;
+       }
+
+       serdes_pdev = of_platform_device_create(child_node, NULL, dev);
+       if (!serdes_pdev) {
+               dev_WARN(dev, "Unable to create SERDES platform device\n");
+               goto err_pdev_create;
+       }
+       wiz->serdes_pdev = serdes_pdev;
+
+       ret = wiz_init(wiz);
+       if (ret) {
+               dev_err(dev, "WIZ initialization failed\n");
+               goto err_wiz_init;
+       }
+
+       of_node_put(child_node);
+       return 0;
+
+err_wiz_init:
+       of_platform_device_destroy(&serdes_pdev->dev, NULL);
+
+err_pdev_create:
+       wiz_clock_cleanup(wiz, node);
+
+err_get_sync:
+       pm_runtime_put(dev);
+       pm_runtime_disable(dev);
+
+err_addr_to_resource:
+       of_node_put(child_node);
+
+       return ret;
+}
+
+static int wiz_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct platform_device *serdes_pdev;
+       struct wiz *wiz;
+
+       wiz = dev_get_drvdata(dev);
+       serdes_pdev = wiz->serdes_pdev;
+
+       of_platform_device_destroy(&serdes_pdev->dev, NULL);
+       wiz_clock_cleanup(wiz, node);
+       pm_runtime_put(dev);
+       pm_runtime_disable(dev);
+
+       return 0;
+}
+
+static struct platform_driver wiz_driver = {
+       .probe          = wiz_probe,
+       .remove         = wiz_remove,
+       .driver         = {
+               .name   = "wiz",
+               .of_match_table = wiz_id_table,
+       },
+};
+module_platform_driver(wiz_driver);
+
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TI J721E WIZ driver");
+MODULE_LICENSE("GPL v2");
index edd6859..a879465 100644 (file)
@@ -850,6 +850,12 @@ static int ti_pipe3_probe(struct platform_device *pdev)
 
 static int ti_pipe3_remove(struct platform_device *pdev)
 {
+       struct ti_pipe3 *phy = platform_get_drvdata(pdev);
+
+       if (phy->mode == PIPE3_MODE_SATA) {
+               clk_disable_unprepare(phy->refclk);
+               phy->sata_refclk_enabled = false;
+       }
        pm_runtime_disable(&pdev->dev);
 
        return 0;
@@ -900,18 +906,8 @@ static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy)
 {
        if (!IS_ERR(phy->wkupclk))
                clk_disable_unprepare(phy->wkupclk);
-       if (!IS_ERR(phy->refclk)) {
+       if (!IS_ERR(phy->refclk))
                clk_disable_unprepare(phy->refclk);
-               /*
-                * SATA refclk needs an additional disable as we left it
-                * on in probe to avoid Errata i783
-                */
-               if (phy->sata_refclk_enabled) {
-                       clk_disable_unprepare(phy->refclk);
-                       phy->sata_refclk_enabled = false;
-               }
-       }
-
        if (!IS_ERR(phy->div_clk))
                clk_disable_unprepare(phy->div_clk);
 }
index 7f2f065..52756fc 100644 (file)
@@ -32,7 +32,7 @@ static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
        struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
 
        if (priv->phy) {
-               phy_put(priv->phy);
+               phy_put(&pdev->dev, priv->phy);
                priv->phy = NULL;
        }
 
index 0217495..3eed333 100644 (file)
@@ -29,7 +29,7 @@ static int usbhs_rza2_hardware_exit(struct platform_device *pdev)
 {
        struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
 
-       phy_put(priv->phy);
+       phy_put(&pdev->dev, priv->phy);
        priv->phy = NULL;
 
        return 0;
index b6a1eaf..1f3f866 100644 (file)
@@ -16,5 +16,6 @@
 #define PHY_TYPE_USB2          3
 #define PHY_TYPE_USB3          4
 #define PHY_TYPE_UFS           5
+#define PHY_TYPE_DP            6
 
 #endif /* _DT_BINDINGS_PHY */
diff --git a/include/linux/phy/phy-dp.h b/include/linux/phy/phy-dp.h
new file mode 100644 (file)
index 0000000..18cad23
--- /dev/null
@@ -0,0 +1,95 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2019 Cadence Design Systems Inc.
+ */
+
+#ifndef __PHY_DP_H_
+#define __PHY_DP_H_
+
+#include <linux/types.h>
+
+/**
+ * struct phy_configure_opts_dp - DisplayPort PHY configuration set
+ *
+ * This structure is used to represent the configuration state of a
+ * DisplayPort phy.
+ */
+struct phy_configure_opts_dp {
+       /**
+        * @link_rate:
+        *
+        * Link Rate, in Mb/s, of the main link.
+        *
+        * Allowed values: 1620, 2160, 2430, 2700, 3240, 4320, 5400, 8100 Mb/s
+        */
+       unsigned int link_rate;
+
+       /**
+        * @lanes:
+        *
+        * Number of active, consecutive, data lanes, starting from
+        * lane 0, used for the transmissions on main link.
+        *
+        * Allowed values: 1, 2, 4
+        */
+       unsigned int lanes;
+
+       /**
+        * @voltage:
+        *
+        * Voltage swing levels, as specified by DisplayPort specification,
+        * to be used by particular lanes. One value per lane.
+        * voltage[0] is for lane 0, voltage[1] is for lane 1, etc.
+        *
+        * Maximum value: 3
+        */
+       unsigned int voltage[4];
+
+       /**
+        * @pre:
+        *
+        * Pre-emphasis levels, as specified by DisplayPort specification, to be
+        * used by particular lanes. One value per lane.
+        *
+        * Maximum value: 3
+        */
+       unsigned int pre[4];
+
+       /**
+        * @ssc:
+        *
+        * Flag indicating, whether or not to enable spread-spectrum clocking.
+        *
+        */
+       u8 ssc : 1;
+
+       /**
+        * @set_rate:
+        *
+        * Flag indicating, whether or not reconfigure link rate and SSC to
+        * requested values.
+        *
+        */
+       u8 set_rate : 1;
+
+       /**
+        * @set_lanes:
+        *
+        * Flag indicating, whether or not reconfigure lane count to
+        * requested value.
+        *
+        */
+       u8 set_lanes : 1;
+
+       /**
+        * @set_voltages:
+        *
+        * Flag indicating, whether or not reconfigure voltage swing
+        * and pre-emphasis to requested values. Only lanes specified
+        * by "lanes" parameter will be affected.
+        *
+        */
+       u8 set_voltages : 1;
+};
+
+#endif /* __PHY_DP_H_ */
index 56d3a10..bcee8eb 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/regulator/consumer.h>
 
+#include <linux/phy/phy-dp.h>
 #include <linux/phy/phy-mipi-dphy.h>
 
 struct phy;
@@ -40,6 +41,7 @@ enum phy_mode {
        PHY_MODE_MIPI_DPHY,
        PHY_MODE_SATA,
        PHY_MODE_LVDS,
+       PHY_MODE_DP
 };
 
 /**
@@ -47,9 +49,12 @@ enum phy_mode {
  *
  * @mipi_dphy: Configuration set applicable for phys supporting
  *             the MIPI_DPHY phy mode.
+ * @dp:                Configuration set applicable for phys supporting
+ *             the DisplayPort protocol.
  */
 union phy_configure_opts {
        struct phy_configure_opts_mipi_dphy     mipi_dphy;
+       struct phy_configure_opts_dp            dp;
 };
 
 /**
@@ -234,7 +239,8 @@ struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
                            const char *con_id);
 struct phy *devm_of_phy_get_by_index(struct device *dev, struct device_node *np,
                                     int index);
-void phy_put(struct phy *phy);
+void of_phy_put(struct phy *phy);
+void phy_put(struct device *dev, struct phy *phy);
 void devm_phy_put(struct device *dev, struct phy *phy);
 struct phy *of_phy_get(struct device_node *np, const char *con_id);
 struct phy *of_phy_simple_xlate(struct device *dev,
@@ -419,7 +425,11 @@ static inline struct phy *devm_of_phy_get_by_index(struct device *dev,
        return ERR_PTR(-ENOSYS);
 }
 
-static inline void phy_put(struct phy *phy)
+static inline void of_phy_put(struct phy *phy)
+{
+}
+
+static inline void phy_put(struct device *dev, struct phy *phy)
 {
 }