]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
Merge branch 'next' into resolution
authorKishon Vijay Abraham I <kishon@ti.com>
Thu, 15 Sep 2016 10:16:11 +0000 (15:46 +0530)
committerKishon Vijay Abraham I <kishon@ti.com>
Thu, 15 Sep 2016 11:15:20 +0000 (16:45 +0530)
Conflicts:
drivers/extcon/extcon-adc-jack.c
drivers/extcon/extcon-arizona.c
drivers/extcon/extcon-gpio.c
include/linux/extcon.h

28 files changed:
Documentation/devicetree/bindings/phy/bcm-ns-usb3-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt
Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
Documentation/devicetree/bindings/phy/ti-phy.txt
drivers/extcon/extcon-gpio.c
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/phy-bcm-ns-usb3.c [new file with mode: 0644]
drivers/phy/phy-bcm-ns2-pcie.c
drivers/phy/phy-core.c
drivers/phy/phy-da8xx-usb.c
drivers/phy/phy-exynos5-usbdrd.c
drivers/phy/phy-omap-usb2.c
drivers/phy/phy-qcom-ufs.c
drivers/phy/phy-rcar-gen3-usb2.c
drivers/phy/phy-rockchip-inno-usb2.c [new file with mode: 0644]
drivers/phy/phy-rockchip-pcie.c [new file with mode: 0644]
drivers/phy/phy-rockchip-typec.c [new file with mode: 0644]
drivers/phy/phy-rockchip-usb.c
drivers/phy/phy-sun4i-usb.c
drivers/phy/phy-twl4030-usb.c
drivers/phy/tegra/xusb.c
include/linux/extcon.h
include/linux/phy/phy.h

diff --git a/Documentation/devicetree/bindings/phy/bcm-ns-usb3-phy.txt b/Documentation/devicetree/bindings/phy/bcm-ns-usb3-phy.txt
new file mode 100644 (file)
index 0000000..09aeba9
--- /dev/null
@@ -0,0 +1,23 @@
+Driver for Broadcom Northstar USB 3.0 PHY
+
+Required properties:
+
+- compatible: one of: "brcm,ns-ax-usb3-phy", "brcm,ns-bx-usb3-phy".
+- reg: register mappings for DMP (Device Management Plugin) and ChipCommon B
+       MMI.
+- reg-names: "dmp" and "ccb-mii"
+
+Initialization of USB 3.0 PHY depends on Northstar version. There are currently
+three known series: Ax, Bx and Cx.
+Known A0: BCM4707 rev 0
+Known B0: BCM4707 rev 4, BCM53573 rev 2
+Known B1: BCM4707 rev 6
+Known C0: BCM47094 rev 0
+
+Example:
+       usb3-phy {
+               compatible = "brcm,ns-ax-usb3-phy";
+               reg = <0x18105000 0x1000>, <0x18003000 0x1000>;
+               reg-names = "dmp", "ccb-mii";
+               #phy-cells = <0>;
+       };
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-inno-usb2.txt
new file mode 100644 (file)
index 0000000..3c29c77
--- /dev/null
@@ -0,0 +1,64 @@
+ROCKCHIP USB2.0 PHY WITH INNO IP BLOCK
+
+Required properties (phy (parent) node):
+ - compatible : should be one of the listed compatibles:
+       * "rockchip,rk3366-usb2phy"
+       * "rockchip,rk3399-usb2phy"
+ - reg : the address offset of grf for usb-phy configuration.
+ - #clock-cells : should be 0.
+ - clock-output-names : specify the 480m output clock name.
+
+Optional properties:
+ - clocks : phandle + phy specifier pair, for the input clock of phy.
+ - clock-names : input clock name of phy, must be "phyclk".
+
+Required nodes : a sub-node is required for each port the phy provides.
+                The sub-node name is used to identify host or otg port,
+                and shall be the following entries:
+       * "otg-port" : the name of otg port.
+       * "host-port" : the name of host port.
+
+Required properties (port (child) node):
+ - #phy-cells : must be 0. See ./phy-bindings.txt for details.
+ - interrupts : specify an interrupt for each entry in interrupt-names.
+ - interrupt-names : a list which shall be the following entries:
+       * "otg-id" : for the otg id interrupt.
+       * "otg-bvalid" : for the otg vbus interrupt.
+       * "linestate" : for the host/otg linestate interrupt.
+
+Optional properties:
+ - phy-supply : phandle to a regulator that provides power to VBUS.
+               See ./phy-bindings.txt for details.
+
+Example:
+
+grf: syscon@ff770000 {
+       compatible = "rockchip,rk3366-grf", "syscon", "simple-mfd";
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+...
+
+       u2phy: usb2-phy@700 {
+               compatible = "rockchip,rk3366-usb2phy";
+               reg = <0x700 0x2c>;
+               #clock-cells = <0>;
+               clock-output-names = "sclk_otgphy0_480m";
+
+               u2phy_otg: otg-port {
+                       #phy-cells = <0>;
+                       interrupts = <GIC_SPI 93 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 94 IRQ_TYPE_LEVEL_HIGH>,
+                                    <GIC_SPI 95 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "otg-id", "otg-bvalid", "linestate";
+                       status = "okay";
+               };
+
+               u2phy_host: host-port {
+                       #phy-cells = <0>;
+                       interrupts = <GIC_SPI 96 IRQ_TYPE_LEVEL_HIGH>;
+                       interrupt-names = "linestate";
+                       status = "okay";
+               };
+       };
+};
diff --git a/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt b/Documentation/devicetree/bindings/phy/phy-rockchip-typec.txt
new file mode 100644 (file)
index 0000000..6ea867e
--- /dev/null
@@ -0,0 +1,101 @@
+* ROCKCHIP type-c PHY
+---------------------
+
+Required properties:
+ - compatible : must be "rockchip,rk3399-typec-phy"
+ - reg: Address and length of the usb phy control register set
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - clocks : phandle + clock specifier for the phy clocks
+ - clock-names : string, clock name, must be "tcpdcore", "tcpdphy-ref";
+ - assigned-clocks: main clock, should be <&cru SCLK_UPHY0_TCPDCORE> or
+                   <&cru SCLK_UPHY1_TCPDCORE>;
+ - assigned-clock-rates : the phy core clk frequency, shall be: 50000000
+ - resets : a list of phandle + reset specifier pairs
+ - reset-names : string reset name, must be:
+                "uphy", "uphy-pipe", "uphy-tcphy"
+ - extcon : extcon specifier for the Power Delivery
+
+Note, there are 2 type-c phys for RK3399, and they are almost identical, except
+these registers(description below), every register node contains 3 sections:
+offset, enable bit, write mask bit.
+ - rockchip,typec-conn-dir : the register of type-c connector direction,
+   for type-c phy0, it must be <0xe580 0 16>;
+   for type-c phy1, it must be <0xe58c 0 16>;
+ - rockchip,usb3tousb2-en : the register of type-c force usb3 to usb2 enable
+   control.
+   for type-c phy0, it must be <0xe580 3 19>;
+   for type-c phy1, it must be <0xe58c 3 19>;
+ - rockchip,external-psm : the register of type-c phy external psm clock
+   selection.
+   for type-c phy0, it must be <0xe588 14 30>;
+   for type-c phy1, it must be <0xe594 14 30>;
+ - rockchip,pipe-status : the register of type-c phy pipe status.
+   for type-c phy0, it must be <0xe5c0 0 0>;
+   for type-c phy1, it must be <0xe5c0 16 16>;
+
+Required nodes : a sub-node is required for each port the phy provides.
+                The sub-node name is used to identify dp or usb3 port,
+                and shall be the following entries:
+       * "dp-port" : the name of DP port.
+       * "usb3-port" : the name of USB3 port.
+
+Required properties (port (child) node):
+- #phy-cells : must be 0, See ./phy-bindings.txt for details.
+
+Example:
+       tcphy0: phy@ff7c0000 {
+               compatible = "rockchip,rk3399-typec-phy";
+               reg = <0x0 0xff7c0000 0x0 0x40000>;
+               rockchip,grf = <&grf>;
+               extcon = <&fusb0>;
+               clocks = <&cru SCLK_UPHY0_TCPDCORE>,
+                        <&cru SCLK_UPHY0_TCPDPHY_REF>;
+               clock-names = "tcpdcore", "tcpdphy-ref";
+               assigned-clocks = <&cru SCLK_UPHY0_TCPDCORE>;
+               assigned-clock-rates = <50000000>;
+               resets = <&cru SRST_UPHY0>,
+                        <&cru SRST_UPHY0_PIPE_L00>,
+                        <&cru SRST_P_UPHY0_TCPHY>;
+               reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+               rockchip,typec-conn-dir = <0xe580 0 16>;
+               rockchip,usb3tousb2-en = <0xe580 3 19>;
+               rockchip,external-psm = <0xe588 14 30>;
+               rockchip,pipe-status = <0xe5c0 0 0>;
+
+               tcphy0_dp: dp-port {
+                       #phy-cells = <0>;
+               };
+
+               tcphy0_usb3: usb3-port {
+                       #phy-cells = <0>;
+               };
+       };
+
+       tcphy1: phy@ff800000 {
+               compatible = "rockchip,rk3399-typec-phy";
+               reg = <0x0 0xff800000 0x0 0x40000>;
+               rockchip,grf = <&grf>;
+               extcon = <&fusb1>;
+               clocks = <&cru SCLK_UPHY1_TCPDCORE>,
+                        <&cru SCLK_UPHY1_TCPDPHY_REF>;
+               clock-names = "tcpdcore", "tcpdphy-ref";
+               assigned-clocks = <&cru SCLK_UPHY1_TCPDCORE>;
+               assigned-clock-rates = <50000000>;
+               resets = <&cru SRST_UPHY1>,
+                        <&cru SRST_UPHY1_PIPE_L00>,
+                        <&cru SRST_P_UPHY1_TCPHY>;
+               reset-names = "uphy", "uphy-pipe", "uphy-tcphy";
+               rockchip,typec-conn-dir = <0xe58c 0 16>;
+               rockchip,usb3tousb2-en = <0xe58c 3 19>;
+               rockchip,external-psm = <0xe594 14 30>;
+               rockchip,pipe-status = <0xe5c0 16 16>;
+
+               tcphy1_dp: dp-port {
+                       #phy-cells = <0>;
+               };
+
+               tcphy1_usb3: usb3-port {
+                       #phy-cells = <0>;
+               };
+       };
index 2281d6cdecb13cfec3c7c6c9ad6ab4a641524fa4..ace9cce2704a889525c1fd8219501d2bb1329b7f 100644 (file)
@@ -5,6 +5,8 @@ This file provides information on what the device node for the R-Car generation
 
 Required properties:
 - compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
+             SoC.
+             "renesas,usb2-phy-r8a7796" if the device is a part of an R8A7796
              SoC.
              "renesas,rcar-gen3-usb2-phy" for a generic R-Car Gen3 compatible device.
 
@@ -30,11 +32,11 @@ Example (R-Car H3):
                compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
                reg = <0 0xee080200 0 0x700>;
                interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
-               clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
+               clocks = <&cpg CPG_MOD 703>;
        };
 
        usb-phy@ee0a0200 {
                compatible = "renesas,usb2-phy-r8a7795", "renesas,rcar-gen3-usb2-phy";
                reg = <0 0xee0a0200 0 0x700>;
-               clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
+               clocks = <&cpg CPG_MOD 702>;
        };
diff --git a/Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-pcie-phy.txt
new file mode 100644 (file)
index 0000000..0f6222a
--- /dev/null
@@ -0,0 +1,31 @@
+Rockchip PCIE PHY
+-----------------------
+
+Required properties:
+ - compatible: rockchip,rk3399-pcie-phy
+ - #phy-cells: must be 0
+ - clocks: Must contain an entry in clock-names.
+       See ../clocks/clock-bindings.txt for details.
+ - clock-names: Must be "refclk"
+ - resets: Must contain an entry in reset-names.
+       See ../reset/reset.txt for details.
+ - reset-names: Must be "phy"
+
+Example:
+
+grf: syscon@ff770000 {
+       compatible = "rockchip,rk3399-grf", "syscon", "simple-mfd";
+       #address-cells = <1>;
+       #size-cells = <1>;
+
+       ...
+
+       pcie_phy: pcie-phy {
+               compatible = "rockchip,rk3399-pcie-phy";
+               #phy-cells = <0>;
+               clocks = <&cru SCLK_PCIEPHY_REF>;
+               clock-names = "refclk";
+               resets = <&cru SRST_PCIEPHY>;
+               reset-names = "phy";
+       };
+};
index cc6be9680a6d6859852c27f1ab0d31a898b4eb03..57dc388e2fa2fe7086b2abe186b46ef2703d7112 100644 (file)
@@ -27,6 +27,9 @@ Optional Properties:
 - clocks : phandle + clock specifier for the phy clocks
 - clock-names: string, clock name, must be "phyclk"
 - #clock-cells: for users of the phy-pll, should be 0
+- reset-names: Only allow the following entries:
+ - phy-reset
+- resets: Must contain an entry for each entry in reset-names.
 
 Example:
 
index 95736d77fbb7168f045f031f8bba7f63980ad14d..287150db6db4f958add361f0aeabe087b50b60a9 100644 (file)
@@ -10,6 +10,7 @@ Required properties:
   * allwinner,sun8i-a23-usb-phy
   * allwinner,sun8i-a33-usb-phy
   * allwinner,sun8i-h3-usb-phy
+  * allwinner,sun50i-a64-usb-phy
 - reg : a list of offset + length pairs
 - reg-names :
   * "phy_ctrl"
index a3b394587874dcaeaa34bd08f3fd40d07b19f573..cd13e61570884aac0bb9fc898cc1cd47bc547c12 100644 (file)
@@ -31,6 +31,8 @@ OMAP USB2 PHY
 
 Required properties:
  - compatible: Should be "ti,omap-usb2"
+              Should be "ti,dra7x-usb2" for the 1st instance of USB2 PHY on
+              DRA7x
               Should be "ti,dra7x-usb2-phy2" for the 2nd instance of USB2 PHY
               in DRA7x
  - reg : Address and length of the register set for the device.
index e7aebbc945f65bc24f846efbc465efb1f969db98..ebed22f22d75701e020716c0bd2c4c1da3722fb1 100644 (file)
@@ -49,6 +49,7 @@ static void gpio_extcon_work(struct work_struct *work)
        state = gpiod_get_value_cansleep(data->id_gpiod);
        if (data->pdata->gpio_active_low)
                state = !state;
+
        extcon_set_state_sync(data->edev, data->pdata->extcon_id, state);
 }
 
index 19bff3a10f695fad180a259cc7f68b05f1d950f3..fe00f9134d515e604fcbf24769a90da3376916b8 100644 (file)
@@ -24,6 +24,15 @@ config PHY_BCM_NS_USB2
          Enable this to support Broadcom USB 2.0 PHY connected to the USB
          controller on Northstar family.
 
+config PHY_BCM_NS_USB3
+       tristate "Broadcom Northstar USB 3.0 PHY Driver"
+       depends on ARCH_BCM_IPROC || COMPILE_TEST
+       depends on HAS_IOMEM && OF
+       select GENERIC_PHY
+       help
+         Enable this to support Broadcom USB 3.0 PHY connected to the USB
+         controller on Northstar family.
+
 config PHY_BERLIN_USB
        tristate "Marvell Berlin USB PHY Driver"
        depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
@@ -258,7 +267,9 @@ config PHY_SUN4I_USB
        depends on RESET_CONTROLLER
        depends on EXTCON
        depends on POWER_SUPPLY
+       depends on USB_SUPPORT
        select GENERIC_PHY
+       select USB_COMMON
        help
          Enable this to support the transceiver that is part of Allwinner
          sunxi SoCs.
@@ -358,6 +369,14 @@ config PHY_ROCKCHIP_USB
        help
          Enable this to support the Rockchip USB 2.0 PHY.
 
+config PHY_ROCKCHIP_INNO_USB2
+       tristate "Rockchip INNO USB2PHY Driver"
+       depends on (ARCH_ROCKCHIP || COMPILE_TEST) && OF
+       depends on COMMON_CLK
+       select GENERIC_PHY
+       help
+         Support for Rockchip USB2.0 PHY with Innosilicon IP block.
+
 config PHY_ROCKCHIP_EMMC
        tristate "Rockchip EMMC PHY Driver"
        depends on ARCH_ROCKCHIP && OF
@@ -372,6 +391,23 @@ config PHY_ROCKCHIP_DP
        help
          Enable this to support the Rockchip Display Port PHY.
 
+config PHY_ROCKCHIP_PCIE
+       tristate "Rockchip PCIe PHY Driver"
+       depends on (ARCH_ROCKCHIP && OF) || COMPILE_TEST
+       select GENERIC_PHY
+       select MFD_SYSCON
+       help
+         Enable this to support the Rockchip PCIe PHY.
+
+config PHY_ROCKCHIP_TYPEC
+       tristate "Rockchip TYPEC PHY Driver"
+       depends on OF && (ARCH_ROCKCHIP || COMPILE_TEST)
+       select EXTCON
+       select GENERIC_PHY
+       select RESET_CONTROLLER
+       help
+         Enable this to support the Rockchip USB TYPEC PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
        tristate "ST SPEAR1310-MIPHY driver"
        select GENERIC_PHY
index 90ae19879b0aec609796e73ce400df16bc63b134..a534cf5be07dbe1a00d5bf5f7eab958c66060642 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_GENERIC_PHY)              += phy-core.o
 obj-$(CONFIG_PHY_BCM_NS_USB2)          += phy-bcm-ns-usb2.o
+obj-$(CONFIG_PHY_BCM_NS_USB3)          += phy-bcm-ns-usb3.o
 obj-$(CONFIG_PHY_BERLIN_USB)           += phy-berlin-usb.o
 obj-$(CONFIG_PHY_BERLIN_SATA)          += phy-berlin-sata.o
 obj-$(CONFIG_PHY_DA8XX_USB)            += phy-da8xx-usb.o
@@ -39,8 +40,11 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)   += phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)       += phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)    += phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
+obj-$(CONFIG_PHY_ROCKCHIP_INNO_USB2)   += phy-rockchip-inno-usb2.o
 obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
+obj-$(CONFIG_PHY_ROCKCHIP_PCIE) += phy-rockchip-pcie.o
 obj-$(CONFIG_PHY_ROCKCHIP_DP)          += phy-rockchip-dp.o
+obj-$(CONFIG_PHY_ROCKCHIP_TYPEC) += phy-rockchip-typec.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)    += phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)   += phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)   += phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-bcm-ns-usb3.c b/drivers/phy/phy-bcm-ns-usb3.c
new file mode 100644 (file)
index 0000000..f420fa4
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Broadcom Northstar USB 3.0 PHY Driver
+ *
+ * Copyright (C) 2016 RafaÅ‚ MiÅ‚ecki <rafal@milecki.pl>
+ *
+ * All magic values used for initialization (and related comments) were obtained
+ * from Broadcom's SDK:
+ * Copyright (c) Broadcom Corp, 2012
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bcma/bcma.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+#include <linux/slab.h>
+
+#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000    /* usecs */
+
+enum bcm_ns_family {
+       BCM_NS_UNKNOWN,
+       BCM_NS_AX,
+       BCM_NS_BX,
+};
+
+struct bcm_ns_usb3 {
+       struct device *dev;
+       enum bcm_ns_family family;
+       void __iomem *dmp;
+       void __iomem *ccb_mii;
+       struct phy *phy;
+};
+
+static const struct of_device_id bcm_ns_usb3_id_table[] = {
+       {
+               .compatible = "brcm,ns-ax-usb3-phy",
+               .data = (int *)BCM_NS_AX,
+       },
+       {
+               .compatible = "brcm,ns-bx-usb3-phy",
+               .data = (int *)BCM_NS_BX,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table);
+
+static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr,
+                               u32 mask, u32 value, unsigned long timeout)
+{
+       unsigned long deadline = jiffies + timeout;
+       u32 val;
+
+       do {
+               val = readl(addr);
+               if ((val & mask) == value)
+                       return 0;
+               cpu_relax();
+               udelay(10);
+       } while (!time_after_eq(jiffies, deadline));
+
+       dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
+
+       return -EBUSY;
+}
+
+static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
+{
+       return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL,
+                                   0x0100, 0x0000,
+                                   usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US));
+}
+
+static int bcm_ns_usb3_mii_mng_write32(struct bcm_ns_usb3 *usb3, u32 value)
+{
+       int err;
+
+       err = bcm_ns_usb3_mii_mng_wait_idle(usb3);
+       if (err < 0) {
+               dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value);
+               return err;
+       }
+
+       writel(value, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA);
+
+       return 0;
+}
+
+static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
+{
+       int err;
+
+       /* Enable MDIO. Setting MDCDIV as 26  */
+       writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL);
+
+       /* Wait for MDIO? */
+       udelay(2);
+
+       /* USB3 PLL Block */
+       err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
+       if (err < 0)
+               return err;
+
+       /* Assert Ana_Pllseq start */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x58061000);
+
+       /* Assert CML Divider ratio to 26 */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
+
+       /* Asserting PLL Reset */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x582ec000);
+
+       /* Deaaserting PLL Reset */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x582e8000);
+
+       /* Waiting MII Mgt interface idle */
+       bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+       /* Deasserting USB3 system reset */
+       writel(0, usb3->dmp + BCMA_RESET_CTL);
+
+       /* PLL frequency monitor enable */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x58069000);
+
+       /* PIPE Block */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8060);
+
+       /* CMPMAX & CMPMINTH setting */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x580af30d);
+
+       /* DEGLITCH MIN & MAX setting */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x580e6302);
+
+       /* TXPMD block */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
+
+       /* Enabling SSC */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
+
+       /* Waiting MII Mgt interface idle */
+       bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+       return 0;
+}
+
+static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3)
+{
+       int err;
+
+       /* Enable MDIO. Setting MDCDIV as 26  */
+       writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL);
+
+       /* Wait for MDIO? */
+       udelay(2);
+
+       /* PLL30 block */
+       err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
+       if (err < 0)
+               return err;
+
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
+
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x587e80e0);
+
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x580a009c);
+
+       /* Enable SSC */
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
+
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x580a21d3);
+
+       bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
+
+       /* Waiting MII Mgt interface idle */
+       bcm_ns_usb3_mii_mng_wait_idle(usb3);
+
+       /* Deasserting USB3 system reset */
+       writel(0, usb3->dmp + BCMA_RESET_CTL);
+
+       return 0;
+}
+
+static int bcm_ns_usb3_phy_init(struct phy *phy)
+{
+       struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy);
+       int err;
+
+       /* Perform USB3 system soft reset */
+       writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL);
+
+       switch (usb3->family) {
+       case BCM_NS_AX:
+               err = bcm_ns_usb3_phy_init_ns_ax(usb3);
+               break;
+       case BCM_NS_BX:
+               err = bcm_ns_usb3_phy_init_ns_bx(usb3);
+               break;
+       default:
+               WARN_ON(1);
+               err = -ENOTSUPP;
+       }
+
+       return err;
+}
+
+static const struct phy_ops ops = {
+       .init           = bcm_ns_usb3_phy_init,
+       .owner          = THIS_MODULE,
+};
+
+static int bcm_ns_usb3_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *of_id;
+       struct bcm_ns_usb3 *usb3;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+
+       usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL);
+       if (!usb3)
+               return -ENOMEM;
+
+       usb3->dev = dev;
+
+       of_id = of_match_device(bcm_ns_usb3_id_table, dev);
+       if (!of_id)
+               return -EINVAL;
+       usb3->family = (enum bcm_ns_family)of_id->data;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dmp");
+       usb3->dmp = devm_ioremap_resource(dev, res);
+       if (IS_ERR(usb3->dmp)) {
+               dev_err(dev, "Failed to map DMP regs\n");
+               return PTR_ERR(usb3->dmp);
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ccb-mii");
+       usb3->ccb_mii = devm_ioremap_resource(dev, res);
+       if (IS_ERR(usb3->ccb_mii)) {
+               dev_err(dev, "Failed to map ChipCommon B MII regs\n");
+               return PTR_ERR(usb3->ccb_mii);
+       }
+
+       usb3->phy = devm_phy_create(dev, NULL, &ops);
+       if (IS_ERR(usb3->phy)) {
+               dev_err(dev, "Failed to create PHY\n");
+               return PTR_ERR(usb3->phy);
+       }
+
+       phy_set_drvdata(usb3->phy, usb3);
+       platform_set_drvdata(pdev, usb3);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (!IS_ERR(phy_provider))
+               dev_info(dev, "Registered Broadcom Northstar USB 3.0 PHY driver\n");
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver bcm_ns_usb3_driver = {
+       .probe          = bcm_ns_usb3_probe,
+       .driver = {
+               .name = "bcm_ns_usb3",
+               .of_match_table = bcm_ns_usb3_id_table,
+       },
+};
+module_platform_driver(bcm_ns_usb3_driver);
+
+MODULE_LICENSE("GPL v2");
index 9513f7ab1eaabc933b0276205b748d7e4f102dd0..4c7d11d2b37849233e34f02860bb3ed9765b011c 100644 (file)
 #include <linux/phy.h>
 #include <linux/phy/phy.h>
 
-struct ns2_pci_phy {
-       struct mdio_device *mdiodev;
-       struct phy *phy;
-};
-
 #define BLK_ADDR_REG_OFFSET    0x1f
 #define PLL_AFE1_100MHZ_BLK    0x2100
 #define PLL_CLK_AMP_OFFSET     0x03
@@ -30,17 +25,17 @@ struct ns2_pci_phy {
 
 static int ns2_pci_phy_init(struct phy *p)
 {
-       struct ns2_pci_phy *phy = phy_get_drvdata(p);
+       struct mdio_device *mdiodev = phy_get_drvdata(p);
        int rc;
 
        /* select the AFE 100MHz block page */
-       rc = mdiobus_write(phy->mdiodev->bus, phy->mdiodev->addr,
+       rc = mdiobus_write(mdiodev->bus, mdiodev->addr,
                           BLK_ADDR_REG_OFFSET, PLL_AFE1_100MHZ_BLK);
        if (rc)
                goto err;
 
        /* set the 100 MHz reference clock amplitude to 2.05 v */
-       rc = mdiobus_write(phy->mdiodev->bus, phy->mdiodev->addr,
+       rc = mdiobus_write(mdiodev->bus, mdiodev->addr,
                           PLL_CLK_AMP_OFFSET, PLL_CLK_AMP_2P05V);
        if (rc)
                goto err;
@@ -48,19 +43,19 @@ static int ns2_pci_phy_init(struct phy *p)
        return 0;
 
 err:
-       dev_err(&phy->mdiodev->dev, "Error %d writing to phy\n", rc);
+       dev_err(&mdiodev->dev, "Error %d writing to phy\n", rc);
        return rc;
 }
 
-static struct phy_ops ns2_pci_phy_ops = {
+static const struct phy_ops ns2_pci_phy_ops = {
        .init = ns2_pci_phy_init,
+       .owner = THIS_MODULE,
 };
 
 static int ns2_pci_phy_probe(struct mdio_device *mdiodev)
 {
        struct device *dev = &mdiodev->dev;
        struct phy_provider *provider;
-       struct ns2_pci_phy *p;
        struct phy *phy;
 
        phy = devm_phy_create(dev, dev->of_node, &ns2_pci_phy_ops);
@@ -69,16 +64,7 @@ static int ns2_pci_phy_probe(struct mdio_device *mdiodev)
                return PTR_ERR(phy);
        }
 
-       p = devm_kmalloc(dev, sizeof(struct ns2_pci_phy),
-                        GFP_KERNEL);
-       if (!p)
-               return -ENOMEM;
-
-       p->mdiodev = mdiodev;
-       dev_set_drvdata(dev, p);
-
-       p->phy = phy;
-       phy_set_drvdata(phy, p);
+       phy_set_drvdata(phy, mdiodev);
 
        provider = devm_of_phy_provider_register(&phy->dev,
                                                 of_phy_simple_xlate);
index 8eca906b6e70b463b63a49998f55f8813d54dc92..a268f4d6f3e9002e753d18ecabab123feed37a29 100644 (file)
@@ -357,6 +357,21 @@ int phy_set_mode(struct phy *phy, enum phy_mode mode)
 }
 EXPORT_SYMBOL_GPL(phy_set_mode);
 
+int phy_reset(struct phy *phy)
+{
+       int ret;
+
+       if (!phy || !phy->ops->reset)
+               return 0;
+
+       mutex_lock(&phy->mutex);
+       ret = phy->ops->reset(phy);
+       mutex_unlock(&phy->mutex);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_reset);
+
 /**
  * _of_phy_get() - lookup and obtain a reference to a phy by phandle
  * @np: device_node for which to get the phy
index b2e59b6170ac99fc575495e356bb831a59f6e92c..32ae78c8ca17655d877a66952d098d56a0eacf3c 100644 (file)
@@ -154,7 +154,7 @@ static int da8xx_usb_phy_probe(struct platform_device *pdev)
                d_phy->regmap = syscon_regmap_lookup_by_compatible(
                                                        "ti,da830-cfgchip");
        else
-               d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon.0");
+               d_phy->regmap = syscon_regmap_lookup_by_pdevname("syscon");
        if (IS_ERR(d_phy->regmap)) {
                dev_err(dev, "Failed to get syscon\n");
                return PTR_ERR(d_phy->regmap);
index 20696f53303f0798059f45273cf817cb936ea415..07ed608905ac975440f028b5ac9117f7da5b1ce1 100644 (file)
@@ -249,7 +249,7 @@ static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst,
 static unsigned int
 exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
 {
-       static u32 reg;
+       u32 reg;
        struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
 
        /* restore any previous reference clock settings */
@@ -295,7 +295,7 @@ exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
 static unsigned int
 exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst)
 {
-       static u32 reg;
+       u32 reg;
        struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
 
        /* restore any previous reference clock settings */
index c134989052f5e85968621db791ed21c49a3467f6..fe909fd8144f95df33d29ee346ff9eae19325d7c 100644 (file)
@@ -133,11 +133,49 @@ static int omap_usb_power_on(struct phy *x)
        return omap_usb_phy_power(phy, true);
 }
 
+static int omap_usb2_disable_clocks(struct omap_usb *phy)
+{
+       clk_disable(phy->wkupclk);
+       if (!IS_ERR(phy->optclk))
+               clk_disable(phy->optclk);
+
+       return 0;
+}
+
+static int omap_usb2_enable_clocks(struct omap_usb *phy)
+{
+       int ret;
+
+       ret = clk_enable(phy->wkupclk);
+       if (ret < 0) {
+               dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+               goto err0;
+       }
+
+       if (!IS_ERR(phy->optclk)) {
+               ret = clk_enable(phy->optclk);
+               if (ret < 0) {
+                       dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
+                       goto err1;
+               }
+       }
+
+       return 0;
+
+err1:
+       clk_disable(phy->wkupclk);
+
+err0:
+       return ret;
+}
+
 static int omap_usb_init(struct phy *x)
 {
        struct omap_usb *phy = phy_get_drvdata(x);
        u32 val;
 
+       omap_usb2_enable_clocks(phy);
+
        if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
                /*
                 *
@@ -155,8 +193,16 @@ static int omap_usb_init(struct phy *x)
        return 0;
 }
 
+static int omap_usb_exit(struct phy *x)
+{
+       struct omap_usb *phy = phy_get_drvdata(x);
+
+       return omap_usb2_disable_clocks(phy);
+}
+
 static const struct phy_ops ops = {
        .init           = omap_usb_init,
+       .exit           = omap_usb_exit,
        .power_on       = omap_usb_power_on,
        .power_off      = omap_usb_power_off,
        .owner          = THIS_MODULE,
@@ -376,65 +422,11 @@ static int omap_usb2_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_PM
-
-static int omap_usb2_runtime_suspend(struct device *dev)
-{
-       struct platform_device  *pdev = to_platform_device(dev);
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       clk_disable(phy->wkupclk);
-       if (!IS_ERR(phy->optclk))
-               clk_disable(phy->optclk);
-
-       return 0;
-}
-
-static int omap_usb2_runtime_resume(struct device *dev)
-{
-       struct platform_device  *pdev = to_platform_device(dev);
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-       int ret;
-
-       ret = clk_enable(phy->wkupclk);
-       if (ret < 0) {
-               dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
-               goto err0;
-       }
-
-       if (!IS_ERR(phy->optclk)) {
-               ret = clk_enable(phy->optclk);
-               if (ret < 0) {
-                       dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
-                       goto err1;
-               }
-       }
-
-       return 0;
-
-err1:
-       clk_disable(phy->wkupclk);
-
-err0:
-       return ret;
-}
-
-static const struct dev_pm_ops omap_usb2_pm_ops = {
-       SET_RUNTIME_PM_OPS(omap_usb2_runtime_suspend, omap_usb2_runtime_resume,
-               NULL)
-};
-
-#define DEV_PM_OPS     (&omap_usb2_pm_ops)
-#else
-#define DEV_PM_OPS     NULL
-#endif
-
 static struct platform_driver omap_usb2_driver = {
        .probe          = omap_usb2_probe,
        .remove         = omap_usb2_remove,
        .driver         = {
                .name   = "omap-usb2",
-               .pm     = DEV_PM_OPS,
                .of_match_table = omap_usb2_id_table,
        },
 };
index 107cb57c3513c22642bb14420f47c469a39dcfa2..18a5b495ad65accddda823338376678276b8f0e6 100644 (file)
@@ -283,10 +283,8 @@ static int __ufs_qcom_phy_init_vreg(struct phy *phy,
                        err = 0;
                }
                snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name);
-               if (of_get_property(dev->of_node, prop_name, NULL))
-                       vreg->is_always_on = true;
-               else
-                       vreg->is_always_on = false;
+               vreg->is_always_on = of_property_read_bool(dev->of_node,
+                                                          prop_name);
        }
 
        if (!strcmp(name, "vdda-pll")) {
index 31156c9c4707e81807f86e9e053dba25174c0b51..3d97eadd247d11253263c30981240f8698456b68 100644 (file)
@@ -280,6 +280,7 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch)
 
 static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = {
        { .compatible = "renesas,usb2-phy-r8a7795" },
+       { .compatible = "renesas,usb2-phy-r8a7796" },
        { .compatible = "renesas,rcar-gen3-usb2-phy" },
        { }
 };
diff --git a/drivers/phy/phy-rockchip-inno-usb2.c b/drivers/phy/phy-rockchip-inno-usb2.c
new file mode 100644 (file)
index 0000000..ac20310
--- /dev/null
@@ -0,0 +1,707 @@
+/*
+ * Rockchip USB2.0 PHY with Innosilicon IP block driver
+ *
+ * Copyright (C) 2016 Fuzhou Rockchip Electronics Co., Ltd
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/gpio/consumer.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define BIT_WRITEABLE_SHIFT    16
+#define SCHEDULE_DELAY (60 * HZ)
+
+enum rockchip_usb2phy_port_id {
+       USB2PHY_PORT_OTG,
+       USB2PHY_PORT_HOST,
+       USB2PHY_NUM_PORTS,
+};
+
+enum rockchip_usb2phy_host_state {
+       PHY_STATE_HS_ONLINE     = 0,
+       PHY_STATE_DISCONNECT    = 1,
+       PHY_STATE_CONNECT       = 2,
+       PHY_STATE_FS_LS_ONLINE  = 4,
+};
+
+struct usb2phy_reg {
+       unsigned int    offset;
+       unsigned int    bitend;
+       unsigned int    bitstart;
+       unsigned int    disable;
+       unsigned int    enable;
+};
+
+/**
+ * struct rockchip_usb2phy_port_cfg: usb-phy port configuration.
+ * @phy_sus: phy suspend register.
+ * @ls_det_en: linestate detection enable register.
+ * @ls_det_st: linestate detection state register.
+ * @ls_det_clr: linestate detection clear register.
+ * @utmi_ls: utmi linestate state register.
+ * @utmi_hstdet: utmi host disconnect register.
+ */
+struct rockchip_usb2phy_port_cfg {
+       struct usb2phy_reg      phy_sus;
+       struct usb2phy_reg      ls_det_en;
+       struct usb2phy_reg      ls_det_st;
+       struct usb2phy_reg      ls_det_clr;
+       struct usb2phy_reg      utmi_ls;
+       struct usb2phy_reg      utmi_hstdet;
+};
+
+/**
+ * struct rockchip_usb2phy_cfg: usb-phy configuration.
+ * @reg: the address offset of grf for usb-phy config.
+ * @num_ports: specify how many ports that the phy has.
+ * @clkout_ctl: keep on/turn off output clk of phy.
+ */
+struct rockchip_usb2phy_cfg {
+       unsigned int    reg;
+       unsigned int    num_ports;
+       struct usb2phy_reg      clkout_ctl;
+       const struct rockchip_usb2phy_port_cfg  port_cfgs[USB2PHY_NUM_PORTS];
+};
+
+/**
+ * struct rockchip_usb2phy_port: usb-phy port data.
+ * @port_id: flag for otg port or host port.
+ * @suspended: phy suspended flag.
+ * @ls_irq: IRQ number assigned for linestate detection.
+ * @mutex: for register updating in sm_work.
+ * @sm_work: OTG state machine work.
+ * @phy_cfg: port register configuration, assigned by driver data.
+ */
+struct rockchip_usb2phy_port {
+       struct phy      *phy;
+       unsigned int    port_id;
+       bool            suspended;
+       int             ls_irq;
+       struct mutex    mutex;
+       struct          delayed_work sm_work;
+       const struct    rockchip_usb2phy_port_cfg *port_cfg;
+};
+
+/**
+ * struct rockchip_usb2phy: usb2.0 phy driver data.
+ * @grf: General Register Files regmap.
+ * @clk: clock struct of phy input clk.
+ * @clk480m: clock struct of phy output clk.
+ * @clk_hw: clock struct of phy output clk management.
+ * @phy_cfg: phy register configuration, assigned by driver data.
+ * @ports: phy port instance.
+ */
+struct rockchip_usb2phy {
+       struct device   *dev;
+       struct regmap   *grf;
+       struct clk      *clk;
+       struct clk      *clk480m;
+       struct clk_hw   clk480m_hw;
+       const struct rockchip_usb2phy_cfg       *phy_cfg;
+       struct rockchip_usb2phy_port    ports[USB2PHY_NUM_PORTS];
+};
+
+static inline int property_enable(struct rockchip_usb2phy *rphy,
+                                 const struct usb2phy_reg *reg, bool en)
+{
+       unsigned int val, mask, tmp;
+
+       tmp = en ? reg->enable : reg->disable;
+       mask = GENMASK(reg->bitend, reg->bitstart);
+       val = (tmp << reg->bitstart) | (mask << BIT_WRITEABLE_SHIFT);
+
+       return regmap_write(rphy->grf, reg->offset, val);
+}
+
+static inline bool property_enabled(struct rockchip_usb2phy *rphy,
+                                   const struct usb2phy_reg *reg)
+{
+       int ret;
+       unsigned int tmp, orig;
+       unsigned int mask = GENMASK(reg->bitend, reg->bitstart);
+
+       ret = regmap_read(rphy->grf, reg->offset, &orig);
+       if (ret)
+               return false;
+
+       tmp = (orig & mask) >> reg->bitstart;
+       return tmp == reg->enable;
+}
+
+static int rockchip_usb2phy_clk480m_enable(struct clk_hw *hw)
+{
+       struct rockchip_usb2phy *rphy =
+               container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+       int ret;
+
+       /* turn on 480m clk output if it is off */
+       if (!property_enabled(rphy, &rphy->phy_cfg->clkout_ctl)) {
+               ret = property_enable(rphy, &rphy->phy_cfg->clkout_ctl, true);
+               if (ret)
+                       return ret;
+
+               /* waitting for the clk become stable */
+               mdelay(1);
+       }
+
+       return 0;
+}
+
+static void rockchip_usb2phy_clk480m_disable(struct clk_hw *hw)
+{
+       struct rockchip_usb2phy *rphy =
+               container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+       /* turn off 480m clk output */
+       property_enable(rphy, &rphy->phy_cfg->clkout_ctl, false);
+}
+
+static int rockchip_usb2phy_clk480m_enabled(struct clk_hw *hw)
+{
+       struct rockchip_usb2phy *rphy =
+               container_of(hw, struct rockchip_usb2phy, clk480m_hw);
+
+       return property_enabled(rphy, &rphy->phy_cfg->clkout_ctl);
+}
+
+static unsigned long
+rockchip_usb2phy_clk480m_recalc_rate(struct clk_hw *hw,
+                                    unsigned long parent_rate)
+{
+       return 480000000;
+}
+
+static const struct clk_ops rockchip_usb2phy_clkout_ops = {
+       .enable = rockchip_usb2phy_clk480m_enable,
+       .disable = rockchip_usb2phy_clk480m_disable,
+       .is_enabled = rockchip_usb2phy_clk480m_enabled,
+       .recalc_rate = rockchip_usb2phy_clk480m_recalc_rate,
+};
+
+static void rockchip_usb2phy_clk480m_unregister(void *data)
+{
+       struct rockchip_usb2phy *rphy = data;
+
+       of_clk_del_provider(rphy->dev->of_node);
+       clk_unregister(rphy->clk480m);
+}
+
+static int
+rockchip_usb2phy_clk480m_register(struct rockchip_usb2phy *rphy)
+{
+       struct device_node *node = rphy->dev->of_node;
+       struct clk_init_data init;
+       const char *clk_name;
+       int ret;
+
+       init.flags = 0;
+       init.name = "clk_usbphy_480m";
+       init.ops = &rockchip_usb2phy_clkout_ops;
+
+       /* optional override of the clockname */
+       of_property_read_string(node, "clock-output-names", &init.name);
+
+       if (rphy->clk) {
+               clk_name = __clk_get_name(rphy->clk);
+               init.parent_names = &clk_name;
+               init.num_parents = 1;
+       } else {
+               init.parent_names = NULL;
+               init.num_parents = 0;
+       }
+
+       rphy->clk480m_hw.init = &init;
+
+       /* register the clock */
+       rphy->clk480m = clk_register(rphy->dev, &rphy->clk480m_hw);
+       if (IS_ERR(rphy->clk480m)) {
+               ret = PTR_ERR(rphy->clk480m);
+               goto err_ret;
+       }
+
+       ret = of_clk_add_provider(node, of_clk_src_simple_get, rphy->clk480m);
+       if (ret < 0)
+               goto err_clk_provider;
+
+       ret = devm_add_action(rphy->dev, rockchip_usb2phy_clk480m_unregister,
+                             rphy);
+       if (ret < 0)
+               goto err_unreg_action;
+
+       return 0;
+
+err_unreg_action:
+       of_clk_del_provider(node);
+err_clk_provider:
+       clk_unregister(rphy->clk480m);
+err_ret:
+       return ret;
+}
+
+static int rockchip_usb2phy_init(struct phy *phy)
+{
+       struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+       struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+       int ret;
+
+       if (rport->port_id == USB2PHY_PORT_HOST) {
+               /* clear linestate and enable linestate detect irq */
+               mutex_lock(&rport->mutex);
+
+               ret = property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+               if (ret) {
+                       mutex_unlock(&rport->mutex);
+                       return ret;
+               }
+
+               ret = property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+               if (ret) {
+                       mutex_unlock(&rport->mutex);
+                       return ret;
+               }
+
+               mutex_unlock(&rport->mutex);
+               schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+       }
+
+       return 0;
+}
+
+static int rockchip_usb2phy_power_on(struct phy *phy)
+{
+       struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+       struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+       int ret;
+
+       dev_dbg(&rport->phy->dev, "port power on\n");
+
+       if (!rport->suspended)
+               return 0;
+
+       ret = clk_prepare_enable(rphy->clk480m);
+       if (ret)
+               return ret;
+
+       ret = property_enable(rphy, &rport->port_cfg->phy_sus, false);
+       if (ret)
+               return ret;
+
+       rport->suspended = false;
+       return 0;
+}
+
+static int rockchip_usb2phy_power_off(struct phy *phy)
+{
+       struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+       struct rockchip_usb2phy *rphy = dev_get_drvdata(phy->dev.parent);
+       int ret;
+
+       dev_dbg(&rport->phy->dev, "port power off\n");
+
+       if (rport->suspended)
+               return 0;
+
+       ret = property_enable(rphy, &rport->port_cfg->phy_sus, true);
+       if (ret)
+               return ret;
+
+       rport->suspended = true;
+       clk_disable_unprepare(rphy->clk480m);
+
+       return 0;
+}
+
+static int rockchip_usb2phy_exit(struct phy *phy)
+{
+       struct rockchip_usb2phy_port *rport = phy_get_drvdata(phy);
+
+       if (rport->port_id == USB2PHY_PORT_HOST)
+               cancel_delayed_work_sync(&rport->sm_work);
+
+       return 0;
+}
+
+static const struct phy_ops rockchip_usb2phy_ops = {
+       .init           = rockchip_usb2phy_init,
+       .exit           = rockchip_usb2phy_exit,
+       .power_on       = rockchip_usb2phy_power_on,
+       .power_off      = rockchip_usb2phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+/*
+ * The function manage host-phy port state and suspend/resume phy port
+ * to save power.
+ *
+ * we rely on utmi_linestate and utmi_hostdisconnect to identify whether
+ * devices is disconnect or not. Besides, we do not need care it is FS/LS
+ * disconnected or HS disconnected, actually, we just only need get the
+ * device is disconnected at last through rearm the delayed work,
+ * to suspend the phy port in _PHY_STATE_DISCONNECT_ case.
+ *
+ * NOTE: It may invoke *phy_powr_off or *phy_power_on which will invoke
+ * some clk related APIs, so do not invoke it from interrupt context directly.
+ */
+static void rockchip_usb2phy_sm_work(struct work_struct *work)
+{
+       struct rockchip_usb2phy_port *rport =
+               container_of(work, struct rockchip_usb2phy_port, sm_work.work);
+       struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+       unsigned int sh = rport->port_cfg->utmi_hstdet.bitend -
+                         rport->port_cfg->utmi_hstdet.bitstart + 1;
+       unsigned int ul, uhd, state;
+       unsigned int ul_mask, uhd_mask;
+       int ret;
+
+       mutex_lock(&rport->mutex);
+
+       ret = regmap_read(rphy->grf, rport->port_cfg->utmi_ls.offset, &ul);
+       if (ret < 0)
+               goto next_schedule;
+
+       ret = regmap_read(rphy->grf, rport->port_cfg->utmi_hstdet.offset,
+                         &uhd);
+       if (ret < 0)
+               goto next_schedule;
+
+       uhd_mask = GENMASK(rport->port_cfg->utmi_hstdet.bitend,
+                          rport->port_cfg->utmi_hstdet.bitstart);
+       ul_mask = GENMASK(rport->port_cfg->utmi_ls.bitend,
+                         rport->port_cfg->utmi_ls.bitstart);
+
+       /* stitch on utmi_ls and utmi_hstdet as phy state */
+       state = ((uhd & uhd_mask) >> rport->port_cfg->utmi_hstdet.bitstart) |
+               (((ul & ul_mask) >> rport->port_cfg->utmi_ls.bitstart) << sh);
+
+       switch (state) {
+       case PHY_STATE_HS_ONLINE:
+               dev_dbg(&rport->phy->dev, "HS online\n");
+               break;
+       case PHY_STATE_FS_LS_ONLINE:
+               /*
+                * For FS/LS device, the online state share with connect state
+                * from utmi_ls and utmi_hstdet register, so we distinguish
+                * them via suspended flag.
+                *
+                * Plus, there are two cases, one is D- Line pull-up, and D+
+                * line pull-down, the state is 4; another is D+ line pull-up,
+                * and D- line pull-down, the state is 2.
+                */
+               if (!rport->suspended) {
+                       /* D- line pull-up, D+ line pull-down */
+                       dev_dbg(&rport->phy->dev, "FS/LS online\n");
+                       break;
+               }
+               /* fall through */
+       case PHY_STATE_CONNECT:
+               if (rport->suspended) {
+                       dev_dbg(&rport->phy->dev, "Connected\n");
+                       rockchip_usb2phy_power_on(rport->phy);
+                       rport->suspended = false;
+               } else {
+                       /* D+ line pull-up, D- line pull-down */
+                       dev_dbg(&rport->phy->dev, "FS/LS online\n");
+               }
+               break;
+       case PHY_STATE_DISCONNECT:
+               if (!rport->suspended) {
+                       dev_dbg(&rport->phy->dev, "Disconnected\n");
+                       rockchip_usb2phy_power_off(rport->phy);
+                       rport->suspended = true;
+               }
+
+               /*
+                * activate the linestate detection to get the next device
+                * plug-in irq.
+                */
+               property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+               property_enable(rphy, &rport->port_cfg->ls_det_en, true);
+
+               /*
+                * we don't need to rearm the delayed work when the phy port
+                * is suspended.
+                */
+               mutex_unlock(&rport->mutex);
+               return;
+       default:
+               dev_dbg(&rport->phy->dev, "unknown phy state\n");
+               break;
+       }
+
+next_schedule:
+       mutex_unlock(&rport->mutex);
+       schedule_delayed_work(&rport->sm_work, SCHEDULE_DELAY);
+}
+
+static irqreturn_t rockchip_usb2phy_linestate_irq(int irq, void *data)
+{
+       struct rockchip_usb2phy_port *rport = data;
+       struct rockchip_usb2phy *rphy = dev_get_drvdata(rport->phy->dev.parent);
+
+       if (!property_enabled(rphy, &rport->port_cfg->ls_det_st))
+               return IRQ_NONE;
+
+       mutex_lock(&rport->mutex);
+
+       /* disable linestate detect irq and clear its status */
+       property_enable(rphy, &rport->port_cfg->ls_det_en, false);
+       property_enable(rphy, &rport->port_cfg->ls_det_clr, true);
+
+       mutex_unlock(&rport->mutex);
+
+       /*
+        * In this case for host phy port, a new device is plugged in,
+        * meanwhile, if the phy port is suspended, we need rearm the work to
+        * resume it and mange its states; otherwise, we do nothing about that.
+        */
+       if (rport->suspended && rport->port_id == USB2PHY_PORT_HOST)
+               rockchip_usb2phy_sm_work(&rport->sm_work.work);
+
+       return IRQ_HANDLED;
+}
+
+static int rockchip_usb2phy_host_port_init(struct rockchip_usb2phy *rphy,
+                                          struct rockchip_usb2phy_port *rport,
+                                          struct device_node *child_np)
+{
+       int ret;
+
+       rport->port_id = USB2PHY_PORT_HOST;
+       rport->port_cfg = &rphy->phy_cfg->port_cfgs[USB2PHY_PORT_HOST];
+       rport->suspended = true;
+
+       mutex_init(&rport->mutex);
+       INIT_DELAYED_WORK(&rport->sm_work, rockchip_usb2phy_sm_work);
+
+       rport->ls_irq = of_irq_get_byname(child_np, "linestate");
+       if (rport->ls_irq < 0) {
+               dev_err(rphy->dev, "no linestate irq provided\n");
+               return rport->ls_irq;
+       }
+
+       ret = devm_request_threaded_irq(rphy->dev, rport->ls_irq, NULL,
+                                       rockchip_usb2phy_linestate_irq,
+                                       IRQF_ONESHOT,
+                                       "rockchip_usb2phy", rport);
+       if (ret) {
+               dev_err(rphy->dev, "failed to request irq handle\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rockchip_usb2phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *child_np;
+       struct phy_provider *provider;
+       struct rockchip_usb2phy *rphy;
+       const struct rockchip_usb2phy_cfg *phy_cfgs;
+       const struct of_device_id *match;
+       unsigned int reg;
+       int index, ret;
+
+       rphy = devm_kzalloc(dev, sizeof(*rphy), GFP_KERNEL);
+       if (!rphy)
+               return -ENOMEM;
+
+       match = of_match_device(dev->driver->of_match_table, dev);
+       if (!match || !match->data) {
+               dev_err(dev, "phy configs are not assigned!\n");
+               return -EINVAL;
+       }
+
+       if (!dev->parent || !dev->parent->of_node)
+               return -EINVAL;
+
+       rphy->grf = syscon_node_to_regmap(dev->parent->of_node);
+       if (IS_ERR(rphy->grf))
+               return PTR_ERR(rphy->grf);
+
+       if (of_property_read_u32(np, "reg", &reg)) {
+               dev_err(dev, "the reg property is not assigned in %s node\n",
+                       np->name);
+               return -EINVAL;
+       }
+
+       rphy->dev = dev;
+       phy_cfgs = match->data;
+       platform_set_drvdata(pdev, rphy);
+
+       /* find out a proper config which can be matched with dt. */
+       index = 0;
+       while (phy_cfgs[index].reg) {
+               if (phy_cfgs[index].reg == reg) {
+                       rphy->phy_cfg = &phy_cfgs[index];
+                       break;
+               }
+
+               ++index;
+       }
+
+       if (!rphy->phy_cfg) {
+               dev_err(dev, "no phy-config can be matched with %s node\n",
+                       np->name);
+               return -EINVAL;
+       }
+
+       rphy->clk = of_clk_get_by_name(np, "phyclk");
+       if (!IS_ERR(rphy->clk)) {
+               clk_prepare_enable(rphy->clk);
+       } else {
+               dev_info(&pdev->dev, "no phyclk specified\n");
+               rphy->clk = NULL;
+       }
+
+       ret = rockchip_usb2phy_clk480m_register(rphy);
+       if (ret) {
+               dev_err(dev, "failed to register 480m output clock\n");
+               goto disable_clks;
+       }
+
+       index = 0;
+       for_each_available_child_of_node(np, child_np) {
+               struct rockchip_usb2phy_port *rport = &rphy->ports[index];
+               struct phy *phy;
+
+               /*
+                * This driver aim to support both otg-port and host-port,
+                * but unfortunately, the otg part is not ready in current,
+                * so this comments and below codes are interim, which should
+                * be changed after otg-port is supplied soon.
+                */
+               if (of_node_cmp(child_np->name, "host-port"))
+                       goto next_child;
+
+               phy = devm_phy_create(dev, child_np, &rockchip_usb2phy_ops);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create phy\n");
+                       ret = PTR_ERR(phy);
+                       goto put_child;
+               }
+
+               rport->phy = phy;
+               phy_set_drvdata(rport->phy, rport);
+
+               ret = rockchip_usb2phy_host_port_init(rphy, rport, child_np);
+               if (ret)
+                       goto put_child;
+
+next_child:
+               /* to prevent out of boundary */
+               if (++index >= rphy->phy_cfg->num_ports)
+                       break;
+       }
+
+       provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       return PTR_ERR_OR_ZERO(provider);
+
+put_child:
+       of_node_put(child_np);
+disable_clks:
+       if (rphy->clk) {
+               clk_disable_unprepare(rphy->clk);
+               clk_put(rphy->clk);
+       }
+       return ret;
+}
+
+static const struct rockchip_usb2phy_cfg rk3366_phy_cfgs[] = {
+       {
+               .reg = 0x700,
+               .num_ports      = 2,
+               .clkout_ctl     = { 0x0724, 15, 15, 1, 0 },
+               .port_cfgs      = {
+                       [USB2PHY_PORT_HOST] = {
+                               .phy_sus        = { 0x0728, 15, 0, 0, 0x1d1 },
+                               .ls_det_en      = { 0x0680, 4, 4, 0, 1 },
+                               .ls_det_st      = { 0x0690, 4, 4, 0, 1 },
+                               .ls_det_clr     = { 0x06a0, 4, 4, 0, 1 },
+                               .utmi_ls        = { 0x049c, 14, 13, 0, 1 },
+                               .utmi_hstdet    = { 0x049c, 12, 12, 0, 1 }
+                       }
+               },
+       },
+       { /* sentinel */ }
+};
+
+static const struct rockchip_usb2phy_cfg rk3399_phy_cfgs[] = {
+       {
+               .reg = 0xe450,
+               .num_ports      = 2,
+               .clkout_ctl     = { 0xe450, 4, 4, 1, 0 },
+               .port_cfgs      = {
+                       [USB2PHY_PORT_HOST] = {
+                               .phy_sus        = { 0xe458, 1, 0, 0x2, 0x1 },
+                               .ls_det_en      = { 0xe3c0, 6, 6, 0, 1 },
+                               .ls_det_st      = { 0xe3e0, 6, 6, 0, 1 },
+                               .ls_det_clr     = { 0xe3d0, 6, 6, 0, 1 },
+                               .utmi_ls        = { 0xe2ac, 22, 21, 0, 1 },
+                               .utmi_hstdet    = { 0xe2ac, 23, 23, 0, 1 }
+                       }
+               },
+       },
+       {
+               .reg = 0xe460,
+               .num_ports      = 2,
+               .clkout_ctl     = { 0xe460, 4, 4, 1, 0 },
+               .port_cfgs      = {
+                       [USB2PHY_PORT_HOST] = {
+                               .phy_sus        = { 0xe468, 1, 0, 0x2, 0x1 },
+                               .ls_det_en      = { 0xe3c0, 11, 11, 0, 1 },
+                               .ls_det_st      = { 0xe3e0, 11, 11, 0, 1 },
+                               .ls_det_clr     = { 0xe3d0, 11, 11, 0, 1 },
+                               .utmi_ls        = { 0xe2ac, 26, 25, 0, 1 },
+                               .utmi_hstdet    = { 0xe2ac, 27, 27, 0, 1 }
+                       }
+               },
+       },
+       { /* sentinel */ }
+};
+
+static const struct of_device_id rockchip_usb2phy_dt_match[] = {
+       { .compatible = "rockchip,rk3366-usb2phy", .data = &rk3366_phy_cfgs },
+       { .compatible = "rockchip,rk3399-usb2phy", .data = &rk3399_phy_cfgs },
+       {}
+};
+MODULE_DEVICE_TABLE(of, rockchip_usb2phy_dt_match);
+
+static struct platform_driver rockchip_usb2phy_driver = {
+       .probe          = rockchip_usb2phy_probe,
+       .driver         = {
+               .name   = "rockchip-usb2phy",
+               .of_match_table = rockchip_usb2phy_dt_match,
+       },
+};
+module_platform_driver(rockchip_usb2phy_driver);
+
+MODULE_AUTHOR("Frank Wang <frank.wang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rockchip-pcie.c b/drivers/phy/phy-rockchip-pcie.c
new file mode 100644 (file)
index 0000000..a2b4c6b
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ * Rockchip PCIe PHY driver
+ *
+ * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
+ * Copyright (C) 2016 ROCKCHIP, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(x + 16) set to 1 the BIT(x) can be written.
+ */
+#define HIWORD_UPDATE(val, mask, shift) \
+               ((val) << (shift) | (mask) << ((shift) + 16))
+
+#define PHY_MAX_LANE_NUM      4
+#define PHY_CFG_DATA_SHIFT    7
+#define PHY_CFG_ADDR_SHIFT    1
+#define PHY_CFG_DATA_MASK     0xf
+#define PHY_CFG_ADDR_MASK     0x3f
+#define PHY_CFG_RD_MASK       0x3ff
+#define PHY_CFG_WR_ENABLE     1
+#define PHY_CFG_WR_DISABLE    1
+#define PHY_CFG_WR_SHIFT      0
+#define PHY_CFG_WR_MASK       1
+#define PHY_CFG_PLL_LOCK      0x10
+#define PHY_CFG_CLK_TEST      0x10
+#define PHY_CFG_CLK_SCC       0x12
+#define PHY_CFG_SEPE_RATE     BIT(3)
+#define PHY_CFG_PLL_100M      BIT(3)
+#define PHY_PLL_LOCKED        BIT(9)
+#define PHY_PLL_OUTPUT        BIT(10)
+#define PHY_LANE_A_STATUS     0x30
+#define PHY_LANE_B_STATUS     0x31
+#define PHY_LANE_C_STATUS     0x32
+#define PHY_LANE_D_STATUS     0x33
+#define PHY_LANE_RX_DET_SHIFT 11
+#define PHY_LANE_RX_DET_TH    0x1
+#define PHY_LANE_IDLE_OFF     0x1
+#define PHY_LANE_IDLE_MASK    0x1
+#define PHY_LANE_IDLE_A_SHIFT 3
+#define PHY_LANE_IDLE_B_SHIFT 4
+#define PHY_LANE_IDLE_C_SHIFT 5
+#define PHY_LANE_IDLE_D_SHIFT 6
+
+struct rockchip_pcie_data {
+       unsigned int pcie_conf;
+       unsigned int pcie_status;
+       unsigned int pcie_laneoff;
+};
+
+struct rockchip_pcie_phy {
+       struct rockchip_pcie_data *phy_data;
+       struct regmap *reg_base;
+       struct reset_control *phy_rst;
+       struct clk *clk_pciephy_ref;
+};
+
+static inline void phy_wr_cfg(struct rockchip_pcie_phy *rk_phy,
+                             u32 addr, u32 data)
+{
+       regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+                    HIWORD_UPDATE(data,
+                                  PHY_CFG_DATA_MASK,
+                                  PHY_CFG_DATA_SHIFT) |
+                    HIWORD_UPDATE(addr,
+                                  PHY_CFG_ADDR_MASK,
+                                  PHY_CFG_ADDR_SHIFT));
+       udelay(1);
+       regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+                    HIWORD_UPDATE(PHY_CFG_WR_ENABLE,
+                                  PHY_CFG_WR_MASK,
+                                  PHY_CFG_WR_SHIFT));
+       udelay(1);
+       regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+                    HIWORD_UPDATE(PHY_CFG_WR_DISABLE,
+                                  PHY_CFG_WR_MASK,
+                                  PHY_CFG_WR_SHIFT));
+}
+
+static inline u32 phy_rd_cfg(struct rockchip_pcie_phy *rk_phy,
+                            u32 addr)
+{
+       u32 val;
+
+       regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+                    HIWORD_UPDATE(addr,
+                                  PHY_CFG_RD_MASK,
+                                  PHY_CFG_ADDR_SHIFT));
+       regmap_read(rk_phy->reg_base,
+                   rk_phy->phy_data->pcie_status,
+                   &val);
+       return val;
+}
+
+static int rockchip_pcie_phy_power_off(struct phy *phy)
+{
+       struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+       int err = 0;
+
+       err = reset_control_assert(rk_phy->phy_rst);
+       if (err) {
+               dev_err(&phy->dev, "assert phy_rst err %d\n", err);
+               return err;
+       }
+
+       return 0;
+}
+
+static int rockchip_pcie_phy_power_on(struct phy *phy)
+{
+       struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+       int err = 0;
+       u32 status;
+       unsigned long timeout;
+
+       err = reset_control_deassert(rk_phy->phy_rst);
+       if (err) {
+               dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
+               return err;
+       }
+
+       regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+                    HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
+                                  PHY_CFG_ADDR_MASK,
+                                  PHY_CFG_ADDR_SHIFT));
+
+       /*
+        * No documented timeout value for phy operation below,
+        * so we make it large enough here. And we use loop-break
+        * method which should not be harmful.
+        */
+       timeout = jiffies + msecs_to_jiffies(1000);
+
+       err = -EINVAL;
+       while (time_before(jiffies, timeout)) {
+               regmap_read(rk_phy->reg_base,
+                           rk_phy->phy_data->pcie_status,
+                           &status);
+               if (status & PHY_PLL_LOCKED) {
+                       dev_dbg(&phy->dev, "pll locked!\n");
+                       err = 0;
+                       break;
+               }
+               msleep(20);
+       }
+
+       if (err) {
+               dev_err(&phy->dev, "pll lock timeout!\n");
+               goto err_pll_lock;
+       }
+
+       phy_wr_cfg(rk_phy, PHY_CFG_CLK_TEST, PHY_CFG_SEPE_RATE);
+       phy_wr_cfg(rk_phy, PHY_CFG_CLK_SCC, PHY_CFG_PLL_100M);
+
+       err = -ETIMEDOUT;
+       while (time_before(jiffies, timeout)) {
+               regmap_read(rk_phy->reg_base,
+                           rk_phy->phy_data->pcie_status,
+                           &status);
+               if (!(status & PHY_PLL_OUTPUT)) {
+                       dev_dbg(&phy->dev, "pll output enable done!\n");
+                       err = 0;
+                       break;
+               }
+               msleep(20);
+       }
+
+       if (err) {
+               dev_err(&phy->dev, "pll output enable timeout!\n");
+               goto err_pll_lock;
+       }
+
+       regmap_write(rk_phy->reg_base, rk_phy->phy_data->pcie_conf,
+                    HIWORD_UPDATE(PHY_CFG_PLL_LOCK,
+                                  PHY_CFG_ADDR_MASK,
+                                  PHY_CFG_ADDR_SHIFT));
+       err = -EINVAL;
+       while (time_before(jiffies, timeout)) {
+               regmap_read(rk_phy->reg_base,
+                           rk_phy->phy_data->pcie_status,
+                           &status);
+               if (status & PHY_PLL_LOCKED) {
+                       dev_dbg(&phy->dev, "pll relocked!\n");
+                       err = 0;
+                       break;
+               }
+               msleep(20);
+       }
+
+       if (err) {
+               dev_err(&phy->dev, "pll relock timeout!\n");
+               goto err_pll_lock;
+       }
+
+       return 0;
+
+err_pll_lock:
+       reset_control_assert(rk_phy->phy_rst);
+       return err;
+}
+
+static int rockchip_pcie_phy_init(struct phy *phy)
+{
+       struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+       int err = 0;
+
+       err = clk_prepare_enable(rk_phy->clk_pciephy_ref);
+       if (err) {
+               dev_err(&phy->dev, "Fail to enable pcie ref clock.\n");
+               goto err_refclk;
+       }
+
+       err = reset_control_assert(rk_phy->phy_rst);
+       if (err) {
+               dev_err(&phy->dev, "assert phy_rst err %d\n", err);
+               goto err_reset;
+       }
+
+       return err;
+
+err_reset:
+       clk_disable_unprepare(rk_phy->clk_pciephy_ref);
+err_refclk:
+       return err;
+}
+
+static int rockchip_pcie_phy_exit(struct phy *phy)
+{
+       struct rockchip_pcie_phy *rk_phy = phy_get_drvdata(phy);
+       int err = 0;
+
+       clk_disable_unprepare(rk_phy->clk_pciephy_ref);
+
+       err = reset_control_deassert(rk_phy->phy_rst);
+       if (err) {
+               dev_err(&phy->dev, "deassert phy_rst err %d\n", err);
+               goto err_reset;
+       }
+
+       return err;
+
+err_reset:
+       clk_prepare_enable(rk_phy->clk_pciephy_ref);
+       return err;
+}
+
+static const struct phy_ops ops = {
+       .init           = rockchip_pcie_phy_init,
+       .exit           = rockchip_pcie_phy_exit,
+       .power_on       = rockchip_pcie_phy_power_on,
+       .power_off      = rockchip_pcie_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static const struct rockchip_pcie_data rk3399_pcie_data = {
+       .pcie_conf = 0xe220,
+       .pcie_status = 0xe2a4,
+       .pcie_laneoff = 0xe214,
+};
+
+static const struct of_device_id rockchip_pcie_phy_dt_ids[] = {
+       {
+               .compatible = "rockchip,rk3399-pcie-phy",
+               .data = &rk3399_pcie_data,
+       },
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_pcie_phy_dt_ids);
+
+static int rockchip_pcie_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rockchip_pcie_phy *rk_phy;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       struct regmap *grf;
+       const struct of_device_id *of_id;
+
+       grf = syscon_node_to_regmap(dev->parent->of_node);
+       if (IS_ERR(grf)) {
+               dev_err(dev, "Cannot find GRF syscon\n");
+               return PTR_ERR(grf);
+       }
+
+       rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
+       if (!rk_phy)
+               return -ENOMEM;
+
+       of_id = of_match_device(rockchip_pcie_phy_dt_ids, &pdev->dev);
+       if (!of_id)
+               return -EINVAL;
+
+       rk_phy->phy_data = (struct rockchip_pcie_data *)of_id->data;
+       rk_phy->reg_base = grf;
+
+       rk_phy->phy_rst = devm_reset_control_get(dev, "phy");
+       if (IS_ERR(rk_phy->phy_rst)) {
+               if (PTR_ERR(rk_phy->phy_rst) != -EPROBE_DEFER)
+                       dev_err(dev,
+                               "missing phy property for reset controller\n");
+               return PTR_ERR(rk_phy->phy_rst);
+       }
+
+       rk_phy->clk_pciephy_ref = devm_clk_get(dev, "refclk");
+       if (IS_ERR(rk_phy->clk_pciephy_ref)) {
+               dev_err(dev, "refclk not found.\n");
+               return PTR_ERR(rk_phy->clk_pciephy_ref);
+       }
+
+       generic_phy = devm_phy_create(dev, dev->of_node, &ops);
+       if (IS_ERR(generic_phy)) {
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(generic_phy);
+       }
+
+       phy_set_drvdata(generic_phy, rk_phy);
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver rockchip_pcie_driver = {
+       .probe          = rockchip_pcie_phy_probe,
+       .driver         = {
+               .name   = "rockchip-pcie-phy",
+               .of_match_table = rockchip_pcie_phy_dt_ids,
+       },
+};
+
+module_platform_driver(rockchip_pcie_driver);
+
+MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip PCIe PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rockchip-typec.c b/drivers/phy/phy-rockchip-typec.c
new file mode 100644 (file)
index 0000000..7cfb0f8
--- /dev/null
@@ -0,0 +1,1023 @@
+/*
+ * Copyright (C) Fuzhou Rockchip Electronics Co.Ltd
+ * Author: Chris Zhong <zyw@rock-chips.com>
+ *         Kever Yang <kever.yang@rock-chips.com>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * The ROCKCHIP Type-C PHY has two PLL clocks. The first PLL clock
+ * is used for USB3, the second PLL clock is used for DP. This Type-C PHY has
+ * 3 working modes: USB3 only mode, DP only mode, and USB3+DP mode.
+ * At USB3 only mode, both PLL clocks need to be initialized, this allows the
+ * PHY to switch mode between USB3 and USB3+DP, without disconnecting the USB
+ * device.
+ * In The DP only mode, only the DP PLL needs to be powered on, and the 4 lanes
+ * are all used for DP.
+ *
+ * This driver gets extcon cable state and property, then decides which mode to
+ * select:
+ *
+ * 1. USB3 only mode:
+ *    EXTCON_USB or EXTCON_USB_HOST state is true, and
+ *    EXTCON_PROP_USB_SS property is true.
+ *    EXTCON_DISP_DP state is false.
+ *
+ * 2. DP only mode:
+ *    EXTCON_DISP_DP state is true, and
+ *    EXTCON_PROP_USB_SS property is false.
+ *    If EXTCON_USB_HOST state is true, it is DP + USB2 mode, since the USB2 phy
+ *    is a separate phy, so this case is still DP only mode.
+ *
+ * 3. USB3+DP mode:
+ *    EXTCON_USB_HOST and EXTCON_DISP_DP are both true, and
+ *    EXTCON_PROP_USB_SS property is true.
+ *
+ * This Type-C PHY driver supports normal and flip orientation. The orientation
+ * is reported by the EXTCON_PROP_USB_TYPEC_POLARITY property: true is flip
+ * orientation, false is normal orientation.
+ *
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/delay.h>
+#include <linux/extcon.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+#define CMN_SSM_BANDGAP                        (0x21 << 2)
+#define CMN_SSM_BIAS                   (0x22 << 2)
+#define CMN_PLLSM0_PLLEN               (0x29 << 2)
+#define CMN_PLLSM0_PLLPRE              (0x2a << 2)
+#define CMN_PLLSM0_PLLVREF             (0x2b << 2)
+#define CMN_PLLSM0_PLLLOCK             (0x2c << 2)
+#define CMN_PLLSM1_PLLEN               (0x31 << 2)
+#define CMN_PLLSM1_PLLPRE              (0x32 << 2)
+#define CMN_PLLSM1_PLLVREF             (0x33 << 2)
+#define CMN_PLLSM1_PLLLOCK             (0x34 << 2)
+#define CMN_PLLSM1_USER_DEF_CTRL       (0x37 << 2)
+#define CMN_ICAL_OVRD                  (0xc1 << 2)
+#define CMN_PLL0_VCOCAL_OVRD           (0x83 << 2)
+#define CMN_PLL0_VCOCAL_INIT           (0x84 << 2)
+#define CMN_PLL0_VCOCAL_ITER           (0x85 << 2)
+#define CMN_PLL0_LOCK_REFCNT_START     (0x90 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_START     (0x92 << 2)
+#define CMN_PLL0_LOCK_PLLCNT_THR       (0x93 << 2)
+#define CMN_PLL0_INTDIV                        (0x94 << 2)
+#define CMN_PLL0_FRACDIV               (0x95 << 2)
+#define CMN_PLL0_HIGH_THR              (0x96 << 2)
+#define CMN_PLL0_DSM_DIAG              (0x97 << 2)
+#define CMN_PLL0_SS_CTRL1              (0x98 << 2)
+#define CMN_PLL0_SS_CTRL2              (0x99 << 2)
+#define CMN_PLL1_VCOCAL_START          (0xa1 << 2)
+#define CMN_PLL1_VCOCAL_OVRD           (0xa3 << 2)
+#define CMN_PLL1_VCOCAL_INIT           (0xa4 << 2)
+#define CMN_PLL1_VCOCAL_ITER           (0xa5 << 2)
+#define CMN_PLL1_LOCK_REFCNT_START     (0xb0 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_START     (0xb2 << 2)
+#define CMN_PLL1_LOCK_PLLCNT_THR       (0xb3 << 2)
+#define CMN_PLL1_INTDIV                        (0xb4 << 2)
+#define CMN_PLL1_FRACDIV               (0xb5 << 2)
+#define CMN_PLL1_HIGH_THR              (0xb6 << 2)
+#define CMN_PLL1_DSM_DIAG              (0xb7 << 2)
+#define CMN_PLL1_SS_CTRL1              (0xb8 << 2)
+#define CMN_PLL1_SS_CTRL2              (0xb9 << 2)
+#define CMN_RXCAL_OVRD                 (0xd1 << 2)
+#define CMN_TXPUCAL_CTRL               (0xe0 << 2)
+#define CMN_TXPUCAL_OVRD               (0xe1 << 2)
+#define CMN_TXPDCAL_OVRD               (0xf1 << 2)
+#define CMN_DIAG_PLL0_FBH_OVRD         (0x1c0 << 2)
+#define CMN_DIAG_PLL0_FBL_OVRD         (0x1c1 << 2)
+#define CMN_DIAG_PLL0_OVRD             (0x1c2 << 2)
+#define CMN_DIAG_PLL0_V2I_TUNE         (0x1c5 << 2)
+#define CMN_DIAG_PLL0_CP_TUNE          (0x1c6 << 2)
+#define CMN_DIAG_PLL0_LF_PROG          (0x1c7 << 2)
+#define CMN_DIAG_PLL1_FBH_OVRD         (0x1d0 << 2)
+#define CMN_DIAG_PLL1_FBL_OVRD         (0x1d1 << 2)
+#define CMN_DIAG_PLL1_OVRD             (0x1d2 << 2)
+#define CMN_DIAG_PLL1_V2I_TUNE         (0x1d5 << 2)
+#define CMN_DIAG_PLL1_CP_TUNE          (0x1d6 << 2)
+#define CMN_DIAG_PLL1_LF_PROG          (0x1d7 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE1     (0x1d8 << 2)
+#define CMN_DIAG_PLL1_PTATIS_TUNE2     (0x1d9 << 2)
+#define CMN_DIAG_PLL1_INCLK_CTRL       (0x1da << 2)
+#define CMN_DIAG_HSCLK_SEL             (0x1e0 << 2)
+
+#define XCVR_PSM_RCTRL(n)              ((0x4001 | ((n) << 9)) << 2)
+#define XCVR_PSM_CAL_TMR(n)            ((0x4002 | ((n) << 9)) << 2)
+#define XCVR_PSM_A0IN_TMR(n)           ((0x4003 | ((n) << 9)) << 2)
+#define TX_TXCC_CAL_SCLR_MULT(n)       ((0x4047 | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_00(n)       ((0x404c | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_01(n)       ((0x404d | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_10(n)       ((0x404e | ((n) << 9)) << 2)
+#define TX_TXCC_CPOST_MULT_11(n)       ((0x404f | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_000(n)      ((0x4050 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_001(n)      ((0x4051 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_010(n)      ((0x4052 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_011(n)      ((0x4053 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_100(n)      ((0x4054 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_101(n)      ((0x4055 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_110(n)      ((0x4056 | ((n) << 9)) << 2)
+#define TX_TXCC_MGNFS_MULT_111(n)      ((0x4057 | ((n) << 9)) << 2)
+#define XCVR_DIAG_PLLDRC_CTRL(n)       ((0x40e0 | ((n) << 9)) << 2)
+#define XCVR_DIAG_BIDI_CTRL(n)         ((0x40e8 | ((n) << 9)) << 2)
+#define XCVR_DIAG_LANE_FCM_EN_MGN(n)   ((0x40f2 | ((n) << 9)) << 2)
+#define TX_PSC_A0(n)                   ((0x4100 | ((n) << 9)) << 2)
+#define TX_PSC_A1(n)                   ((0x4101 | ((n) << 9)) << 2)
+#define TX_PSC_A2(n)                   ((0x4102 | ((n) << 9)) << 2)
+#define TX_PSC_A3(n)                   ((0x4103 | ((n) << 9)) << 2)
+#define TX_RCVDET_CTRL(n)              ((0x4120 | ((n) << 9)) << 2)
+#define TX_RCVDET_EN_TMR(n)            ((0x4122 | ((n) << 9)) << 2)
+#define TX_RCVDET_ST_TMR(n)            ((0x4123 | ((n) << 9)) << 2)
+#define TX_DIAG_TX_DRV(n)              ((0x41e1 | ((n) << 9)) << 2)
+#define TX_DIAG_BGREF_PREDRV_DELAY     (0x41e7 << 2)
+#define TX_ANA_CTRL_REG_1              (0x5020 << 2)
+#define TX_ANA_CTRL_REG_2              (0x5021 << 2)
+#define TXDA_COEFF_CALC_CTRL           (0x5022 << 2)
+#define TX_DIG_CTRL_REG_2              (0x5024 << 2)
+#define TXDA_CYA_AUXDA_CYA             (0x5025 << 2)
+#define TX_ANA_CTRL_REG_3              (0x5026 << 2)
+#define TX_ANA_CTRL_REG_4              (0x5027 << 2)
+#define TX_ANA_CTRL_REG_5              (0x5029 << 2)
+
+#define RX_PSC_A0(n)                   ((0x8000 | ((n) << 9)) << 2)
+#define RX_PSC_A1(n)                   ((0x8001 | ((n) << 9)) << 2)
+#define RX_PSC_A2(n)                   ((0x8002 | ((n) << 9)) << 2)
+#define RX_PSC_A3(n)                   ((0x8003 | ((n) << 9)) << 2)
+#define RX_PSC_CAL(n)                  ((0x8006 | ((n) << 9)) << 2)
+#define RX_PSC_RDY(n)                  ((0x8007 | ((n) << 9)) << 2)
+#define RX_IQPI_ILL_CAL_OVRD           (0x8023 << 2)
+#define RX_EPI_ILL_CAL_OVRD            (0x8033 << 2)
+#define RX_SDCAL0_OVRD                 (0x8041 << 2)
+#define RX_SDCAL1_OVRD                 (0x8049 << 2)
+#define RX_SLC_INIT                    (0x806d << 2)
+#define RX_SLC_RUN                     (0x806e << 2)
+#define RX_CDRLF_CNFG2                 (0x8081 << 2)
+#define RX_SIGDET_HL_FILT_TMR(n)       ((0x8090 | ((n) << 9)) << 2)
+#define RX_SLC_IOP0_OVRD               (0x8101 << 2)
+#define RX_SLC_IOP1_OVRD               (0x8105 << 2)
+#define RX_SLC_QOP0_OVRD               (0x8109 << 2)
+#define RX_SLC_QOP1_OVRD               (0x810d << 2)
+#define RX_SLC_EOP0_OVRD               (0x8111 << 2)
+#define RX_SLC_EOP1_OVRD               (0x8115 << 2)
+#define RX_SLC_ION0_OVRD               (0x8119 << 2)
+#define RX_SLC_ION1_OVRD               (0x811d << 2)
+#define RX_SLC_QON0_OVRD               (0x8121 << 2)
+#define RX_SLC_QON1_OVRD               (0x8125 << 2)
+#define RX_SLC_EON0_OVRD               (0x8129 << 2)
+#define RX_SLC_EON1_OVRD               (0x812d << 2)
+#define RX_SLC_IEP0_OVRD               (0x8131 << 2)
+#define RX_SLC_IEP1_OVRD               (0x8135 << 2)
+#define RX_SLC_QEP0_OVRD               (0x8139 << 2)
+#define RX_SLC_QEP1_OVRD               (0x813d << 2)
+#define RX_SLC_EEP0_OVRD               (0x8141 << 2)
+#define RX_SLC_EEP1_OVRD               (0x8145 << 2)
+#define RX_SLC_IEN0_OVRD               (0x8149 << 2)
+#define RX_SLC_IEN1_OVRD               (0x814d << 2)
+#define RX_SLC_QEN0_OVRD               (0x8151 << 2)
+#define RX_SLC_QEN1_OVRD               (0x8155 << 2)
+#define RX_SLC_EEN0_OVRD               (0x8159 << 2)
+#define RX_SLC_EEN1_OVRD               (0x815d << 2)
+#define RX_REE_CTRL_DATA_MASK(n)       ((0x81bb | ((n) << 9)) << 2)
+#define RX_DIAG_SIGDET_TUNE(n)         ((0x81dc | ((n) << 9)) << 2)
+#define RX_DIAG_SC2C_DELAY             (0x81e1 << 2)
+
+#define PMA_LANE_CFG                   (0xc000 << 2)
+#define PIPE_CMN_CTRL1                 (0xc001 << 2)
+#define PIPE_CMN_CTRL2                 (0xc002 << 2)
+#define PIPE_COM_LOCK_CFG1             (0xc003 << 2)
+#define PIPE_COM_LOCK_CFG2             (0xc004 << 2)
+#define PIPE_RCV_DET_INH               (0xc005 << 2)
+#define DP_MODE_CTL                    (0xc008 << 2)
+#define DP_CLK_CTL                     (0xc009 << 2)
+#define STS                            (0xc00F << 2)
+#define PHY_ISO_CMN_CTRL               (0xc010 << 2)
+#define PHY_DP_TX_CTL                  (0xc408 << 2)
+#define PMA_CMN_CTRL1                  (0xc800 << 2)
+#define PHY_PMA_ISO_CMN_CTRL           (0xc810 << 2)
+#define PHY_ISOLATION_CTRL             (0xc81f << 2)
+#define PHY_PMA_ISO_XCVR_CTRL(n)       ((0xcc11 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_LINK_MODE(n)       ((0xcc12 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_PWRST_CTRL(n)      ((0xcc13 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_LO(n)      ((0xcc14 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_TX_DATA_HI(n)      ((0xcc15 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_LO(n)      ((0xcc16 | ((n) << 6)) << 2)
+#define PHY_PMA_ISO_RX_DATA_HI(n)      ((0xcc17 | ((n) << 6)) << 2)
+#define TX_BIST_CTRL(n)                        ((0x4140 | ((n) << 9)) << 2)
+#define TX_BIST_UDDWR(n)               ((0x4141 | ((n) << 9)) << 2)
+
+/*
+ * Selects which PLL clock will be driven on the analog high speed
+ * clock 0: PLL 0 div 1
+ * clock 1: PLL 1 div 2
+ */
+#define CLK_PLL_CONFIG                 0X30
+#define CLK_PLL_MASK                   0x33
+
+#define CMN_READY                      BIT(0)
+
+#define DP_PLL_CLOCK_ENABLE            BIT(2)
+#define DP_PLL_ENABLE                  BIT(0)
+#define DP_PLL_DATA_RATE_RBR           ((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR           ((2 << 12) | (4 << 8))
+#define DP_PLL_DATA_RATE_HBR2          ((1 << 12) | (2 << 8))
+
+#define DP_MODE_A0                     BIT(4)
+#define DP_MODE_A2                     BIT(6)
+#define DP_MODE_ENTER_A0               0xc101
+#define DP_MODE_ENTER_A2               0xc104
+
+#define PHY_MODE_SET_TIMEOUT           100000
+
+#define PIN_ASSIGN_C_E                 0x51d9
+#define PIN_ASSIGN_D_F                 0x5100
+
+#define MODE_DISCONNECT                        0
+#define MODE_UFP_USB                   BIT(0)
+#define MODE_DFP_USB                   BIT(1)
+#define MODE_DFP_DP                    BIT(2)
+
+struct usb3phy_reg {
+       u32 offset;
+       u32 enable_bit;
+       u32 write_enable;
+};
+
+struct rockchip_usb3phy_port_cfg {
+       struct usb3phy_reg typec_conn_dir;
+       struct usb3phy_reg usb3tousb2_en;
+       struct usb3phy_reg external_psm;
+       struct usb3phy_reg pipe_status;
+};
+
+struct rockchip_typec_phy {
+       struct device *dev;
+       void __iomem *base;
+       struct extcon_dev *extcon;
+       struct regmap *grf_regs;
+       struct clk *clk_core;
+       struct clk *clk_ref;
+       struct reset_control *uphy_rst;
+       struct reset_control *pipe_rst;
+       struct reset_control *tcphy_rst;
+       struct rockchip_usb3phy_port_cfg port_cfgs;
+       /* mutex to protect access to individual PHYs */
+       struct mutex lock;
+
+       bool flip;
+       u8 mode;
+};
+
+struct phy_reg {
+       u16 value;
+       u32 addr;
+};
+
+struct phy_reg usb3_pll_cfg[] = {
+       { 0xf0,         CMN_PLL0_VCOCAL_INIT },
+       { 0x18,         CMN_PLL0_VCOCAL_ITER },
+       { 0xd0,         CMN_PLL0_INTDIV },
+       { 0x4a4a,       CMN_PLL0_FRACDIV },
+       { 0x34,         CMN_PLL0_HIGH_THR },
+       { 0x1ee,        CMN_PLL0_SS_CTRL1 },
+       { 0x7f03,       CMN_PLL0_SS_CTRL2 },
+       { 0x20,         CMN_PLL0_DSM_DIAG },
+       { 0,            CMN_DIAG_PLL0_OVRD },
+       { 0,            CMN_DIAG_PLL0_FBH_OVRD },
+       { 0,            CMN_DIAG_PLL0_FBL_OVRD },
+       { 0x7,          CMN_DIAG_PLL0_V2I_TUNE },
+       { 0x45,         CMN_DIAG_PLL0_CP_TUNE },
+       { 0x8,          CMN_DIAG_PLL0_LF_PROG },
+};
+
+struct phy_reg dp_pll_cfg[] = {
+       { 0xf0,         CMN_PLL1_VCOCAL_INIT },
+       { 0x18,         CMN_PLL1_VCOCAL_ITER },
+       { 0x30b9,       CMN_PLL1_VCOCAL_START },
+       { 0x21c,        CMN_PLL1_INTDIV },
+       { 0,            CMN_PLL1_FRACDIV },
+       { 0x5,          CMN_PLL1_HIGH_THR },
+       { 0x35,         CMN_PLL1_SS_CTRL1 },
+       { 0x7f1e,       CMN_PLL1_SS_CTRL2 },
+       { 0x20,         CMN_PLL1_DSM_DIAG },
+       { 0,            CMN_PLLSM1_USER_DEF_CTRL },
+       { 0,            CMN_DIAG_PLL1_OVRD },
+       { 0,            CMN_DIAG_PLL1_FBH_OVRD },
+       { 0,            CMN_DIAG_PLL1_FBL_OVRD },
+       { 0x6,          CMN_DIAG_PLL1_V2I_TUNE },
+       { 0x45,         CMN_DIAG_PLL1_CP_TUNE },
+       { 0x8,          CMN_DIAG_PLL1_LF_PROG },
+       { 0x100,        CMN_DIAG_PLL1_PTATIS_TUNE1 },
+       { 0x7,          CMN_DIAG_PLL1_PTATIS_TUNE2 },
+       { 0x4,          CMN_DIAG_PLL1_INCLK_CTRL },
+};
+
+static void tcphy_cfg_24m(struct rockchip_typec_phy *tcphy)
+{
+       u32 i, rdata;
+
+       /*
+        * cmn_ref_clk_sel = 3, select the 24Mhz for clk parent
+        * cmn_psm_clk_dig_div = 2, set the clk division to 2
+        */
+       writel(0x830, tcphy->base + PMA_CMN_CTRL1);
+       for (i = 0; i < 4; i++) {
+               /*
+                * The following PHY configuration assumes a 24 MHz reference
+                * clock.
+                */
+               writel(0x90, tcphy->base + XCVR_DIAG_LANE_FCM_EN_MGN(i));
+               writel(0x960, tcphy->base + TX_RCVDET_EN_TMR(i));
+               writel(0x30, tcphy->base + TX_RCVDET_ST_TMR(i));
+       }
+
+       rdata = readl(tcphy->base + CMN_DIAG_HSCLK_SEL);
+       rdata &= ~CLK_PLL_MASK;
+       rdata |= CLK_PLL_CONFIG;
+       writel(rdata, tcphy->base + CMN_DIAG_HSCLK_SEL);
+}
+
+static void tcphy_cfg_usb3_pll(struct rockchip_typec_phy *tcphy)
+{
+       u32 i;
+
+       /* load the configuration of PLL0 */
+       for (i = 0; i < ARRAY_SIZE(usb3_pll_cfg); i++)
+               writel(usb3_pll_cfg[i].value,
+                      tcphy->base + usb3_pll_cfg[i].addr);
+}
+
+static void tcphy_cfg_dp_pll(struct rockchip_typec_phy *tcphy)
+{
+       u32 i;
+
+       /* set the default mode to RBR */
+       writel(DP_PLL_CLOCK_ENABLE | DP_PLL_ENABLE | DP_PLL_DATA_RATE_RBR,
+              tcphy->base + DP_CLK_CTL);
+
+       /* load the configuration of PLL1 */
+       for (i = 0; i < ARRAY_SIZE(dp_pll_cfg); i++)
+               writel(dp_pll_cfg[i].value, tcphy->base + dp_pll_cfg[i].addr);
+}
+
+static void tcphy_tx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+       writel(0x7799, tcphy->base + TX_PSC_A0(lane));
+       writel(0x7798, tcphy->base + TX_PSC_A1(lane));
+       writel(0x5098, tcphy->base + TX_PSC_A2(lane));
+       writel(0x5098, tcphy->base + TX_PSC_A3(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+       writel(0xbf, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_rx_usb3_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+       writel(0xa6fd, tcphy->base + RX_PSC_A0(lane));
+       writel(0xa6fd, tcphy->base + RX_PSC_A1(lane));
+       writel(0xa410, tcphy->base + RX_PSC_A2(lane));
+       writel(0x2410, tcphy->base + RX_PSC_A3(lane));
+       writel(0x23ff, tcphy->base + RX_PSC_CAL(lane));
+       writel(0x13, tcphy->base + RX_SIGDET_HL_FILT_TMR(lane));
+       writel(0x03e7, tcphy->base + RX_REE_CTRL_DATA_MASK(lane));
+       writel(0x1004, tcphy->base + RX_DIAG_SIGDET_TUNE(lane));
+       writel(0x2010, tcphy->base + RX_PSC_RDY(lane));
+       writel(0xfb, tcphy->base + XCVR_DIAG_BIDI_CTRL(lane));
+}
+
+static void tcphy_dp_cfg_lane(struct rockchip_typec_phy *tcphy, u32 lane)
+{
+       u16 rdata;
+
+       writel(0xbefc, tcphy->base + XCVR_PSM_RCTRL(lane));
+       writel(0x6799, tcphy->base + TX_PSC_A0(lane));
+       writel(0x6798, tcphy->base + TX_PSC_A1(lane));
+       writel(0x98, tcphy->base + TX_PSC_A2(lane));
+       writel(0x98, tcphy->base + TX_PSC_A3(lane));
+
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_000(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_001(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_010(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_011(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_100(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_101(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_110(lane));
+       writel(0, tcphy->base + TX_TXCC_MGNFS_MULT_111(lane));
+       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_10(lane));
+       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_01(lane));
+       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_00(lane));
+       writel(0, tcphy->base + TX_TXCC_CPOST_MULT_11(lane));
+
+       writel(0x128, tcphy->base + TX_TXCC_CAL_SCLR_MULT(lane));
+       writel(0x400, tcphy->base + TX_DIAG_TX_DRV(lane));
+
+       rdata = readl(tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+       rdata = (rdata & 0x8fff) | 0x6000;
+       writel(rdata, tcphy->base + XCVR_DIAG_PLLDRC_CTRL(lane));
+}
+
+static inline int property_enable(struct rockchip_typec_phy *tcphy,
+                                 const struct usb3phy_reg *reg, bool en)
+{
+       u32 mask = 1 << reg->write_enable;
+       u32 val = en << reg->enable_bit;
+
+       return regmap_write(tcphy->grf_regs, reg->offset, val | mask);
+}
+
+static void tcphy_dp_aux_calibration(struct rockchip_typec_phy *tcphy)
+{
+       u16 rdata, rdata2, val;
+
+       /* disable txda_cal_latch_en for rewrite the calibration values */
+       rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+       val = rdata & 0xdfff;
+       writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+       /*
+        * read a resistor calibration code from CMN_TXPUCAL_CTRL[6:0] and
+        * write it to TX_DIG_CTRL_REG_2[6:0], and delay 1ms to make sure it
+        * works.
+        */
+       rdata = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+       rdata = rdata & 0xffc0;
+
+       rdata2 = readl(tcphy->base + CMN_TXPUCAL_CTRL);
+       rdata2 = rdata2 & 0x3f;
+
+       val = rdata | rdata2;
+       writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+       usleep_range(1000, 1050);
+
+       /*
+        * Enable signal for latch that sample and holds calibration values.
+        * Activate this signal for 1 clock cycle to sample new calibration
+        * values.
+        */
+       rdata = readl(tcphy->base + TX_ANA_CTRL_REG_1);
+       val = rdata | 0x2000;
+       writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+       usleep_range(150, 200);
+
+       /* set TX Voltage Level and TX Deemphasis to 0 */
+       writel(0, tcphy->base + PHY_DP_TX_CTL);
+       /* re-enable decap */
+       writel(0x100, tcphy->base + TX_ANA_CTRL_REG_2);
+       writel(0x300, tcphy->base + TX_ANA_CTRL_REG_2);
+       writel(0x2008, tcphy->base + TX_ANA_CTRL_REG_1);
+       writel(0x2018, tcphy->base + TX_ANA_CTRL_REG_1);
+
+       writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+       /*
+        * Programs txda_drv_ldo_prog[15:0], Sets driver LDO
+        * voltage 16'h1001 for DP-AUX-TX and RX
+        */
+       writel(0x1001, tcphy->base + TX_ANA_CTRL_REG_4);
+
+       /* re-enables Bandgap reference for LDO */
+       writel(0x2098, tcphy->base + TX_ANA_CTRL_REG_1);
+       writel(0x2198, tcphy->base + TX_ANA_CTRL_REG_1);
+
+       /*
+        * re-enables the transmitter pre-driver, driver data selection MUX,
+        * and receiver detect circuits.
+        */
+       writel(0x301, tcphy->base + TX_ANA_CTRL_REG_2);
+       writel(0x303, tcphy->base + TX_ANA_CTRL_REG_2);
+
+       /*
+        * BIT 12: Controls auxda_polarity, which selects the polarity of the
+        * xcvr:
+        * 1, Reverses the polarity (If TYPEC, Pulls ups aux_p and pull
+        * down aux_m)
+        * 0, Normal polarity (if TYPE_C, pulls up aux_m and pulls down
+        * aux_p)
+        */
+       val = 0xa078;
+       if (!tcphy->flip)
+               val |= BIT(12);
+       writel(val, tcphy->base + TX_ANA_CTRL_REG_1);
+
+       writel(0, tcphy->base + TX_ANA_CTRL_REG_3);
+       writel(0, tcphy->base + TX_ANA_CTRL_REG_4);
+       writel(0, tcphy->base + TX_ANA_CTRL_REG_5);
+
+       /*
+        * Controls low_power_swing_en, set the voltage swing of the driver
+        * to 400mv. The values below are peak to peak (differential) values.
+        */
+       writel(4, tcphy->base + TXDA_COEFF_CALC_CTRL);
+       writel(0, tcphy->base + TXDA_CYA_AUXDA_CYA);
+
+       /* Controls tx_high_z_tm_en */
+       val = readl(tcphy->base + TX_DIG_CTRL_REG_2);
+       val |= BIT(15);
+       writel(val, tcphy->base + TX_DIG_CTRL_REG_2);
+}
+
+static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode)
+{
+       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+       int ret, i;
+       u32 val;
+
+       ret = clk_prepare_enable(tcphy->clk_core);
+       if (ret) {
+               dev_err(tcphy->dev, "Failed to prepare_enable core clock\n");
+               return ret;
+       }
+
+       ret = clk_prepare_enable(tcphy->clk_ref);
+       if (ret) {
+               dev_err(tcphy->dev, "Failed to prepare_enable ref clock\n");
+               goto err_clk_core;
+       }
+
+       reset_control_deassert(tcphy->tcphy_rst);
+
+       property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip);
+
+       tcphy_cfg_24m(tcphy);
+
+       if (mode == MODE_DFP_DP) {
+               tcphy_cfg_dp_pll(tcphy);
+               for (i = 0; i < 4; i++)
+                       tcphy_dp_cfg_lane(tcphy, i);
+
+               writel(PIN_ASSIGN_C_E, tcphy->base + PMA_LANE_CFG);
+       } else {
+               tcphy_cfg_usb3_pll(tcphy);
+               tcphy_cfg_dp_pll(tcphy);
+               if (tcphy->flip) {
+                       tcphy_tx_usb3_cfg_lane(tcphy, 3);
+                       tcphy_rx_usb3_cfg_lane(tcphy, 2);
+                       tcphy_dp_cfg_lane(tcphy, 0);
+                       tcphy_dp_cfg_lane(tcphy, 1);
+               } else {
+                       tcphy_tx_usb3_cfg_lane(tcphy, 0);
+                       tcphy_rx_usb3_cfg_lane(tcphy, 1);
+                       tcphy_dp_cfg_lane(tcphy, 2);
+                       tcphy_dp_cfg_lane(tcphy, 3);
+               }
+
+               writel(PIN_ASSIGN_D_F, tcphy->base + PMA_LANE_CFG);
+       }
+
+       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+
+       reset_control_deassert(tcphy->uphy_rst);
+
+       ret = readx_poll_timeout(readl, tcphy->base + PMA_CMN_CTRL1,
+                                val, val & CMN_READY, 10,
+                                PHY_MODE_SET_TIMEOUT);
+       if (ret < 0) {
+               dev_err(tcphy->dev, "wait pma ready timeout\n");
+               ret = -ETIMEDOUT;
+               goto err_wait_pma;
+       }
+
+       reset_control_deassert(tcphy->pipe_rst);
+
+       return 0;
+
+err_wait_pma:
+       reset_control_assert(tcphy->uphy_rst);
+       reset_control_assert(tcphy->tcphy_rst);
+       clk_disable_unprepare(tcphy->clk_ref);
+err_clk_core:
+       clk_disable_unprepare(tcphy->clk_core);
+       return ret;
+}
+
+static void tcphy_phy_deinit(struct rockchip_typec_phy *tcphy)
+{
+       reset_control_assert(tcphy->tcphy_rst);
+       reset_control_assert(tcphy->uphy_rst);
+       reset_control_assert(tcphy->pipe_rst);
+       clk_disable_unprepare(tcphy->clk_core);
+       clk_disable_unprepare(tcphy->clk_ref);
+}
+
+static int tcphy_get_mode(struct rockchip_typec_phy *tcphy)
+{
+       struct extcon_dev *edev = tcphy->extcon;
+       union extcon_property_value property;
+       unsigned int id;
+       bool dfp, ufp, dp;
+       u8 mode;
+       int ret;
+
+       ufp = extcon_get_state(edev, EXTCON_USB);
+       dfp = extcon_get_state(edev, EXTCON_USB_HOST);
+       dp = extcon_get_state(edev, EXTCON_DISP_DP);
+
+       mode = MODE_DFP_USB;
+       id = EXTCON_USB_HOST;
+
+       if (ufp) {
+               mode = MODE_UFP_USB;
+               id = EXTCON_USB;
+       } else if (dp) {
+               mode = MODE_DFP_DP;
+               id = EXTCON_DISP_DP;
+
+               ret = extcon_get_property(edev, id, EXTCON_PROP_USB_SS,
+                                         &property);
+               if (ret) {
+                       dev_err(tcphy->dev, "get superspeed property failed\n");
+                       return ret;
+               }
+
+               if (property.intval)
+                       mode |= MODE_DFP_USB;
+       }
+
+       ret = extcon_get_property(edev, id, EXTCON_PROP_USB_TYPEC_POLARITY,
+                                 &property);
+       if (ret) {
+               dev_err(tcphy->dev, "get polarity property failed\n");
+               return ret;
+       }
+
+       tcphy->flip = property.intval ? 1 : 0;
+
+       return mode;
+}
+
+static int rockchip_usb3_phy_power_on(struct phy *phy)
+{
+       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+       const struct usb3phy_reg *reg = &cfg->pipe_status;
+       int timeout, new_mode, ret = 0;
+       u32 val;
+
+       mutex_lock(&tcphy->lock);
+
+       new_mode = tcphy_get_mode(tcphy);
+       if (new_mode < 0) {
+               ret = new_mode;
+               goto unlock_ret;
+       }
+
+       /* DP-only mode; fall back to USB2 */
+       if (!(new_mode & (MODE_DFP_USB | MODE_UFP_USB)))
+               goto unlock_ret;
+
+       if (tcphy->mode == new_mode)
+               goto unlock_ret;
+
+       if (tcphy->mode == MODE_DISCONNECT)
+               tcphy_phy_init(tcphy, new_mode);
+
+       /* wait TCPHY for pipe ready */
+       for (timeout = 0; timeout < 100; timeout++) {
+               regmap_read(tcphy->grf_regs, reg->offset, &val);
+               if (!(val & BIT(reg->enable_bit))) {
+                       tcphy->mode |= new_mode & (MODE_DFP_USB | MODE_UFP_USB);
+                       goto unlock_ret;
+               }
+               usleep_range(10, 20);
+       }
+
+       if (tcphy->mode == MODE_DISCONNECT)
+               tcphy_phy_deinit(tcphy);
+
+       ret = -ETIMEDOUT;
+
+unlock_ret:
+       mutex_unlock(&tcphy->lock);
+       return ret;
+}
+
+static int rockchip_usb3_phy_power_off(struct phy *phy)
+{
+       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+
+       mutex_lock(&tcphy->lock);
+
+       if (tcphy->mode == MODE_DISCONNECT)
+               goto unlock;
+
+       tcphy->mode &= ~(MODE_UFP_USB | MODE_DFP_USB);
+       if (tcphy->mode == MODE_DISCONNECT)
+               tcphy_phy_deinit(tcphy);
+
+unlock:
+       mutex_unlock(&tcphy->lock);
+       return 0;
+}
+
+static const struct phy_ops rockchip_usb3_phy_ops = {
+       .power_on       = rockchip_usb3_phy_power_on,
+       .power_off      = rockchip_usb3_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int rockchip_dp_phy_power_on(struct phy *phy)
+{
+       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+       int new_mode, ret = 0;
+       u32 val;
+
+       mutex_lock(&tcphy->lock);
+
+       new_mode = tcphy_get_mode(tcphy);
+       if (new_mode < 0) {
+               ret = new_mode;
+               goto unlock_ret;
+       }
+
+       if (!(new_mode & MODE_DFP_DP)) {
+               ret = -ENODEV;
+               goto unlock_ret;
+       }
+
+       if (tcphy->mode == new_mode)
+               goto unlock_ret;
+
+       /*
+        * If the PHY has been power on, but the mode is not DP only mode,
+        * re-init the PHY for setting all of 4 lanes to DP.
+        */
+       if (new_mode == MODE_DFP_DP && tcphy->mode != MODE_DISCONNECT) {
+               tcphy_phy_deinit(tcphy);
+               tcphy_phy_init(tcphy, new_mode);
+       } else if (tcphy->mode == MODE_DISCONNECT) {
+               tcphy_phy_init(tcphy, new_mode);
+       }
+
+       ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+                                val, val & DP_MODE_A2, 1000,
+                                PHY_MODE_SET_TIMEOUT);
+       if (ret < 0) {
+               dev_err(tcphy->dev, "failed to wait TCPHY enter A2\n");
+               goto power_on_finish;
+       }
+
+       tcphy_dp_aux_calibration(tcphy);
+
+       writel(DP_MODE_ENTER_A0, tcphy->base + DP_MODE_CTL);
+
+       ret = readx_poll_timeout(readl, tcphy->base + DP_MODE_CTL,
+                                val, val & DP_MODE_A0, 1000,
+                                PHY_MODE_SET_TIMEOUT);
+       if (ret < 0) {
+               writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+               dev_err(tcphy->dev, "failed to wait TCPHY enter A0\n");
+               goto power_on_finish;
+       }
+
+       tcphy->mode |= MODE_DFP_DP;
+
+power_on_finish:
+       if (tcphy->mode == MODE_DISCONNECT)
+               tcphy_phy_deinit(tcphy);
+unlock_ret:
+       mutex_unlock(&tcphy->lock);
+       return ret;
+}
+
+static int rockchip_dp_phy_power_off(struct phy *phy)
+{
+       struct rockchip_typec_phy *tcphy = phy_get_drvdata(phy);
+
+       mutex_lock(&tcphy->lock);
+
+       if (tcphy->mode == MODE_DISCONNECT)
+               goto unlock;
+
+       tcphy->mode &= ~MODE_DFP_DP;
+
+       writel(DP_MODE_ENTER_A2, tcphy->base + DP_MODE_CTL);
+
+       if (tcphy->mode == MODE_DISCONNECT)
+               tcphy_phy_deinit(tcphy);
+
+unlock:
+       mutex_unlock(&tcphy->lock);
+       return 0;
+}
+
+static const struct phy_ops rockchip_dp_phy_ops = {
+       .power_on       = rockchip_dp_phy_power_on,
+       .power_off      = rockchip_dp_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int tcphy_get_param(struct device *dev,
+                          struct usb3phy_reg *reg,
+                          const char *name)
+{
+       u32 buffer[3];
+       int ret;
+
+       ret = of_property_read_u32_array(dev->of_node, name, buffer, 3);
+       if (ret) {
+               dev_err(dev, "Can not parse %s\n", name);
+               return ret;
+       }
+
+       reg->offset = buffer[0];
+       reg->enable_bit = buffer[1];
+       reg->write_enable = buffer[2];
+       return 0;
+}
+
+static int tcphy_parse_dt(struct rockchip_typec_phy *tcphy,
+                         struct device *dev)
+{
+       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+       int ret;
+
+       ret = tcphy_get_param(dev, &cfg->typec_conn_dir,
+                             "rockchip,typec-conn-dir");
+       if (ret)
+               return ret;
+
+       ret = tcphy_get_param(dev, &cfg->usb3tousb2_en,
+                             "rockchip,usb3tousb2-en");
+       if (ret)
+               return ret;
+
+       ret = tcphy_get_param(dev, &cfg->external_psm,
+                             "rockchip,external-psm");
+       if (ret)
+               return ret;
+
+       ret = tcphy_get_param(dev, &cfg->pipe_status,
+                             "rockchip,pipe-status");
+       if (ret)
+               return ret;
+
+       tcphy->grf_regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                         "rockchip,grf");
+       if (IS_ERR(tcphy->grf_regs)) {
+               dev_err(dev, "could not find grf dt node\n");
+               return PTR_ERR(tcphy->grf_regs);
+       }
+
+       tcphy->clk_core = devm_clk_get(dev, "tcpdcore");
+       if (IS_ERR(tcphy->clk_core)) {
+               dev_err(dev, "could not get uphy core clock\n");
+               return PTR_ERR(tcphy->clk_core);
+       }
+
+       tcphy->clk_ref = devm_clk_get(dev, "tcpdphy-ref");
+       if (IS_ERR(tcphy->clk_ref)) {
+               dev_err(dev, "could not get uphy ref clock\n");
+               return PTR_ERR(tcphy->clk_ref);
+       }
+
+       tcphy->uphy_rst = devm_reset_control_get(dev, "uphy");
+       if (IS_ERR(tcphy->uphy_rst)) {
+               dev_err(dev, "no uphy_rst reset control found\n");
+               return PTR_ERR(tcphy->uphy_rst);
+       }
+
+       tcphy->pipe_rst = devm_reset_control_get(dev, "uphy-pipe");
+       if (IS_ERR(tcphy->pipe_rst)) {
+               dev_err(dev, "no pipe_rst reset control found\n");
+               return PTR_ERR(tcphy->pipe_rst);
+       }
+
+       tcphy->tcphy_rst = devm_reset_control_get(dev, "uphy-tcphy");
+       if (IS_ERR(tcphy->tcphy_rst)) {
+               dev_err(dev, "no tcphy_rst reset control found\n");
+               return PTR_ERR(tcphy->tcphy_rst);
+       }
+
+       return 0;
+}
+
+static void typec_phy_pre_init(struct rockchip_typec_phy *tcphy)
+{
+       struct rockchip_usb3phy_port_cfg *cfg = &tcphy->port_cfgs;
+
+       reset_control_assert(tcphy->tcphy_rst);
+       reset_control_assert(tcphy->uphy_rst);
+       reset_control_assert(tcphy->pipe_rst);
+
+       /* select external psm clock */
+       property_enable(tcphy, &cfg->external_psm, 1);
+       property_enable(tcphy, &cfg->usb3tousb2_en, 0);
+
+       tcphy->mode = MODE_DISCONNECT;
+}
+
+static int rockchip_typec_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct device_node *child_np;
+       struct rockchip_typec_phy *tcphy;
+       struct phy_provider *phy_provider;
+       struct resource *res;
+       int ret;
+
+       tcphy = devm_kzalloc(dev, sizeof(*tcphy), GFP_KERNEL);
+       if (!tcphy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       tcphy->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(tcphy->base))
+               return PTR_ERR(tcphy->base);
+
+       ret = tcphy_parse_dt(tcphy, dev);
+       if (ret)
+               return ret;
+
+       tcphy->dev = dev;
+       platform_set_drvdata(pdev, tcphy);
+       mutex_init(&tcphy->lock);
+
+       typec_phy_pre_init(tcphy);
+
+       tcphy->extcon = extcon_get_edev_by_phandle(dev, 0);
+       if (IS_ERR(tcphy->extcon)) {
+               if (PTR_ERR(tcphy->extcon) != -EPROBE_DEFER)
+                       dev_err(dev, "Invalid or missing extcon\n");
+               return PTR_ERR(tcphy->extcon);
+       }
+
+       pm_runtime_enable(dev);
+
+       for_each_available_child_of_node(np, child_np) {
+               struct phy *phy;
+
+               if (!of_node_cmp(child_np->name, "dp-port"))
+                       phy = devm_phy_create(dev, child_np,
+                                             &rockchip_dp_phy_ops);
+               else if (!of_node_cmp(child_np->name, "usb3-port"))
+                       phy = devm_phy_create(dev, child_np,
+                                             &rockchip_usb3_phy_ops);
+               else
+                       continue;
+
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create phy: %s\n",
+                               child_np->name);
+                       return PTR_ERR(phy);
+               }
+
+               phy_set_drvdata(phy, tcphy);
+       }
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               dev_err(dev, "Failed to register phy provider\n");
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static int rockchip_typec_phy_remove(struct platform_device *pdev)
+{
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static const struct of_device_id rockchip_typec_phy_dt_ids[] = {
+       { .compatible = "rockchip,rk3399-typec-phy" },
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_typec_phy_dt_ids);
+
+static struct platform_driver rockchip_typec_phy_driver = {
+       .probe          = rockchip_typec_phy_probe,
+       .remove         = rockchip_typec_phy_remove,
+       .driver         = {
+               .name   = "rockchip-typec-phy",
+               .of_match_table = rockchip_typec_phy_dt_ids,
+       },
+};
+
+module_platform_driver(rockchip_typec_phy_driver);
+
+MODULE_AUTHOR("Chris Zhong <zyw@rock-chips.com>");
+MODULE_AUTHOR("Kever Yang <kever.yang@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB TYPE-C PHY driver");
+MODULE_LICENSE("GPL v2");
index 2a7381f4fe4c81598188a14e7a9bbcd76f0fed16..734987fa0ad780ef1c3bd9a77684bab9ec7783ea 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/reset.h>
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
+#include <linux/delay.h>
 
 static int enable_usb_uart;
 
@@ -64,6 +65,7 @@ struct rockchip_usb_phy {
        struct clk_hw   clk480m_hw;
        struct phy      *phy;
        bool            uart_enabled;
+       struct reset_control *reset;
 };
 
 static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
@@ -144,9 +146,23 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
        return clk_prepare_enable(phy->clk480m);
 }
 
+static int rockchip_usb_phy_reset(struct phy *_phy)
+{
+       struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+
+       if (phy->reset) {
+               reset_control_assert(phy->reset);
+               udelay(10);
+               reset_control_deassert(phy->reset);
+       }
+
+       return 0;
+}
+
 static const struct phy_ops ops = {
        .power_on       = rockchip_usb_phy_power_on,
        .power_off      = rockchip_usb_phy_power_off,
+       .reset          = rockchip_usb_phy_reset,
        .owner          = THIS_MODULE,
 };
 
@@ -185,6 +201,10 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
                return -EINVAL;
        }
 
+       rk_phy->reset = of_reset_control_get(child, "phy-reset");
+       if (IS_ERR(rk_phy->reset))
+               rk_phy->reset = NULL;
+
        rk_phy->reg_offset = reg_offset;
 
        rk_phy->clk = of_clk_get_by_name(child, "phyclk");
index 8c7eb335622ee1781a6b83d155740153b00d4717..b9342a2af7b3666f86471d649bd134a4baa9ff13 100644 (file)
@@ -40,6 +40,7 @@
 #include <linux/power_supply.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
+#include <linux/spinlock.h>
 #include <linux/usb/of.h>
 #include <linux/workqueue.h>
 
@@ -50,7 +51,7 @@
 #define REG_PHYCTL_A33                 0x10
 #define REG_PHY_UNK_H3                 0x20
 
-#define REG_PMU_UNK_H3                 0x10
+#define REG_PMU_UNK1                   0x10
 
 #define PHYCTL_DATA                    BIT(7)
 
@@ -98,6 +99,7 @@ enum sun4i_usb_phy_type {
        sun6i_a31_phy,
        sun8i_a33_phy,
        sun8i_h3_phy,
+       sun50i_a64_phy,
 };
 
 struct sun4i_usb_phy_cfg {
@@ -106,13 +108,14 @@ struct sun4i_usb_phy_cfg {
        u32 disc_thresh;
        u8 phyctl_offset;
        bool dedicated_clocks;
+       bool enable_pmu_unk1;
 };
 
 struct sun4i_usb_phy_data {
        void __iomem *base;
        const struct sun4i_usb_phy_cfg *cfg;
        enum usb_dr_mode dr_mode;
-       struct mutex mutex;
+       spinlock_t reg_lock; /* guard access to phyctl reg */
        struct sun4i_usb_phy {
                struct phy *phy;
                void __iomem *pmu;
@@ -122,7 +125,6 @@ struct sun4i_usb_phy_data {
                bool regulator_on;
                int index;
        } phys[MAX_PHYS];
-       int first_phy;
        /* phy0 / otg related variables */
        struct extcon_dev *extcon;
        bool phy0_init;
@@ -131,6 +133,7 @@ struct sun4i_usb_phy_data {
        struct power_supply *vbus_power_supply;
        struct notifier_block vbus_power_nb;
        bool vbus_power_nb_registered;
+       bool force_session_end;
        int id_det_irq;
        int vbus_det_irq;
        int id_det;
@@ -179,12 +182,14 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
        struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
        u32 temp, usbc_bit = BIT(phy->index * 2);
        void __iomem *phyctl = phy_data->base + phy_data->cfg->phyctl_offset;
+       unsigned long flags;
        int i;
 
-       mutex_lock(&phy_data->mutex);
+       spin_lock_irqsave(&phy_data->reg_lock, flags);
 
-       if (phy_data->cfg->type == sun8i_a33_phy) {
-               /* A33 needs us to set phyctl to 0 explicitly */
+       if (phy_data->cfg->type == sun8i_a33_phy ||
+           phy_data->cfg->type == sun50i_a64_phy) {
+               /* A33 or A64 needs us to set phyctl to 0 explicitly */
                writel(0, phyctl);
        }
 
@@ -218,7 +223,8 @@ static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
 
                data >>= 1;
        }
-       mutex_unlock(&phy_data->mutex);
+
+       spin_unlock_irqrestore(&phy_data->reg_lock, flags);
 }
 
 static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
@@ -258,14 +264,16 @@ static int sun4i_usb_phy_init(struct phy *_phy)
                return ret;
        }
 
+       if (data->cfg->enable_pmu_unk1) {
+               val = readl(phy->pmu + REG_PMU_UNK1);
+               writel(val & ~2, phy->pmu + REG_PMU_UNK1);
+       }
+
        if (data->cfg->type == sun8i_h3_phy) {
                if (phy->index == 0) {
                        val = readl(data->base + REG_PHY_UNK_H3);
                        writel(val & ~1, data->base + REG_PHY_UNK_H3);
                }
-
-               val = readl(phy->pmu + REG_PMU_UNK_H3);
-               writel(val & ~2, phy->pmu + REG_PMU_UNK_H3);
        } else {
                /* Enable USB 45 Ohm resistor calibration */
                if (phy->index == 0)
@@ -320,7 +328,10 @@ static int sun4i_usb_phy0_get_id_det(struct sun4i_usb_phy_data *data)
 {
        switch (data->dr_mode) {
        case USB_DR_MODE_OTG:
-               return gpiod_get_value_cansleep(data->id_det_gpio);
+               if (data->id_det_gpio)
+                       return gpiod_get_value_cansleep(data->id_det_gpio);
+               else
+                       return 1; /* Fallback to peripheral mode */
        case USB_DR_MODE_HOST:
                return 0;
        case USB_DR_MODE_PERIPHERAL:
@@ -382,8 +393,10 @@ static int sun4i_usb_phy_power_on(struct phy *_phy)
 
        /* For phy0 only turn on Vbus if we don't have an ext. Vbus */
        if (phy->index == 0 && sun4i_usb_phy0_have_vbus_det(data) &&
-                               data->vbus_det)
+                               data->vbus_det) {
+               dev_warn(&_phy->dev, "External vbus detected, not enabling our own vbus\n");
                return 0;
+       }
 
        ret = regulator_enable(phy->vbus);
        if (ret)
@@ -419,6 +432,35 @@ static int sun4i_usb_phy_power_off(struct phy *_phy)
        return 0;
 }
 
+static int sun4i_usb_phy_set_mode(struct phy *_phy, enum phy_mode mode)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+       if (phy->index != 0)
+               return -EINVAL;
+
+       switch (mode) {
+       case PHY_MODE_USB_HOST:
+               data->dr_mode = USB_DR_MODE_HOST;
+               break;
+       case PHY_MODE_USB_DEVICE:
+               data->dr_mode = USB_DR_MODE_PERIPHERAL;
+               break;
+       case PHY_MODE_USB_OTG:
+               data->dr_mode = USB_DR_MODE_OTG;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       dev_info(&_phy->dev, "Changing dr_mode to %d\n", (int)data->dr_mode);
+       data->force_session_end = true;
+       queue_delayed_work(system_wq, &data->detect, 0);
+
+       return 0;
+}
+
 void sun4i_usb_phy_set_squelch_detect(struct phy *_phy, bool enabled)
 {
        struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
@@ -432,6 +474,7 @@ static const struct phy_ops sun4i_usb_phy_ops = {
        .exit           = sun4i_usb_phy_exit,
        .power_on       = sun4i_usb_phy_power_on,
        .power_off      = sun4i_usb_phy_power_off,
+       .set_mode       = sun4i_usb_phy_set_mode,
        .owner          = THIS_MODULE,
 };
 
@@ -440,7 +483,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
        struct sun4i_usb_phy_data *data =
                container_of(work, struct sun4i_usb_phy_data, detect.work);
        struct phy *phy0 = data->phys[0].phy;
-       int id_det, vbus_det, id_notify = 0, vbus_notify = 0;
+       bool force_session_end, id_notify = false, vbus_notify = false;
+       int id_det, vbus_det;
 
        if (phy0 == NULL)
                return;
@@ -455,27 +499,30 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
                return;
        }
 
+       force_session_end = data->force_session_end;
+       data->force_session_end = false;
+
        if (id_det != data->id_det) {
-               /*
-                * When a host cable (id == 0) gets plugged in on systems
-                * without vbus detection report vbus low for long enough for
-                * the musb-ip to end the current device session.
-                */
+               /* id-change, force session end if we've no vbus detection */
                if (data->dr_mode == USB_DR_MODE_OTG &&
-                   !sun4i_usb_phy0_have_vbus_det(data) && id_det == 0) {
+                   !sun4i_usb_phy0_have_vbus_det(data))
+                       force_session_end = true;
+
+               /* When entering host mode (id = 0) force end the session now */
+               if (force_session_end && id_det == 0) {
                        sun4i_usb_phy0_set_vbus_detect(phy0, 0);
                        msleep(200);
                        sun4i_usb_phy0_set_vbus_detect(phy0, 1);
                }
                sun4i_usb_phy0_set_id_detect(phy0, id_det);
                data->id_det = id_det;
-               id_notify = 1;
+               id_notify = true;
        }
 
        if (vbus_det != data->vbus_det) {
                sun4i_usb_phy0_set_vbus_detect(phy0, vbus_det);
                data->vbus_det = vbus_det;
-               vbus_notify = 1;
+               vbus_notify = true;
        }
 
        mutex_unlock(&phy0->mutex);
@@ -483,13 +530,8 @@ static void sun4i_usb_phy0_id_vbus_det_scan(struct work_struct *work)
        if (id_notify) {
                extcon_set_cable_state_(data->extcon, EXTCON_USB_HOST,
                                        !id_det);
-               /*
-                * When a host cable gets unplugged (id == 1) on systems
-                * without vbus detection report vbus low for long enough to
-                * the musb-ip to end the current host session.
-                */
-               if (data->dr_mode == USB_DR_MODE_OTG &&
-                   !sun4i_usb_phy0_have_vbus_det(data) && id_det == 1) {
+               /* When leaving host mode force end the session here */
+               if (force_session_end && id_det == 1) {
                        mutex_lock(&phy0->mutex);
                        sun4i_usb_phy0_set_vbus_detect(phy0, 0);
                        msleep(1000);
@@ -534,8 +576,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
 {
        struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
 
-       if (args->args[0] < data->first_phy ||
-           args->args[0] >= data->cfg->num_phys)
+       if (args->args[0] >= data->cfg->num_phys)
                return ERR_PTR(-ENODEV);
 
        return data->phys[args->args[0]].phy;
@@ -577,7 +618,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
        if (!data)
                return -ENOMEM;
 
-       mutex_init(&data->mutex);
+       spin_lock_init(&data->reg_lock);
        INIT_DELAYED_WORK(&data->detect, sun4i_usb_phy0_id_vbus_det_scan);
        dev_set_drvdata(dev, data);
        data->cfg = of_device_get_match_data(dev);
@@ -610,33 +651,18 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
        }
 
        data->dr_mode = of_usb_get_dr_mode_by_phy(np, 0);
-       switch (data->dr_mode) {
-       case USB_DR_MODE_OTG:
-               /* otg without id_det makes no sense, and is not supported */
-               if (!data->id_det_gpio) {
-                       dev_err(dev, "usb0_id_det missing or invalid\n");
-                       return -ENODEV;
-               }
-               /* fall through */
-       case USB_DR_MODE_HOST:
-       case USB_DR_MODE_PERIPHERAL:
-               data->extcon = devm_extcon_dev_allocate(dev,
-                                                       sun4i_usb_phy0_cable);
-               if (IS_ERR(data->extcon))
-                       return PTR_ERR(data->extcon);
 
-               ret = devm_extcon_dev_register(dev, data->extcon);
-               if (ret) {
-                       dev_err(dev, "failed to register extcon: %d\n", ret);
-                       return ret;
-               }
-               break;
-       default:
-               dev_info(dev, "dr_mode unknown, not registering usb phy0\n");
-               data->first_phy = 1;
+       data->extcon = devm_extcon_dev_allocate(dev, sun4i_usb_phy0_cable);
+       if (IS_ERR(data->extcon))
+               return PTR_ERR(data->extcon);
+
+       ret = devm_extcon_dev_register(dev, data->extcon);
+       if (ret) {
+               dev_err(dev, "failed to register extcon: %d\n", ret);
+               return ret;
        }
 
-       for (i = data->first_phy; i < data->cfg->num_phys; i++) {
+       for (i = 0; i < data->cfg->num_phys; i++) {
                struct sun4i_usb_phy *phy = data->phys + i;
                char name[16];
 
@@ -737,6 +763,7 @@ static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = {
        .disc_thresh = 3,
        .phyctl_offset = REG_PHYCTL_A10,
        .dedicated_clocks = false,
+       .enable_pmu_unk1 = false,
 };
 
 static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
@@ -745,6 +772,7 @@ static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = {
        .disc_thresh = 2,
        .phyctl_offset = REG_PHYCTL_A10,
        .dedicated_clocks = false,
+       .enable_pmu_unk1 = false,
 };
 
 static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
@@ -753,6 +781,7 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = {
        .disc_thresh = 3,
        .phyctl_offset = REG_PHYCTL_A10,
        .dedicated_clocks = true,
+       .enable_pmu_unk1 = false,
 };
 
 static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
@@ -761,6 +790,7 @@ static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = {
        .disc_thresh = 2,
        .phyctl_offset = REG_PHYCTL_A10,
        .dedicated_clocks = false,
+       .enable_pmu_unk1 = false,
 };
 
 static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
@@ -769,6 +799,7 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = {
        .disc_thresh = 3,
        .phyctl_offset = REG_PHYCTL_A10,
        .dedicated_clocks = true,
+       .enable_pmu_unk1 = false,
 };
 
 static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
@@ -777,6 +808,7 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = {
        .disc_thresh = 3,
        .phyctl_offset = REG_PHYCTL_A33,
        .dedicated_clocks = true,
+       .enable_pmu_unk1 = false,
 };
 
 static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
@@ -784,6 +816,16 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = {
        .type = sun8i_h3_phy,
        .disc_thresh = 3,
        .dedicated_clocks = true,
+       .enable_pmu_unk1 = true,
+};
+
+static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = {
+       .num_phys = 2,
+       .type = sun50i_a64_phy,
+       .disc_thresh = 3,
+       .phyctl_offset = REG_PHYCTL_A33,
+       .dedicated_clocks = true,
+       .enable_pmu_unk1 = true,
 };
 
 static const struct of_device_id sun4i_usb_phy_of_match[] = {
@@ -794,6 +836,8 @@ static const struct of_device_id sun4i_usb_phy_of_match[] = {
        { .compatible = "allwinner,sun8i-a23-usb-phy", .data = &sun8i_a23_cfg },
        { .compatible = "allwinner,sun8i-a33-usb-phy", .data = &sun8i_a33_cfg },
        { .compatible = "allwinner,sun8i-h3-usb-phy", .data = &sun8i_h3_cfg },
+       { .compatible = "allwinner,sun50i-a64-usb-phy",
+         .data = &sun50i_a64_cfg},
        { },
 };
 MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
index d9b10a39a2cf77c6f67b3655b3a1f56143f0083e..87e6334eab9309f85e8cb5308f96dec54de7f495 100644 (file)
@@ -172,6 +172,7 @@ struct twl4030_usb {
        int                     irq;
        enum musb_vbus_id_status linkstat;
        bool                    vbus_supplied;
+       bool                    musb_mailbox_pending;
 
        struct delayed_work     id_workaround_work;
 };
@@ -439,6 +440,17 @@ static int __maybe_unused twl4030_usb_runtime_resume(struct device *dev)
                          (PHY_CLK_CTRL_CLOCKGATING_EN |
                           PHY_CLK_CTRL_CLK32K_EN));
 
+       twl4030_i2c_access(twl, 1);
+       twl4030_usb_set_mode(twl, twl->usb_mode);
+       if (twl->usb_mode == T2_USB_MODE_ULPI)
+               twl4030_i2c_access(twl, 0);
+       /*
+        * According to the TPS65950 TRM, there has to be at least 50ms
+        * delay between setting POWER_CTRL_OTG_ENAB and enabling charging
+        * so wait here so that a fully enabled phy can be expected after
+        * resume
+        */
+       msleep(50);
        return 0;
 }
 
@@ -459,11 +471,6 @@ static int twl4030_phy_power_on(struct phy *phy)
 
        dev_dbg(twl->dev, "%s\n", __func__);
        pm_runtime_get_sync(twl->dev);
-       twl4030_i2c_access(twl, 1);
-       twl4030_usb_set_mode(twl, twl->usb_mode);
-       if (twl->usb_mode == T2_USB_MODE_ULPI)
-               twl4030_i2c_access(twl, 0);
-       twl->linkstat = MUSB_UNKNOWN;
        schedule_delayed_work(&twl->id_workaround_work, HZ);
 
        return 0;
@@ -569,9 +576,12 @@ static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
                        pm_runtime_mark_last_busy(twl->dev);
                        pm_runtime_put_autosuspend(twl->dev);
                }
+               twl->musb_mailbox_pending = true;
+       }
+       if (twl->musb_mailbox_pending) {
                err = musb_mailbox(status);
-               if (err)
-                       twl->linkstat = MUSB_UNKNOWN;
+               if (!err)
+                       twl->musb_mailbox_pending = false;
        }
 
        /* don't schedule during sleep - irq works right then */
@@ -676,6 +686,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        twl->irq                = platform_get_irq(pdev, 0);
        twl->vbus_supplied      = false;
        twl->linkstat           = MUSB_UNKNOWN;
+       twl->musb_mailbox_pending = false;
 
        twl->phy.dev            = twl->dev;
        twl->phy.label          = "twl4030";
index ec83dfdbc206639da18166313232597d9d179063..873424ab0e3284cd3eaaeb01560e6451e199ff67 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/phy/phy.h>
+#include <linux/phy/tegra/xusb.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/consumer.h>
 #include <linux/reset.h>
@@ -101,7 +102,8 @@ tegra_xusb_pad_find_phy_node(struct tegra_xusb_pad *pad, unsigned int index)
        return of_find_node_by_name(np, pad->soc->lanes[index].name);
 }
 
-int tegra_xusb_lane_lookup_function(struct tegra_xusb_lane *lane,
+static int
+tegra_xusb_lane_lookup_function(struct tegra_xusb_lane *lane,
                                    const char *function)
 {
        unsigned int i;
index 5c35f9d1822c96ef962c6b04e87e1ccbb94387d8..2b9f15156115f0cf134d2ae70d56619dcd35ede5 100644 (file)
@@ -249,7 +249,6 @@ extern int extcon_set_state(struct extcon_dev *edev, unsigned int id,
                                   bool cable_state);
 extern int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id,
                                bool cable_state);
-
 /*
  * Synchronize the state and property data for a specific external connector.
  */
@@ -359,10 +358,27 @@ static inline int extcon_set_state_sync(struct extcon_dev *edev, unsigned int id
 }
 
 static inline int extcon_sync(struct extcon_dev *edev, unsigned int id)
+<<<<<<< HEAD
+=======
+{
+       return 0;
+}
+
+static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop,
+                                       union extcon_property_value *prop_val)
+{
+       return 0;
+}
+static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id,
+                                       unsigned int prop,
+                                       union extcon_property_value prop_val)
+>>>>>>> next
 {
        return 0;
 }
 
+<<<<<<< HEAD
 static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id,
                                        unsigned int prop,
                                        union extcon_property_value *prop_val)
@@ -371,24 +387,36 @@ static inline int extcon_get_property(struct extcon_dev *edev, unsigned int id,
 }
 static inline int extcon_set_property(struct extcon_dev *edev, unsigned int id,
                                        unsigned int prop,
+=======
+static inline int extcon_set_property_sync(struct extcon_dev *edev,
+                                       unsigned int id, unsigned int prop,
+>>>>>>> next
                                        union extcon_property_value prop_val)
 {
        return 0;
 }
 
+<<<<<<< HEAD
 static inline int extcon_set_property_sync(struct extcon_dev *edev,
                                        unsigned int id, unsigned int prop,
                                        union extcon_property_value prop_val)
+=======
+static inline int extcon_get_property_capability(struct extcon_dev *edev,
+                                       unsigned int id, unsigned int prop)
+>>>>>>> next
 {
        return 0;
 }
 
+<<<<<<< HEAD
 static inline int extcon_get_property_capability(struct extcon_dev *edev,
                                        unsigned int id, unsigned int prop)
 {
        return 0;
 }
 
+=======
+>>>>>>> next
 static inline int extcon_set_property_capability(struct extcon_dev *edev,
                                        unsigned int id, unsigned int prop)
 {
index f08b67238b58d93bcb46447122f49281d45d53c0..ee1bed7dbfc634c5e490e00b694b5b170087decd 100644 (file)
@@ -36,6 +36,7 @@ enum phy_mode {
  * @power_on: powering on the phy
  * @power_off: powering off the phy
  * @set_mode: set the mode of the phy
+ * @reset: resetting the phy
  * @owner: the module owner containing the ops
  */
 struct phy_ops {
@@ -44,6 +45,7 @@ struct phy_ops {
        int     (*power_on)(struct phy *phy);
        int     (*power_off)(struct phy *phy);
        int     (*set_mode)(struct phy *phy, enum phy_mode mode);
+       int     (*reset)(struct phy *phy);
        struct module *owner;
 };
 
@@ -136,6 +138,7 @@ int phy_exit(struct phy *phy);
 int phy_power_on(struct phy *phy);
 int phy_power_off(struct phy *phy);
 int phy_set_mode(struct phy *phy, enum phy_mode mode);
+int phy_reset(struct phy *phy);
 static inline int phy_get_bus_width(struct phy *phy)
 {
        return phy->attrs.bus_width;