phy: backport generic phy framework from kernel-3.18
authorlyz <lyz@rock-chips.com>
Tue, 9 Dec 2014 12:31:32 +0000 (20:31 +0800)
committerlyz <lyz@rock-chips.com>
Tue, 9 Dec 2014 12:36:43 +0000 (20:36 +0800)
54 files changed:
Documentation/devicetree/bindings/phy/apm-xgene-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/bcm-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/berlin-sata-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/hix5hd2-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-bindings.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-miphy365x.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-stih407-usb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/phy-stih41x-usb.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/samsung-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/st-spear-miphy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/ti-phy.txt [new file with mode: 0644]
drivers/Kconfig
drivers/Makefile
drivers/phy/Kconfig [new file with mode: 0644]
drivers/phy/Makefile [new file with mode: 0644]
drivers/phy/phy-bcm-kona-usb2.c [new file with mode: 0644]
drivers/phy/phy-berlin-sata.c [new file with mode: 0644]
drivers/phy/phy-core.c [new file with mode: 0644]
drivers/phy/phy-exynos-dp-video.c [new file with mode: 0644]
drivers/phy/phy-exynos-mipi-video.c [new file with mode: 0644]
drivers/phy/phy-exynos4210-usb2.c [new file with mode: 0644]
drivers/phy/phy-exynos4x12-usb2.c [new file with mode: 0644]
drivers/phy/phy-exynos5-usbdrd.c [new file with mode: 0644]
drivers/phy/phy-exynos5250-sata.c [new file with mode: 0644]
drivers/phy/phy-exynos5250-usb2.c [new file with mode: 0644]
drivers/phy/phy-hix5hd2-sata.c [new file with mode: 0644]
drivers/phy/phy-miphy365x.c [new file with mode: 0644]
drivers/phy/phy-mvebu-sata.c [new file with mode: 0644]
drivers/phy/phy-omap-control.c [new file with mode: 0644]
drivers/phy/phy-omap-usb2.c [new file with mode: 0644]
drivers/phy/phy-qcom-apq8064-sata.c [new file with mode: 0644]
drivers/phy/phy-qcom-ipq806x-sata.c [new file with mode: 0644]
drivers/phy/phy-rcar-gen2.c [new file with mode: 0644]
drivers/phy/phy-rockchip-usb.c [new file with mode: 0644]
drivers/phy/phy-s5pv210-usb2.c [new file with mode: 0644]
drivers/phy/phy-samsung-usb2.c [new file with mode: 0644]
drivers/phy/phy-samsung-usb2.h [new file with mode: 0644]
drivers/phy/phy-spear1310-miphy.c [new file with mode: 0644]
drivers/phy/phy-spear1340-miphy.c [new file with mode: 0644]
drivers/phy/phy-stih407-usb.c [new file with mode: 0644]
drivers/phy/phy-stih41x-usb.c [new file with mode: 0644]
drivers/phy/phy-sun4i-usb.c [new file with mode: 0644]
drivers/phy/phy-ti-pipe3.c [new file with mode: 0644]
drivers/phy/phy-twl4030-usb.c [new file with mode: 0644]
drivers/phy/phy-xgene.c [new file with mode: 0644]
include/linux/phy/omap_control_phy.h [new file with mode: 0644]
include/linux/phy/omap_usb.h [new file with mode: 0644]
include/linux/phy/phy.h [new file with mode: 0644]

diff --git a/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt b/Documentation/devicetree/bindings/phy/apm-xgene-phy.txt
new file mode 100644 (file)
index 0000000..5f3a65a
--- /dev/null
@@ -0,0 +1,79 @@
+* APM X-Gene 15Gbps Multi-purpose PHY nodes
+
+PHY nodes are defined to describe on-chip 15Gbps Multi-purpose PHY. Each
+PHY (pair of lanes) has its own node.
+
+Required properties:
+- compatible           : Shall be "apm,xgene-phy".
+- reg                  : PHY memory resource is the SDS PHY access resource.
+- #phy-cells           : Shall be 1 as it expects one argument for setting
+                         the mode of the PHY. Possible values are 0 (SATA),
+                         1 (SGMII), 2 (PCIe), 3 (USB), and 4 (XFI).
+
+Optional properties:
+- status               : Shall be "ok" if enabled or "disabled" if disabled.
+                         Default is "ok".
+- clocks               : Reference to the clock entry.
+- apm,tx-eye-tuning    : Manual control to fine tune the capture of the serial
+                         bit lines from the automatic calibrated position.
+                         Two set of 3-tuple setting for each (up to 3)
+                         supported link speed on the host. Range from 0 to
+                         127 in unit of one bit period. Default is 10.
+- apm,tx-eye-direction : Eye tuning manual control direction. 0 means sample
+                         data earlier than the nominal sampling point. 1 means
+                         sample data later than the nominal sampling point.
+                         Two set of 3-tuple setting for each (up to 3)
+                         supported link speed on the host. Default is 0.
+- apm,tx-boost-gain    : Frequency boost AC (LSB 3-bit) and DC (2-bit)
+                         gain control. Two set of 3-tuple setting for each
+                         (up to 3) supported link speed on the host. Range is
+                         between 0 to 31 in unit of dB. Default is 3.
+- apm,tx-amplitude     : Amplitude control. Two set of 3-tuple setting for
+                         each (up to 3) supported link speed on the host.
+                         Range is between 0 to 199500 in unit of uV.
+                         Default is 199500 uV.
+- apm,tx-pre-cursor1   : 1st pre-cursor emphasis taps control. Two set of
+                         3-tuple setting for each (up to 3) supported link
+                         speed on the host. Range is 0 to 273000 in unit of
+                         uV. Default is 0.
+- apm,tx-pre-cursor2   : 2st pre-cursor emphasis taps control. Two set of
+                         3-tuple setting for each (up to 3) supported link
+                         speed on the host. Range is 0 to 127400 in unit uV.
+                         Default is 0x0.
+- apm,tx-post-cursor   : Post-cursor emphasis taps control. Two set of
+                         3-tuple setting for Gen1, Gen2, and Gen3. Range is
+                         between 0 to 0x1f in unit of 18.2mV. Default is 0xf.
+- apm,tx-speed         : Tx operating speed. One set of 3-tuple for each
+                         supported link speed on the host.
+                          0 = 1-2Gbps
+                          1 = 2-4Gbps (1st tuple default)
+                          2 = 4-8Gbps
+                          3 = 8-15Gbps (2nd tuple default)
+                          4 = 2.5-4Gbps
+                          5 = 4-5Gbps
+                          6 = 5-6Gbps
+                          7 = 6-16Gbps (3rd tuple default)
+
+NOTE: PHY override parameters are board specific setting.
+
+Example:
+               phy1: phy@1f21a000 {
+                       compatible = "apm,xgene-phy";
+                       reg = <0x0 0x1f21a000 0x0 0x100>;
+                       #phy-cells = <1>;
+                       status = "disabled";
+               };
+
+               phy2: phy@1f22a000 {
+                       compatible = "apm,xgene-phy";
+                       reg = <0x0 0x1f22a000 0x0 0x100>;
+                       #phy-cells = <1>;
+                       status = "ok";
+               };
+
+               phy3: phy@1f23a000 {
+                       compatible = "apm,xgene-phy";
+                       reg = <0x0 0x1f23a000 0x0 0x100>;
+                       #phy-cells = <1>;
+                       status = "ok";
+               };
diff --git a/Documentation/devicetree/bindings/phy/bcm-phy.txt b/Documentation/devicetree/bindings/phy/bcm-phy.txt
new file mode 100644 (file)
index 0000000..3dc8b3d
--- /dev/null
@@ -0,0 +1,15 @@
+BROADCOM KONA USB2 PHY
+
+Required properties:
+ - compatible: brcm,kona-usb2-phy
+ - reg: offset and length of the PHY registers
+ - #phy-cells: must be 0
+Refer to phy/phy-bindings.txt for the generic PHY binding properties
+
+Example:
+
+       usbphy: usb-phy@3f130000 {
+               compatible = "brcm,kona-usb2-phy";
+               reg = <0x3f130000 0x28>;
+               #phy-cells = <0>;
+       };
diff --git a/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt b/Documentation/devicetree/bindings/phy/berlin-sata-phy.txt
new file mode 100644 (file)
index 0000000..88f8c23
--- /dev/null
@@ -0,0 +1,34 @@
+Berlin SATA PHY
+---------------
+
+Required properties:
+- compatible: should be "marvell,berlin2q-sata-phy"
+- address-cells: should be 1
+- size-cells: should be 0
+- phy-cells: from the generic PHY bindings, must be 1
+- reg: address and length of the register
+- clocks: reference to the clock entry
+
+Sub-nodes:
+Each PHY should be represented as a sub-node.
+
+Sub-nodes required properties:
+- reg: the PHY number
+
+Example:
+       sata_phy: phy@f7e900a0 {
+               compatible = "marvell,berlin2q-sata-phy";
+               reg = <0xf7e900a0 0x200>;
+               clocks = <&chip CLKID_SATA>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               #phy-cells = <1>;
+
+               sata-phy@0 {
+                       reg = <0>;
+               };
+
+               sata-phy@1 {
+                       reg = <1>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt b/Documentation/devicetree/bindings/phy/hix5hd2-phy.txt
new file mode 100644 (file)
index 0000000..296168b
--- /dev/null
@@ -0,0 +1,22 @@
+Hisilicon hix5hd2 SATA PHY
+-----------------------
+
+Required properties:
+- compatible: should be "hisilicon,hix5hd2-sata-phy"
+- reg: offset and length of the PHY registers
+- #phy-cells: must be 0
+Refer to phy/phy-bindings.txt for the generic PHY binding properties
+
+Optional Properties:
+- hisilicon,peripheral-syscon: phandle of syscon used to control peripheral.
+- hisilicon,power-reg: offset and bit number within peripheral-syscon,
+       register of controlling sata power supply.
+
+Example:
+       sata_phy: phy@f9900000 {
+               compatible = "hisilicon,hix5hd2-sata-phy";
+               reg = <0xf9900000 0x10000>;
+               #phy-cells = <0>;
+               hisilicon,peripheral-syscon = <&peripheral_ctrl>;
+               hisilicon,power-reg = <0x8 10>;
+       };
diff --git a/Documentation/devicetree/bindings/phy/phy-bindings.txt b/Documentation/devicetree/bindings/phy/phy-bindings.txt
new file mode 100644 (file)
index 0000000..1293c32
--- /dev/null
@@ -0,0 +1,70 @@
+This document explains only the device tree data binding. For general
+information about PHY subsystem refer to Documentation/phy.txt
+
+PHY device node
+===============
+
+Required Properties:
+#phy-cells:    Number of cells in a PHY specifier;  The meaning of all those
+               cells is defined by the binding for the phy node. The PHY
+               provider can use the values in cells to find the appropriate
+               PHY.
+
+Optional Properties:
+phy-supply:    Phandle to a regulator that provides power to the PHY. This
+               regulator will be managed during the PHY power on/off sequence.
+
+For example:
+
+phys: phy {
+    compatible = "xxx";
+    reg = <...>;
+    .
+    .
+    #phy-cells = <1>;
+    .
+    .
+};
+
+That node describes an IP block (PHY provider) that implements 2 different PHYs.
+In order to differentiate between these 2 PHYs, an additional specifier should be
+given while trying to get a reference to it.
+
+PHY user node
+=============
+
+Required Properties:
+phys : the phandle for the PHY device (used by the PHY subsystem)
+phy-names : the names of the PHY corresponding to the PHYs present in the
+           *phys* phandle
+
+Example 1:
+usb1: usb_otg_ss@xxx {
+    compatible = "xxx";
+    reg = <xxx>;
+    .
+    .
+    phys = <&usb2_phy>, <&usb3_phy>;
+    phy-names = "usb2phy", "usb3phy";
+    .
+    .
+};
+
+This node represents a controller that uses two PHYs, one for usb2 and one for
+usb3.
+
+Example 2:
+usb2: usb_otg_ss@xxx {
+    compatible = "xxx";
+    reg = <xxx>;
+    .
+    .
+    phys = <&phys 1>;
+    phy-names = "usbphy";
+    .
+    .
+};
+
+This node represents a controller that uses one of the PHYs of the PHY provider
+device defined previously. Note that the phy handle has an additional specifier
+"1" to differentiate between the two PHYs.
diff --git a/Documentation/devicetree/bindings/phy/phy-miphy365x.txt b/Documentation/devicetree/bindings/phy/phy-miphy365x.txt
new file mode 100644 (file)
index 0000000..42c8808
--- /dev/null
@@ -0,0 +1,76 @@
+STMicroelectronics STi MIPHY365x PHY binding
+============================================
+
+This binding describes a miphy device that is used to control PHY hardware
+for SATA and PCIe.
+
+Required properties (controller (parent) node):
+- compatible    : Should be "st,miphy365x-phy"
+- st,syscfg     : Should be a phandle of the system configuration register group
+                 which contain the SATA, PCIe mode setting bits
+
+Required nodes :  A sub-node is required for each channel the controller
+                  provides. Address range information including the usual
+                  'reg' and 'reg-names' properties are used inside these
+                  nodes to describe the controller's topology. These nodes
+                  are translated by the driver's .xlate() function.
+
+Required properties (port (child) node):
+- #phy-cells   : Should be 1 (See second example)
+                 Cell after port phandle is device type from:
+                       - MIPHY_TYPE_SATA
+                       - MIPHY_TYPE_PCI
+- reg          : Address and length of register sets for each device in
+                 "reg-names"
+- reg-names     : The names of the register addresses corresponding to the
+                 registers filled in "reg":
+                       - sata:   For SATA devices
+                       - pcie:   For PCIe devices
+                       - syscfg: To specify the syscfg based config register
+
+Optional properties (port (child) node):
+- st,sata-gen       :  Generation of locally attached SATA IP. Expected values
+                       are {1,2,3). If not supplied generation 1 hardware will
+                       be expected
+- st,pcie-tx-pol-inv : Bool property to invert the polarity PCIe Tx (Txn/Txp)
+- st,sata-tx-pol-inv : Bool property to invert the polarity SATA Tx (Txn/Txp)
+
+Example:
+
+       miphy365x_phy: miphy365x@fe382000 {
+               compatible      = "st,miphy365x-phy";
+               st,syscfg       = <&syscfg_rear>;
+               #address-cells  = <1>;
+               #size-cells     = <1>;
+               ranges;
+
+               phy_port0: port@fe382000 {
+                       reg = <0xfe382000 0x100>, <0xfe394000 0x100>, <0x824 0x4>;
+                       reg-names = "sata", "pcie", "syscfg";
+                       #phy-cells = <1>;
+                       st,sata-gen = <3>;
+               };
+
+               phy_port1: port@fe38a000 {
+                       reg = <0xfe38a000 0x100>, <0xfe804000 0x100>, <0x828 0x4>;;
+                       reg-names = "sata", "pcie", "syscfg";
+                       #phy-cells = <1>;
+                       st,pcie-tx-pol-inv;
+               };
+       };
+
+Specifying phy control of devices
+=================================
+
+Device nodes should specify the configuration required in their "phys"
+property, containing a phandle to the phy port node and a device type.
+
+Example:
+
+#include <dt-bindings/phy/phy-miphy365x.h>
+
+       sata0: sata@fe380000 {
+               ...
+               phys      = <&phy_port0 MIPHY_TYPE_SATA>;
+               ...
+       };
diff --git a/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt b/Documentation/devicetree/bindings/phy/phy-stih407-usb.txt
new file mode 100644 (file)
index 0000000..1ef8228
--- /dev/null
@@ -0,0 +1,30 @@
+ST STiH407 USB PHY controller
+
+This file documents the dt bindings for the usb picoPHY driver which is the PHY for both USB2 and USB3
+host controllers (when controlling usb2/1.1 devices) available on STiH407 SoC family from STMicroelectronics.
+
+Required properties:
+- compatible           : should be "st,stih407-usb2-phy"
+- reg                  : contain the offset and length of the system configuration registers
+                         used as glue logic to control & parameter phy
+- reg-names            : the names of the system configuration registers in "reg", should be "param" and "reg"
+- st,syscfg            : sysconfig register to manage phy parameter at driver level
+- resets               : list of phandle and reset specifier pairs. There should be two entries, one
+                         for the whole phy and one for the port
+- reset-names          : list of reset signal names. Should be "global" and "port"
+See: Documentation/devicetree/bindings/reset/st,sti-powerdown.txt
+See: Documentation/devicetree/bindings/reset/reset.txt
+
+Example:
+
+usb2_picophy0: usbpicophy@f8 {
+       compatible      = "st,stih407-usb2-phy";
+       reg             = <0xf8 0x04>,  /* syscfg 5062 */
+                         <0xf4 0x04>;  /* syscfg 5061 */
+       reg-names       = "param", "ctrl";
+       #phy-cells      = <0>;
+       st,syscfg       = <&syscfg_core>;
+       resets          = <&softreset STIH407_PICOPHY_SOFTRESET>,
+                         <&picophyreset STIH407_PICOPHY0_RESET>;
+       reset-names     = "global", "port";
+};
diff --git a/Documentation/devicetree/bindings/phy/phy-stih41x-usb.txt b/Documentation/devicetree/bindings/phy/phy-stih41x-usb.txt
new file mode 100644 (file)
index 0000000..00944a0
--- /dev/null
@@ -0,0 +1,24 @@
+STMicroelectronics STiH41x USB PHY binding
+------------------------------------------
+
+This file contains documentation for the usb phy found in STiH415/6 SoCs from
+STMicroelectronics.
+
+Required properties:
+- compatible   : should be "st,stih416-usb-phy" or "st,stih415-usb-phy"
+- st,syscfg    : should be a phandle of the syscfg node
+- clock-names  : must contain "osc_phy"
+- clocks       : must contain an entry for each name in clock-names.
+See: Documentation/devicetree/bindings/clock/clock-bindings.txt
+- #phy-cells   : must be 0 for this phy
+See: Documentation/devicetree/bindings/phy/phy-bindings.txt
+
+Example:
+
+usb2_phy: usb2phy@0 {
+       compatible      = "st,stih416-usb-phy";
+       #phy-cell       = <0>;
+       st,syscfg       = <&syscfg_rear>;
+       clocks          = <&clk_sysin>;
+       clock-names     = "osc_phy";
+};
diff --git a/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-apq8064-sata-phy.txt
new file mode 100644 (file)
index 0000000..952f6c9
--- /dev/null
@@ -0,0 +1,24 @@
+Qualcomm APQ8064 SATA PHY Controller
+------------------------------------
+
+SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
+Each SATA PHY controller should have its own node.
+
+Required properties:
+- compatible: compatible list, contains "qcom,apq8064-sata-phy".
+- reg: offset and length of the SATA PHY register set;
+- #phy-cells: must be zero
+- clocks: a list of phandles and clock-specifier pairs, one for each entry in
+  clock-names.
+- clock-names: must be "cfg" for phy config clock.
+
+Example:
+       sata_phy: sata-phy@1b400000 {
+               compatible = "qcom,apq8064-sata-phy";
+               reg = <0x1b400000 0x200>;
+
+               clocks = <&gcc SATA_PHY_CFG_CLK>;
+               clock-names = "cfg";
+
+               #phy-cells = <0>;
+       };
diff --git a/Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt b/Documentation/devicetree/bindings/phy/qcom-dwc3-usb-phy.txt
new file mode 100644 (file)
index 0000000..86f2dbe
--- /dev/null
@@ -0,0 +1,39 @@
+Qualcomm DWC3 HS AND SS PHY CONTROLLER
+--------------------------------------
+
+DWC3 PHY nodes are defined to describe on-chip Synopsis Physical layer
+controllers.  Each DWC3 PHY controller should have its own node.
+
+Required properties:
+- compatible: should contain one of the following:
+       - "qcom,dwc3-hs-usb-phy" for High Speed Synopsis PHY controller
+       - "qcom,dwc3-ss-usb-phy" for Super Speed Synopsis PHY controller
+- reg: offset and length of the DWC3 PHY controller register set
+- #phy-cells: must be zero
+- clocks: a list of phandles and clock-specifier pairs, one for each entry in
+  clock-names.
+- clock-names: Should contain "ref" for the PHY reference clock
+
+Optional clocks:
+  "xo"         External reference clock
+
+Example:
+               phy@100f8800 {
+                       compatible = "qcom,dwc3-hs-usb-phy";
+                       reg = <0x100f8800 0x30>;
+                       clocks = <&gcc USB30_0_UTMI_CLK>;
+                       clock-names = "ref";
+                       #phy-cells = <0>;
+
+                       status = "ok";
+               };
+
+               phy@100f8830 {
+                       compatible = "qcom,dwc3-ss-usb-phy";
+                       reg = <0x100f8830 0x30>;
+                       clocks = <&gcc USB30_0_MASTER_CLK>;
+                       clock-names = "ref";
+                       #phy-cells = <0>;
+
+                       status = "ok";
+               };
diff --git a/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt b/Documentation/devicetree/bindings/phy/qcom-ipq806x-sata-phy.txt
new file mode 100644 (file)
index 0000000..76bfbd0
--- /dev/null
@@ -0,0 +1,23 @@
+Qualcomm IPQ806x SATA PHY Controller
+------------------------------------
+
+SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
+Each SATA PHY controller should have its own node.
+
+Required properties:
+- compatible: compatible list, contains "qcom,ipq806x-sata-phy"
+- reg: offset and length of the SATA PHY register set;
+- #phy-cells: must be zero
+- clocks: must be exactly one entry
+- clock-names: must be "cfg"
+
+Example:
+       sata_phy: sata-phy@1b400000 {
+               compatible = "qcom,ipq806x-sata-phy";
+               reg = <0x1b400000 0x200>;
+
+               clocks = <&gcc SATA_PHY_CFG_CLK>;
+               clock-names = "cfg";
+
+               #phy-cells = <0>;
+       };
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt b/Documentation/devicetree/bindings/phy/rcar-gen2-phy.txt
new file mode 100644 (file)
index 0000000..00fc52a
--- /dev/null
@@ -0,0 +1,51 @@
+* Renesas R-Car generation 2 USB PHY
+
+This file provides information on what the device node for the R-Car generation
+2 USB PHY contains.
+
+Required properties:
+- compatible: "renesas,usb-phy-r8a7790" if the device is a part of R8A7790 SoC.
+             "renesas,usb-phy-r8a7791" if the device is a part of R8A7791 SoC.
+- reg: offset and length of the register block.
+- #address-cells: number of address cells for the USB channel subnodes, must
+                 be <1>.
+- #size-cells: number of size cells for the USB channel subnodes, must be <0>.
+- clocks: clock phandle and specifier pair.
+- clock-names: string, clock input name, must be "usbhs".
+
+The USB PHY device tree node should have the subnodes corresponding to the USB
+channels. These subnodes must contain the following properties:
+- reg: the USB controller selector; see the table below for the values.
+- #phy-cells: see phy-bindings.txt in the same directory, must be <1>.
+
+The phandle's argument in the PHY specifier is the USB controller selector for
+the USB channel; see the selector meanings below:
+
++-----------+---------------+---------------+
+|\ Selector |               |               |
++ --------- +       0       |       1       |
+| Channel  \|               |               |
++-----------+---------------+---------------+
+| 0         | PCI EHCI/OHCI | HS-USB        |
+| 2         | PCI EHCI/OHCI | xHCI          |
++-----------+---------------+---------------+
+
+Example (Lager board):
+
+       usb-phy@e6590100 {
+               compatible = "renesas,usb-phy-r8a7790";
+               reg = <0 0xe6590100 0 0x100>;
+               #address-cells = <1>;
+               #size-cells = <0>;
+               clocks = <&mstp7_clks R8A7790_CLK_HSUSB>;
+               clock-names = "usbhs";
+
+               usb-channel@0 {
+                       reg = <0>;
+                       #phy-cells = <1>;
+               };
+               usb-channel@2 {
+                       reg = <2>;
+                       #phy-cells = <1>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt
new file mode 100644 (file)
index 0000000..18ccc2f
--- /dev/null
@@ -0,0 +1,17 @@
+ROCKCHIP USB2 PHY
+
+Required properties:
+ - compatible: rockchip,rk3288-usb-phy
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - #phy-cells: must be 1
+Refer to phy/phy-bindings.txt for the generic PHY binding
+properties
+
+Example:
+
+       usbphy: phy {
+       #phy-cells = <1>;
+       compatible = "rockchip,rk3288-usb-phy";
+       rockchip,grf = <&grf>;
+       };
diff --git a/Documentation/devicetree/bindings/phy/samsung-phy.txt b/Documentation/devicetree/bindings/phy/samsung-phy.txt
new file mode 100644 (file)
index 0000000..15e0f2c
--- /dev/null
@@ -0,0 +1,169 @@
+Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be "samsung,s5pv210-mipi-video-phy";
+- reg : offset and length of the MIPI DPHY register set;
+- #phy-cells : from the generic phy bindings, must be 1;
+
+For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in
+the PHY specifier identifies the PHY and its meaning is as follows:
+  0 - MIPI CSIS 0,
+  1 - MIPI DSIM 0,
+  2 - MIPI CSIS 1,
+  3 - MIPI DSIM 1.
+
+Samsung EXYNOS SoC series Display Port PHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be one of the following supported values:
+        - "samsung,exynos5250-dp-video-phy"
+        - "samsung,exynos5420-dp-video-phy"
+- samsung,pmu-syscon: phandle for PMU system controller interface, used to
+                     control pmu registers for power isolation.
+- #phy-cells : from the generic PHY bindings, must be 0;
+
+Samsung S5P/EXYNOS SoC series USB PHY
+-------------------------------------------------
+
+Required properties:
+- compatible : should be one of the listed compatibles:
+       - "samsung,exynos3250-usb2-phy"
+       - "samsung,exynos4210-usb2-phy"
+       - "samsung,exynos4x12-usb2-phy"
+       - "samsung,exynos5250-usb2-phy"
+       - "samsung,s5pv210-usb2-phy"
+- reg : a list of registers used by phy driver
+       - first and obligatory is the location of phy modules registers
+- samsung,sysreg-phandle - handle to syscon used to control the system registers
+- samsung,pmureg-phandle - handle to syscon used to control PMU registers
+- #phy-cells : from the generic phy bindings, must be 1;
+- clocks and clock-names:
+       - the "phy" clock is required by the phy module, used as a gate
+       - the "ref" clock is used to get the rate of the clock provided to the
+         PHY module
+
+The first phandle argument in the PHY specifier identifies the PHY, its
+meaning is compatible dependent. For the currently supported SoCs (Exynos 4210
+and Exynos 4212) it is as follows:
+  0 - USB device ("device"),
+  1 - USB host ("host"),
+  2 - HSIC0 ("hsic0"),
+  3 - HSIC1 ("hsic1"),
+Exynos3250 has only USB device phy available as phy 0.
+
+Exynos 4210 and Exynos 4212 use mode switching and require that mode switch
+register is supplied.
+
+Example:
+
+For Exynos 4412 (compatible with Exynos 4212):
+
+usbphy: phy@125b0000 {
+       compatible = "samsung,exynos4x12-usb2-phy";
+       reg = <0x125b0000 0x100>;
+       clocks = <&clock 305>, <&clock 2>;
+       clock-names = "phy", "ref";
+       status = "okay";
+       #phy-cells = <1>;
+       samsung,sysreg-phandle = <&sys_reg>;
+       samsung,pmureg-phandle = <&pmu_reg>;
+};
+
+Then the PHY can be used in other nodes such as:
+
+phy-consumer@12340000 {
+       phys = <&usbphy 2>;
+       phy-names = "phy";
+};
+
+Refer to DT bindings documentation of particular PHY consumer devices for more
+information about required PHYs and the way of specification.
+
+Samsung SATA PHY Controller
+---------------------------
+
+SATA PHY nodes are defined to describe on-chip SATA Physical layer controllers.
+Each SATA PHY controller should have its own node.
+
+Required properties:
+- compatible        : compatible list, contains "samsung,exynos5250-sata-phy"
+- reg : offset and length of the SATA PHY register set;
+- #phy-cells : must be zero
+- clocks : must be exactly one entry
+- clock-names : must be "sata_phyctrl"
+- samsung,exynos-sataphy-i2c-phandle : a phandle to the I2C device, no arguments
+- samsung,syscon-phandle : a phandle to the PMU system controller, no arguments
+
+Example:
+       sata_phy: sata-phy@12170000 {
+               compatible = "samsung,exynos5250-sata-phy";
+               reg = <0x12170000 0x1ff>;
+               clocks = <&clock 287>;
+               clock-names = "sata_phyctrl";
+               #phy-cells = <0>;
+               samsung,exynos-sataphy-i2c-phandle = <&sata_phy_i2c>;
+               samsung,syscon-phandle = <&pmu_syscon>;
+       };
+
+Device-Tree bindings for sataphy i2c client driver
+--------------------------------------------------
+
+Required properties:
+compatible: Should be "samsung,exynos-sataphy-i2c"
+- reg: I2C address of the sataphy i2c device.
+
+Example:
+
+       sata_phy_i2c:sata-phy@38 {
+               compatible = "samsung,exynos-sataphy-i2c";
+               reg = <0x38>;
+       };
+
+Samsung Exynos5 SoC series USB DRD PHY controller
+--------------------------------------------------
+
+Required properties:
+- compatible : Should be set to one of the following supported values:
+       - "samsung,exynos5250-usbdrd-phy" - for exynos5250 SoC,
+       - "samsung,exynos5420-usbdrd-phy" - for exynos5420 SoC.
+- reg : Register offset and length of USB DRD PHY register set;
+- clocks: Clock IDs array as required by the controller
+- clock-names: names of clocks correseponding to IDs in the clock property;
+              Required clocks:
+       - phy: main PHY clock (same as USB DRD controller i.e. DWC3 IP clock),
+              used for register access.
+       - ref: PHY's reference clock (usually crystal clock), used for
+              PHY operations, associated by phy name. It is used to
+              determine bit values for clock settings register.
+              For Exynos5420 this is given as 'sclk_usbphy30' in CMU.
+- samsung,pmu-syscon: phandle for PMU system controller interface, used to
+                     control pmu registers for power isolation.
+- #phy-cells : from the generic PHY bindings, must be 1;
+
+For "samsung,exynos5250-usbdrd-phy" and "samsung,exynos5420-usbdrd-phy"
+compatible PHYs, the second cell in the PHY specifier identifies the
+PHY id, which is interpreted as follows:
+  0 - UTMI+ type phy,
+  1 - PIPE3 type phy,
+
+Example:
+       usbdrd_phy: usbphy@12100000 {
+               compatible = "samsung,exynos5250-usbdrd-phy";
+               reg = <0x12100000 0x100>;
+               clocks = <&clock 286>, <&clock 1>;
+               clock-names = "phy", "ref";
+               samsung,pmu-syscon = <&pmu_system_controller>;
+               #phy-cells = <1>;
+       };
+
+- aliases: For SoCs like Exynos5420 having multiple USB 3.0 DRD PHY controllers,
+          'usbdrd_phy' nodes should have numbered alias in the aliases node,
+          in the form of usbdrdphyN, N = 0, 1... (depending on number of
+          controllers).
+Example:
+       aliases {
+               usbdrdphy0 = &usb3_phy0;
+               usbdrdphy1 = &usb3_phy1;
+       };
diff --git a/Documentation/devicetree/bindings/phy/st-spear-miphy.txt b/Documentation/devicetree/bindings/phy/st-spear-miphy.txt
new file mode 100644 (file)
index 0000000..2a6bfdc
--- /dev/null
@@ -0,0 +1,15 @@
+ST SPEAr miphy DT details
+=========================
+
+ST Microelectronics SPEAr miphy is a phy controller supporting PCIe and SATA.
+
+Required properties:
+- compatible : should be "st,spear1310-miphy" or "st,spear1340-miphy"
+- reg : offset and length of the PHY register set.
+- misc: phandle for the syscon node to access misc registers
+- #phy-cells : from the generic PHY bindings, must be 1.
+       - cell[1]: 0 if phy used for SATA, 1 for PCIe.
+
+Optional properties:
+- phy-id: Instance id of the phy. Only required when there are multiple phys
+  present on a implementation.
diff --git a/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt
new file mode 100644 (file)
index 0000000..16528b9
--- /dev/null
@@ -0,0 +1,37 @@
+Allwinner sun4i USB PHY
+-----------------------
+
+Required properties:
+- compatible : should be one of
+  * allwinner,sun4i-a10-usb-phy
+  * allwinner,sun5i-a13-usb-phy
+  * allwinner,sun6i-a31-usb-phy
+  * allwinner,sun7i-a20-usb-phy
+- reg : a list of offset + length pairs
+- reg-names :
+  * "phy_ctrl"
+  * "pmu1"
+  * "pmu2" for sun4i, sun6i or sun7i
+- #phy-cells : from the generic phy bindings, must be 1
+- clocks : phandle + clock specifier for the phy clocks
+- clock-names :
+  * "usb_phy" for sun4i, sun5i or sun7i
+  * "usb0_phy", "usb1_phy" and "usb2_phy" for sun6i
+- resets : a list of phandle + reset specifier pairs
+- reset-names :
+  * "usb0_reset"
+  * "usb1_reset"
+  * "usb2_reset" for sun4i, sun6i or sun7i
+
+Example:
+       usbphy: phy@0x01c13400 {
+               #phy-cells = <1>;
+               compatible = "allwinner,sun4i-a10-usb-phy";
+               /* phy base regs, phy1 pmu reg, phy2 pmu reg */
+               reg = <0x01c13400 0x10 0x01c14800 0x4 0x01c1c800 0x4>;
+               reg-names = "phy_ctrl", "pmu1", "pmu2";
+               clocks = <&usb_clk 8>;
+               clock-names = "usb_phy";
+               resets = <&usb_clk 1>, <&usb_clk 2>;
+               reset-names = "usb1_reset", "usb2_reset";
+       };
diff --git a/Documentation/devicetree/bindings/phy/ti-phy.txt b/Documentation/devicetree/bindings/phy/ti-phy.txt
new file mode 100644 (file)
index 0000000..305e3df
--- /dev/null
@@ -0,0 +1,102 @@
+TI PHY: DT DOCUMENTATION FOR PHYs in TI PLATFORMs
+
+OMAP CONTROL PHY
+
+Required properties:
+ - compatible: Should be one of
+ "ti,control-phy-otghs" - if it has otghs_control mailbox register as on OMAP4.
+ "ti,control-phy-usb2" - if it has Power down bit in control_dev_conf register
+                        e.g. USB2_PHY on OMAP5.
+ "ti,control-phy-pipe3" - if it has DPLL and individual Rx & Tx power control
+                        e.g. USB3 PHY and SATA PHY on OMAP5.
+ "ti,control-phy-pcie" - for pcie to support external clock for pcie and to
+                       set PCS delay value.
+                       e.g. PCIE PHY in DRA7x
+ "ti,control-phy-usb2-dra7" - if it has power down register like USB2 PHY on
+                        DRA7 platform.
+ "ti,control-phy-usb2-am437" - if it has power down register like USB2 PHY on
+                        AM437 platform.
+ - reg : register ranges as listed in the reg-names property
+ - reg-names: "otghs_control" for control-phy-otghs
+             "power", "pcie_pcs" and "control_sma" for control-phy-pcie
+             "power" for all other types
+
+omap_control_usb: omap-control-usb@4a002300 {
+        compatible = "ti,control-phy-otghs";
+        reg = <0x4a00233c 0x4>;
+        reg-names = "otghs_control";
+};
+
+OMAP USB2 PHY
+
+Required properties:
+ - compatible: Should be "ti,omap-usb2"
+ - reg : Address and length of the register set for the device.
+ - #phy-cells: determine the number of cells that should be given in the
+   phandle while referencing this phy.
+ - clocks: a list of phandles and clock-specifier pairs, one for each entry in
+   clock-names.
+ - clock-names: should include:
+   * "wkupclk" - wakeup clock.
+   * "refclk" - reference clock (optional).
+
+Optional properties:
+ - ctrl-module : phandle of the control module used by PHY driver to power on
+   the PHY.
+
+This is usually a subnode of ocp2scp to which it is connected.
+
+usb2phy@4a0ad080 {
+       compatible = "ti,omap-usb2";
+       reg = <0x4a0ad080 0x58>;
+       ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
+       clocks = <&usb_phy_cm_clk32k>, <&usb_otg_ss_refclk960m>;
+       clock-names = "wkupclk", "refclk";
+};
+
+TI PIPE3 PHY
+
+Required properties:
+ - compatible: Should be "ti,phy-usb3", "ti,phy-pipe3-sata" or
+   "ti,phy-pipe3-pcie. "ti,omap-usb3" is deprecated.
+ - reg : Address and length of the register set for the device.
+ - reg-names: The names of the register addresses corresponding to the registers
+   filled in "reg".
+ - #phy-cells: determine the number of cells that should be given in the
+   phandle while referencing this phy.
+ - clocks: a list of phandles and clock-specifier pairs, one for each entry in
+   clock-names.
+ - clock-names: should include:
+   * "wkupclk" - wakeup clock.
+   * "sysclk" - system clock.
+   * "refclk" - reference clock.
+   * "dpll_ref" - external dpll ref clk
+   * "dpll_ref_m2" - external dpll ref clk
+   * "phy-div" - divider for apll
+   * "div-clk" - apll clock
+
+Optional properties:
+ - ctrl-module : phandle of the control module used by PHY driver to power on
+   the PHY.
+ - id: If there are multiple instance of the same type, in order to
+   differentiate between each instance "id" can be used (e.g., multi-lane PCIe
+   PHY). If "id" is not provided, it is set to default value of '1'.
+
+This is usually a subnode of ocp2scp to which it is connected.
+
+usb3phy@4a084400 {
+       compatible = "ti,phy-usb3";
+       reg = <0x4a084400 0x80>,
+             <0x4a084800 0x64>,
+             <0x4a084c00 0x40>;
+       reg-names = "phy_rx", "phy_tx", "pll_ctrl";
+       ctrl-module = <&omap_control_usb>;
+       #phy-cells = <0>;
+       clocks = <&usb_phy_cm_clk32k>,
+                <&sys_clkin>,
+                <&usb_otg_ss_refclk960m>;
+       clock-names =   "wkupclk",
+                       "sysclk",
+                       "refclk";
+};
index 0f9b78a4489ab72dcf08e5f9072e3548cb2fac64..45a3e8015410c4e2ca7f6e6b63a6a207ce4c16bb 100755 (executable)
@@ -168,6 +168,8 @@ source "drivers/ipack/Kconfig"
 
 source "drivers/reset/Kconfig"
 
+source "drivers/phy/Kconfig"
+
 source "drivers/gator/Kconfig"
 
 source "drivers/headset_observe/Kconfig"
index 8379b43d8618801b82a91e8a1b635239a05a0b85..2c0f180ab34316d77571e5edc0abbabaaa63f992 100755 (executable)
@@ -8,6 +8,8 @@
 obj-y                          += irqchip/
 obj-y                          += bus/
 
+obj-$(CONFIG_GENERIC_PHY)      += phy/
+
 # GPIO must come after pinctrl as gpios may need to mux pins etc
 obj-y                          += pinctrl/
 obj-y                          += gpio/
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
new file mode 100644 (file)
index 0000000..2a436e6
--- /dev/null
@@ -0,0 +1,259 @@
+#
+# PHY
+#
+
+menu "PHY Subsystem"
+
+config GENERIC_PHY
+       bool "PHY Core"
+       help
+         Generic PHY support.
+
+         This framework is designed to provide a generic interface for PHY
+         devices present in the kernel. This layer will have the generic
+         API by which phy drivers can create PHY using the phy framework and
+         phy users can obtain reference to the PHY. All the users of this
+         framework should select this config.
+
+config PHY_BERLIN_SATA
+       tristate "Marvell Berlin SATA PHY driver"
+       depends on ARCH_BERLIN && HAS_IOMEM && OF
+       select GENERIC_PHY
+       help
+         Enable this to support the SATA PHY on Marvell Berlin SoCs.
+
+config PHY_EXYNOS_MIPI_VIDEO
+       tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
+       depends on HAS_IOMEM
+       depends on ARCH_S5PV210 || ARCH_EXYNOS || COMPILE_TEST
+       select GENERIC_PHY
+       default y if ARCH_S5PV210 || ARCH_EXYNOS
+       help
+         Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
+         and EXYNOS SoCs.
+
+config PHY_MVEBU_SATA
+       def_bool y
+       depends on ARCH_DOVE || MACH_DOVE || MACH_KIRKWOOD
+       depends on OF
+       select GENERIC_PHY
+
+config PHY_MIPHY365X
+       tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
+       depends on ARCH_STI
+       depends on HAS_IOMEM
+       depends on OF
+       select GENERIC_PHY
+       help
+         Enable this to support the miphy transceiver (for SATA/PCIE)
+         that is part of STMicroelectronics STiH41x SoC series.
+
+config PHY_RCAR_GEN2
+       tristate "Renesas R-Car generation 2 USB PHY driver"
+       depends on ARCH_SHMOBILE
+       depends on GENERIC_PHY
+       help
+         Support for USB PHY found on Renesas R-Car generation 2 SoCs.
+
+config OMAP_CONTROL_PHY
+       tristate "OMAP CONTROL PHY Driver"
+       depends on ARCH_OMAP2PLUS || COMPILE_TEST
+       help
+         Enable this to add support for the PHY part present in the control
+         module. This driver has API to power on the USB2 PHY and to write to
+         the mailbox. The mailbox is present only in omap4 and the register to
+         power on the USB2 PHY is present in OMAP4 and OMAP5. OMAP5 has an
+         additional register to power on USB3 PHY/SATA PHY/PCIE PHY
+         (PIPE3 PHY).
+
+config OMAP_USB2
+       tristate "OMAP USB2 PHY Driver"
+       depends on ARCH_OMAP2PLUS
+       depends on USB_PHY
+       select GENERIC_PHY
+       select OMAP_CONTROL_PHY
+       depends on OMAP_OCP2SCP
+       help
+         Enable this to support the transceiver that is part of SOC. This
+         driver takes care of all the PHY functionality apart from comparator.
+         The USB OTG controller communicates with the comparator using this
+         driver.
+
+config TI_PIPE3
+       tristate "TI PIPE3 PHY Driver"
+       depends on ARCH_OMAP2PLUS || COMPILE_TEST
+       select GENERIC_PHY
+       select OMAP_CONTROL_PHY
+       depends on OMAP_OCP2SCP
+       help
+         Enable this to support the PIPE3 PHY that is part of TI SOCs. This
+         driver takes care of all the PHY functionality apart from comparator.
+         This driver interacts with the "OMAP Control PHY Driver" to power
+         on/off the PHY.
+
+config TWL4030_USB
+       tristate "TWL4030 USB Transceiver Driver"
+       depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
+       depends on USB_PHY
+       select GENERIC_PHY
+       help
+         Enable this to support the USB OTG transceiver on TWL4030
+         family chips (including the TWL5030 and TPS659x0 devices).
+         This transceiver supports high and full speed devices plus,
+         in host mode, low speed.
+
+config PHY_EXYNOS_DP_VIDEO
+       tristate "EXYNOS SoC series Display Port PHY driver"
+       depends on OF
+       depends on ARCH_EXYNOS || COMPILE_TEST
+       default ARCH_EXYNOS
+       select GENERIC_PHY
+       help
+         Support for Display Port PHY found on Samsung EXYNOS SoCs.
+
+config BCM_KONA_USB2_PHY
+       tristate "Broadcom Kona USB2 PHY Driver"
+       depends on HAS_IOMEM
+       select GENERIC_PHY
+       help
+         Enable this to support the Broadcom Kona USB 2.0 PHY.
+
+config PHY_EXYNOS5250_SATA
+       tristate "Exynos5250 Sata SerDes/PHY driver"
+       depends on SOC_EXYNOS5250
+       depends on HAS_IOMEM
+       depends on OF
+       select GENERIC_PHY
+       select I2C
+       select I2C_S3C2410
+       select MFD_SYSCON
+       help
+         Enable this to support SATA SerDes/Phy found on Samsung's
+         Exynos5250 based SoCs.This SerDes/Phy supports SATA 1.5 Gb/s,
+         SATA 3.0 Gb/s, SATA 6.0 Gb/s speeds. It supports one SATA host
+         port to accept one SATA device.
+
+config PHY_HIX5HD2_SATA
+       tristate "HIX5HD2 SATA PHY Driver"
+       depends on ARCH_HIX5HD2 && OF && HAS_IOMEM
+       select GENERIC_PHY
+       select MFD_SYSCON
+       help
+         Support for SATA PHY on Hisilicon hix5hd2 Soc.
+
+config PHY_SUN4I_USB
+       tristate "Allwinner sunxi SoC USB PHY driver"
+       depends on ARCH_SUNXI && HAS_IOMEM && OF
+       depends on RESET_CONTROLLER
+       select GENERIC_PHY
+       help
+         Enable this to support the transceiver that is part of Allwinner
+         sunxi SoCs.
+
+         This driver controls the entire USB PHY block, both the USB OTG
+         parts, as well as the 2 regular USB 2 host PHYs.
+
+config PHY_SAMSUNG_USB2
+       tristate "Samsung USB 2.0 PHY driver"
+       depends on HAS_IOMEM
+       depends on USB_EHCI_EXYNOS || USB_OHCI_EXYNOS || USB_DWC2
+       select GENERIC_PHY
+       select MFD_SYSCON
+       default ARCH_EXYNOS
+       help
+         Enable this to support the Samsung USB 2.0 PHY driver for Samsung
+         SoCs. This driver provides the interface for USB 2.0 PHY. Support
+         for particular PHYs will be enabled based on the SoC type in addition
+         to this driver.
+
+config PHY_S5PV210_USB2
+       bool "Support for S5PV210"
+       depends on PHY_SAMSUNG_USB2
+       depends on ARCH_S5PV210
+       help
+         Enable USB PHY support for S5PV210. This option requires that Samsung
+         USB 2.0 PHY driver is enabled and means that support for this
+         particular SoC is compiled in the driver. In case of S5PV210 two phys
+         are available - device and host.
+
+config PHY_EXYNOS4210_USB2
+       bool
+       depends on PHY_SAMSUNG_USB2
+       default CPU_EXYNOS4210
+
+config PHY_EXYNOS4X12_USB2
+       bool
+       depends on PHY_SAMSUNG_USB2
+       default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412
+
+config PHY_EXYNOS5250_USB2
+       bool
+       depends on PHY_SAMSUNG_USB2
+       default SOC_EXYNOS5250 || SOC_EXYNOS5420
+
+config PHY_EXYNOS5_USBDRD
+       tristate "Exynos5 SoC series USB DRD PHY driver"
+       depends on ARCH_EXYNOS5 && OF
+       depends on HAS_IOMEM
+       depends on USB_DWC3_EXYNOS
+       select GENERIC_PHY
+       select MFD_SYSCON
+       default y
+       help
+         Enable USB DRD PHY support for Exynos 5 SoC series.
+         This driver provides PHY interface for USB 3.0 DRD controller
+         present on Exynos5 SoC series.
+
+config PHY_QCOM_APQ8064_SATA
+       tristate "Qualcomm APQ8064 SATA SerDes/PHY driver"
+       depends on ARCH_QCOM
+       depends on HAS_IOMEM
+       depends on OF
+       select GENERIC_PHY
+
+config PHY_QCOM_IPQ806X_SATA
+       tristate "Qualcomm IPQ806x SATA SerDes/PHY driver"
+       depends on ARCH_QCOM
+       depends on HAS_IOMEM
+       depends on OF
+       select GENERIC_PHY
+
+config PHY_ST_SPEAR1310_MIPHY
+       tristate "ST SPEAR1310-MIPHY driver"
+       select GENERIC_PHY
+       depends on MACH_SPEAR1310 || COMPILE_TEST
+       help
+         Support for ST SPEAr1310 MIPHY which can be used for PCIe and SATA.
+
+config PHY_ST_SPEAR1340_MIPHY
+       tristate "ST SPEAR1340-MIPHY driver"
+       select GENERIC_PHY
+       depends on MACH_SPEAR1340 || COMPILE_TEST
+       help
+         Support for ST SPEAr1340 MIPHY which can be used for PCIe and SATA.
+
+config PHY_XGENE
+       tristate "APM X-Gene 15Gbps PHY support"
+       depends on HAS_IOMEM && OF && (ARM64 || COMPILE_TEST)
+       select GENERIC_PHY
+       help
+         This option enables support for APM X-Gene SoC multi-purpose PHY.
+
+config PHY_STIH407_USB
+       tristate "STMicroelectronics USB2 picoPHY driver for STiH407 family"
+       depends on RESET_CONTROLLER
+       depends on ARCH_STI || COMPILE_TEST
+       select GENERIC_PHY
+       help
+         Enable this support to enable the picoPHY device used by USB2
+         and USB3 controllers on STMicroelectronics STiH407 SoC families.
+
+config PHY_STIH41X_USB
+       tristate "STMicroelectronics USB2 PHY driver for STiH41x series"
+       depends on ARCH_STI
+       select GENERIC_PHY
+       help
+         Enable this to support the USB transceiver that is part of
+         STMicroelectronics STiH41x SoC series.
+
+endmenu
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
new file mode 100644 (file)
index 0000000..c4590fc
--- /dev/null
@@ -0,0 +1,33 @@
+#
+# Makefile for the phy drivers.
+#
+
+obj-$(CONFIG_GENERIC_PHY)              += phy-core.o
+obj-$(CONFIG_PHY_BERLIN_SATA)          += phy-berlin-sata.o
+obj-$(CONFIG_BCM_KONA_USB2_PHY)                += phy-bcm-kona-usb2.o
+obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)      += phy-exynos-dp-video.o
+obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)    += phy-exynos-mipi-video.o
+obj-$(CONFIG_PHY_MVEBU_SATA)           += phy-mvebu-sata.o
+obj-$(CONFIG_PHY_MIPHY365X)            += phy-miphy365x.o
+obj-$(CONFIG_PHY_RCAR_GEN2)            += phy-rcar-gen2.o
+obj-$(CONFIG_OMAP_CONTROL_PHY)         += phy-omap-control.o
+obj-$(CONFIG_OMAP_USB2)                        += phy-omap-usb2.o
+obj-$(CONFIG_TI_PIPE3)                 += phy-ti-pipe3.o
+obj-$(CONFIG_TWL4030_USB)              += phy-twl4030-usb.o
+obj-$(CONFIG_PHY_EXYNOS5250_SATA)      += phy-exynos5250-sata.o
+obj-$(CONFIG_PHY_HIX5HD2_SATA)         += phy-hix5hd2-sata.o
+obj-$(CONFIG_PHY_SUN4I_USB)            += phy-sun4i-usb.o
+obj-$(CONFIG_PHY_SAMSUNG_USB2)         += phy-exynos-usb2.o
+phy-exynos-usb2-y                      += phy-samsung-usb2.o
+phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2)  += phy-exynos4210-usb2.o
+phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4X12_USB2)  += phy-exynos4x12-usb2.o
+phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2)  += phy-exynos5250-usb2.o
+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_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
+obj-$(CONFIG_PHY_XGENE)                        += phy-xgene.o
+obj-$(CONFIG_PHY_STIH407_USB)          += phy-stih407-usb.o
+obj-$(CONFIG_PHY_STIH41X_USB)          += phy-stih41x-usb.o
diff --git a/drivers/phy/phy-bcm-kona-usb2.c b/drivers/phy/phy-bcm-kona-usb2.c
new file mode 100644 (file)
index 0000000..c1e0ca3
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * phy-bcm-kona-usb2.c - Broadcom Kona USB2 Phy Driver
+ *
+ * Copyright (C) 2013 Linaro Limited
+ * Matt Porter <mporter@linaro.org>
+ *
+ * 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.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#define OTGCTL                 (0)
+#define OTGCTL_OTGSTAT2                BIT(31)
+#define OTGCTL_OTGSTAT1                BIT(30)
+#define OTGCTL_PRST_N_SW       BIT(11)
+#define OTGCTL_HRESET_N                BIT(10)
+#define OTGCTL_UTMI_LINE_STATE1        BIT(9)
+#define OTGCTL_UTMI_LINE_STATE0        BIT(8)
+
+#define P1CTL                  (8)
+#define P1CTL_SOFT_RESET       BIT(1)
+#define P1CTL_NON_DRIVING      BIT(0)
+
+struct bcm_kona_usb {
+       void __iomem *regs;
+};
+
+static void bcm_kona_usb_phy_power(struct bcm_kona_usb *phy, int on)
+{
+       u32 val;
+
+       val = readl(phy->regs + OTGCTL);
+       if (on) {
+               /* Configure and power PHY */
+               val &= ~(OTGCTL_OTGSTAT2 | OTGCTL_OTGSTAT1 |
+                        OTGCTL_UTMI_LINE_STATE1 | OTGCTL_UTMI_LINE_STATE0);
+               val |= OTGCTL_PRST_N_SW | OTGCTL_HRESET_N;
+       } else {
+               val &= ~(OTGCTL_PRST_N_SW | OTGCTL_HRESET_N);
+       }
+       writel(val, phy->regs + OTGCTL);
+}
+
+static int bcm_kona_usb_phy_init(struct phy *gphy)
+{
+       struct bcm_kona_usb *phy = phy_get_drvdata(gphy);
+       u32 val;
+
+       /* Soft reset PHY */
+       val = readl(phy->regs + P1CTL);
+       val &= ~P1CTL_NON_DRIVING;
+       val |= P1CTL_SOFT_RESET;
+       writel(val, phy->regs + P1CTL);
+       writel(val & ~P1CTL_SOFT_RESET, phy->regs + P1CTL);
+       /* Reset needs to be asserted for 2ms */
+       mdelay(2);
+       writel(val | P1CTL_SOFT_RESET, phy->regs + P1CTL);
+
+       return 0;
+}
+
+static int bcm_kona_usb_phy_power_on(struct phy *gphy)
+{
+       struct bcm_kona_usb *phy = phy_get_drvdata(gphy);
+
+       bcm_kona_usb_phy_power(phy, 1);
+
+       return 0;
+}
+
+static int bcm_kona_usb_phy_power_off(struct phy *gphy)
+{
+       struct bcm_kona_usb *phy = phy_get_drvdata(gphy);
+
+       bcm_kona_usb_phy_power(phy, 0);
+
+       return 0;
+}
+
+static struct phy_ops ops = {
+       .init           = bcm_kona_usb_phy_init,
+       .power_on       = bcm_kona_usb_phy_power_on,
+       .power_off      = bcm_kona_usb_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int bcm_kona_usb2_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct bcm_kona_usb *phy;
+       struct resource *res;
+       struct phy *gphy;
+       struct phy_provider *phy_provider;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy->regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(phy->regs))
+               return PTR_ERR(phy->regs);
+
+       platform_set_drvdata(pdev, phy);
+
+       gphy = devm_phy_create(dev, NULL, &ops, NULL);
+       if (IS_ERR(gphy))
+               return PTR_ERR(gphy);
+
+       /* The Kona PHY supports an 8-bit wide UTMI interface */
+       phy_set_bus_width(gphy, 8);
+
+       phy_set_drvdata(gphy, phy);
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                       of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id bcm_kona_usb2_dt_ids[] = {
+       { .compatible = "brcm,kona-usb2-phy" },
+       { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, bcm_kona_usb2_dt_ids);
+
+static struct platform_driver bcm_kona_usb2_driver = {
+       .probe          = bcm_kona_usb2_probe,
+       .driver         = {
+               .name   = "bcm-kona-usb2",
+               .of_match_table = bcm_kona_usb2_dt_ids,
+       },
+};
+
+module_platform_driver(bcm_kona_usb2_driver);
+
+MODULE_ALIAS("platform:bcm-kona-usb2");
+MODULE_AUTHOR("Matt Porter <mporter@linaro.org>");
+MODULE_DESCRIPTION("BCM Kona USB 2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-berlin-sata.c b/drivers/phy/phy-berlin-sata.c
new file mode 100644 (file)
index 0000000..69ced52
--- /dev/null
@@ -0,0 +1,283 @@
+/*
+ * Marvell Berlin SATA PHY driver
+ *
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Ténart <antoine.tenart@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+#define HOST_VSA_ADDR          0x0
+#define HOST_VSA_DATA          0x4
+#define PORT_SCR_CTL           0x2c
+#define PORT_VSR_ADDR          0x78
+#define PORT_VSR_DATA          0x7c
+
+#define CONTROL_REGISTER       0x0
+#define MBUS_SIZE_CONTROL      0x4
+
+#define POWER_DOWN_PHY0                        BIT(6)
+#define POWER_DOWN_PHY1                        BIT(14)
+#define MBUS_WRITE_REQUEST_SIZE_128    (BIT(2) << 16)
+#define MBUS_READ_REQUEST_SIZE_128     (BIT(2) << 19)
+
+#define PHY_BASE               0x200
+
+/* register 0x01 */
+#define REF_FREF_SEL_25                BIT(0)
+#define PHY_MODE_SATA          (0x0 << 5)
+
+/* register 0x02 */
+#define USE_MAX_PLL_RATE       BIT(12)
+
+/* register 0x23 */
+#define DATA_BIT_WIDTH_10      (0x0 << 10)
+#define DATA_BIT_WIDTH_20      (0x1 << 10)
+#define DATA_BIT_WIDTH_40      (0x2 << 10)
+
+/* register 0x25 */
+#define PHY_GEN_MAX_1_5                (0x0 << 10)
+#define PHY_GEN_MAX_3_0                (0x1 << 10)
+#define PHY_GEN_MAX_6_0                (0x2 << 10)
+
+struct phy_berlin_desc {
+       struct phy      *phy;
+       u32             power_bit;
+       unsigned        index;
+};
+
+struct phy_berlin_priv {
+       void __iomem            *base;
+       spinlock_t              lock;
+       struct clk              *clk;
+       struct phy_berlin_desc  **phys;
+       unsigned                nphys;
+};
+
+static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
+                                              u32 mask, u32 val)
+{
+       u32 regval;
+
+       /* select register */
+       writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
+
+       /* set bits */
+       regval = readl(ctrl_reg + PORT_VSR_DATA);
+       regval &= ~mask;
+       regval |= val;
+       writel(regval, ctrl_reg + PORT_VSR_DATA);
+}
+
+static int phy_berlin_sata_power_on(struct phy *phy)
+{
+       struct phy_berlin_desc *desc = phy_get_drvdata(phy);
+       struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
+       void __iomem *ctrl_reg = priv->base + 0x60 + (desc->index * 0x80);
+       int ret = 0;
+       u32 regval;
+
+       clk_prepare_enable(priv->clk);
+
+       spin_lock(&priv->lock);
+
+       /* Power on PHY */
+       writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
+       regval = readl(priv->base + HOST_VSA_DATA);
+       regval &= ~desc->power_bit;
+       writel(regval, priv->base + HOST_VSA_DATA);
+
+       /* Configure MBus */
+       writel(MBUS_SIZE_CONTROL, priv->base + HOST_VSA_ADDR);
+       regval = readl(priv->base + HOST_VSA_DATA);
+       regval |= MBUS_WRITE_REQUEST_SIZE_128 | MBUS_READ_REQUEST_SIZE_128;
+       writel(regval, priv->base + HOST_VSA_DATA);
+
+       /* set PHY mode and ref freq to 25 MHz */
+       phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
+                                   REF_FREF_SEL_25 | PHY_MODE_SATA);
+
+       /* set PHY up to 6 Gbps */
+       phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
+
+       /* set 40 bits width */
+       phy_berlin_sata_reg_setbits(ctrl_reg, 0x23,  0xc00, DATA_BIT_WIDTH_40);
+
+       /* use max pll rate */
+       phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
+
+       /* set Gen3 controller speed */
+       regval = readl(ctrl_reg + PORT_SCR_CTL);
+       regval &= ~GENMASK(7, 4);
+       regval |= 0x30;
+       writel(regval, ctrl_reg + PORT_SCR_CTL);
+
+       spin_unlock(&priv->lock);
+
+       clk_disable_unprepare(priv->clk);
+
+       return ret;
+}
+
+static int phy_berlin_sata_power_off(struct phy *phy)
+{
+       struct phy_berlin_desc *desc = phy_get_drvdata(phy);
+       struct phy_berlin_priv *priv = dev_get_drvdata(phy->dev.parent);
+       u32 regval;
+
+       clk_prepare_enable(priv->clk);
+
+       spin_lock(&priv->lock);
+
+       /* Power down PHY */
+       writel(CONTROL_REGISTER, priv->base + HOST_VSA_ADDR);
+       regval = readl(priv->base + HOST_VSA_DATA);
+       regval |= desc->power_bit;
+       writel(regval, priv->base + HOST_VSA_DATA);
+
+       spin_unlock(&priv->lock);
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static struct phy *phy_berlin_sata_phy_xlate(struct device *dev,
+                                            struct of_phandle_args *args)
+{
+       struct phy_berlin_priv *priv = dev_get_drvdata(dev);
+       int i;
+
+       if (WARN_ON(args->args[0] >= priv->nphys))
+               return ERR_PTR(-ENODEV);
+
+       for (i = 0; i < priv->nphys; i++) {
+               if (priv->phys[i]->index == args->args[0])
+                       break;
+       }
+
+       if (i == priv->nphys)
+               return ERR_PTR(-ENODEV);
+
+       return priv->phys[i]->phy;
+}
+
+static struct phy_ops phy_berlin_sata_ops = {
+       .power_on       = phy_berlin_sata_power_on,
+       .power_off      = phy_berlin_sata_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static u32 phy_berlin_power_down_bits[] = {
+       POWER_DOWN_PHY0,
+       POWER_DOWN_PHY1,
+};
+
+static int phy_berlin_sata_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *child;
+       struct phy *phy;
+       struct phy_provider *phy_provider;
+       struct phy_berlin_priv *priv;
+       struct resource *res;
+       int i = 0;
+       u32 phy_id;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -EINVAL;
+
+       priv->base = devm_ioremap(dev, res->start, resource_size(res));
+       if (!priv->base)
+               return -ENOMEM;
+
+       priv->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(priv->clk))
+               return PTR_ERR(priv->clk);
+
+       priv->nphys = of_get_child_count(dev->of_node);
+       if (priv->nphys == 0)
+               return -ENODEV;
+
+       priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
+                                 GFP_KERNEL);
+       if (!priv->phys)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, priv);
+       spin_lock_init(&priv->lock);
+
+       for_each_available_child_of_node(dev->of_node, child) {
+               struct phy_berlin_desc *phy_desc;
+
+               if (of_property_read_u32(child, "reg", &phy_id)) {
+                       dev_err(dev, "missing reg property in node %s\n",
+                               child->name);
+                       return -EINVAL;
+               }
+
+               if (phy_id >= ARRAY_SIZE(phy_berlin_power_down_bits)) {
+                       dev_err(dev, "invalid reg in node %s\n", child->name);
+                       return -EINVAL;
+               }
+
+               phy_desc = devm_kzalloc(dev, sizeof(*phy_desc), GFP_KERNEL);
+               if (!phy_desc)
+                       return -ENOMEM;
+
+               phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", phy_id);
+                       return PTR_ERR(phy);
+               }
+
+               phy_desc->phy = phy;
+               phy_desc->power_bit = phy_berlin_power_down_bits[phy_id];
+               phy_desc->index = phy_id;
+               phy_set_drvdata(phy, phy_desc);
+
+               priv->phys[i++] = phy_desc;
+
+               /* Make sure the PHY is off */
+               phy_berlin_sata_power_off(phy);
+       }
+
+       phy_provider =
+               devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       return 0;
+}
+
+static const struct of_device_id phy_berlin_sata_of_match[] = {
+       { .compatible = "marvell,berlin2q-sata-phy" },
+       { },
+};
+
+static struct platform_driver phy_berlin_sata_driver = {
+       .probe  = phy_berlin_sata_probe,
+       .driver = {
+               .name           = "phy-berlin-sata",
+               .of_match_table = phy_berlin_sata_of_match,
+       },
+};
+module_platform_driver(phy_berlin_sata_driver);
+
+MODULE_DESCRIPTION("Marvell Berlin SATA PHY driver");
+MODULE_AUTHOR("Antoine Ténart <antoine.tenart@free-electrons.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c
new file mode 100644 (file)
index 0000000..ff5eec5
--- /dev/null
@@ -0,0 +1,872 @@
+/*
+ * phy-core.c  --  Generic Phy framework.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/idr.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+static struct class *phy_class;
+static DEFINE_MUTEX(phy_provider_mutex);
+static LIST_HEAD(phy_provider_list);
+static DEFINE_IDA(phy_ida);
+
+static void devm_phy_release(struct device *dev, void *res)
+{
+       struct phy *phy = *(struct phy **)res;
+
+       phy_put(phy);
+}
+
+static void devm_phy_provider_release(struct device *dev, void *res)
+{
+       struct phy_provider *phy_provider = *(struct phy_provider **)res;
+
+       of_phy_provider_unregister(phy_provider);
+}
+
+static void devm_phy_consume(struct device *dev, void *res)
+{
+       struct phy *phy = *(struct phy **)res;
+
+       phy_destroy(phy);
+}
+
+static int devm_phy_match(struct device *dev, void *res, void *match_data)
+{
+       return res == match_data;
+}
+
+static struct phy *phy_lookup(struct device *device, const char *port)
+{
+       unsigned int count;
+       struct phy *phy;
+       struct device *dev;
+       struct phy_consumer *consumers;
+       struct class_dev_iter iter;
+
+       class_dev_iter_init(&iter, phy_class, NULL, NULL);
+       while ((dev = class_dev_iter_next(&iter))) {
+               phy = to_phy(dev);
+
+               if (!phy->init_data)
+                       continue;
+               count = phy->init_data->num_consumers;
+               consumers = phy->init_data->consumers;
+               while (count--) {
+                       if (!strcmp(consumers->dev_name, dev_name(device)) &&
+                                       !strcmp(consumers->port, port)) {
+                               class_dev_iter_exit(&iter);
+                               return phy;
+                       }
+                       consumers++;
+               }
+       }
+
+       class_dev_iter_exit(&iter);
+       return ERR_PTR(-ENODEV);
+}
+
+static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
+{
+       struct phy_provider *phy_provider;
+       struct device_node *child;
+
+       list_for_each_entry(phy_provider, &phy_provider_list, list) {
+               if (phy_provider->dev->of_node == node)
+                       return phy_provider;
+
+               for_each_child_of_node(phy_provider->dev->of_node, child)
+                       if (child == node)
+                               return phy_provider;
+       }
+
+       return ERR_PTR(-EPROBE_DEFER);
+}
+
+int phy_pm_runtime_get(struct phy *phy)
+{
+       int ret;
+
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       ret = pm_runtime_get(&phy->dev);
+       if (ret < 0 && ret != -EINPROGRESS)
+               pm_runtime_put_noidle(&phy->dev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
+
+int phy_pm_runtime_get_sync(struct phy *phy)
+{
+       int ret;
+
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       ret = pm_runtime_get_sync(&phy->dev);
+       if (ret < 0)
+               pm_runtime_put_sync(&phy->dev);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
+
+int phy_pm_runtime_put(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_put(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put);
+
+int phy_pm_runtime_put_sync(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return -ENOTSUPP;
+
+       return pm_runtime_put_sync(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_put_sync);
+
+void phy_pm_runtime_allow(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return;
+
+       pm_runtime_allow(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_allow);
+
+void phy_pm_runtime_forbid(struct phy *phy)
+{
+       if (!pm_runtime_enabled(&phy->dev))
+               return;
+
+       pm_runtime_forbid(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_pm_runtime_forbid);
+
+int phy_init(struct phy *phy)
+{
+       int ret;
+
+       if (!phy)
+               return 0;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (phy->init_count == 0 && phy->ops->init) {
+               ret = phy->ops->init(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy init failed --> %d\n", ret);
+                       goto out;
+               }
+       } else {
+               ret = 0; /* Override possible ret == -ENOTSUPP */
+       }
+       ++phy->init_count;
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_init);
+
+int phy_exit(struct phy *phy)
+{
+       int ret;
+
+       if (!phy)
+               return 0;
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (phy->init_count == 1 && phy->ops->exit) {
+               ret = phy->ops->exit(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
+                       goto out;
+               }
+       }
+       --phy->init_count;
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_exit);
+
+int phy_power_on(struct phy *phy)
+{
+       int ret;
+
+       if (!phy)
+               return 0;
+
+       if (phy->pwr) {
+               ret = regulator_enable(phy->pwr);
+               if (ret)
+                       return ret;
+       }
+
+       ret = phy_pm_runtime_get_sync(phy);
+       if (ret < 0 && ret != -ENOTSUPP)
+               return ret;
+
+       mutex_lock(&phy->mutex);
+       if (phy->power_count == 0 && phy->ops->power_on) {
+               ret = phy->ops->power_on(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
+                       goto out;
+               }
+       } else {
+               ret = 0; /* Override possible ret == -ENOTSUPP */
+       }
+       ++phy->power_count;
+       mutex_unlock(&phy->mutex);
+       return 0;
+
+out:
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put_sync(phy);
+       if (phy->pwr)
+               regulator_disable(phy->pwr);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(phy_power_on);
+
+int phy_power_off(struct phy *phy)
+{
+       int ret;
+
+       if (!phy)
+               return 0;
+
+       mutex_lock(&phy->mutex);
+       if (phy->power_count == 1 && phy->ops->power_off) {
+               ret =  phy->ops->power_off(phy);
+               if (ret < 0) {
+                       dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
+                       mutex_unlock(&phy->mutex);
+                       return ret;
+               }
+       }
+       --phy->power_count;
+       mutex_unlock(&phy->mutex);
+       phy_pm_runtime_put(phy);
+
+       if (phy->pwr)
+               regulator_disable(phy->pwr);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(phy_power_off);
+
+/**
+ * _of_phy_get() - lookup and obtain a reference to a phy by phandle
+ * @np: device_node for which to get the phy
+ * @index: the index of the phy
+ *
+ * Returns the phy associated with the given phandle value,
+ * after getting a refcount to it or -ENODEV if there is no such phy or
+ * -EPROBE_DEFER if there is a phandle to the phy, but the device is
+ * not yet loaded. This function uses of_xlate call back function provided
+ * while registering the phy_provider to find the phy instance.
+ */
+static struct phy *_of_phy_get(struct device_node *np, int index)
+{
+       int ret;
+       struct phy_provider *phy_provider;
+       struct phy *phy = NULL;
+       struct of_phandle_args args;
+
+       ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
+               index, &args);
+       if (ret)
+               return ERR_PTR(-ENODEV);
+
+       mutex_lock(&phy_provider_mutex);
+       phy_provider = of_phy_provider_lookup(args.np);
+       if (IS_ERR(phy_provider) || !try_module_get(phy_provider->owner)) {
+               phy = ERR_PTR(-EPROBE_DEFER);
+               goto err0;
+       }
+
+       phy = phy_provider->of_xlate(phy_provider->dev, &args);
+       module_put(phy_provider->owner);
+
+err0:
+       mutex_unlock(&phy_provider_mutex);
+       of_node_put(args.np);
+
+       return phy;
+}
+
+/**
+ * of_phy_get() - lookup and obtain a reference to a phy using a device_node.
+ * @np: device_node for which to get the phy
+ * @con_id: name of the phy from device's point of view
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy. The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *of_phy_get(struct device_node *np, const char *con_id)
+{
+       struct phy *phy = NULL;
+       int index = 0;
+
+       if (con_id)
+               index = of_property_match_string(np, "phy-names", con_id);
+
+       phy = _of_phy_get(np, index);
+       if (IS_ERR(phy))
+               return phy;
+
+       if (!try_module_get(phy->ops->owner))
+               return ERR_PTR(-EPROBE_DEFER);
+
+       get_device(&phy->dev);
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(of_phy_get);
+
+/**
+ * phy_put() - release the PHY
+ * @phy: the phy returned by phy_get()
+ *
+ * Releases a refcount the caller received from phy_get().
+ */
+void phy_put(struct phy *phy)
+{
+       if (!phy || IS_ERR(phy))
+               return;
+
+       module_put(phy->ops->owner);
+       put_device(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_put);
+
+/**
+ * devm_phy_put() - release the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_put
+ * to release the phy.
+ */
+void devm_phy_put(struct device *dev, struct phy *phy)
+{
+       int r;
+
+       if (!phy)
+               return;
+
+       r = devres_destroy(dev, devm_phy_release, devm_phy_match, phy);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_put);
+
+/**
+ * of_phy_simple_xlate() - returns the phy instance from phy provider
+ * @dev: the PHY provider device
+ * @args: of_phandle_args (not used here)
+ *
+ * Intended to be used by phy provider for the common case where #phy-cells is
+ * 0. For other cases where #phy-cells is greater than '0', the phy provider
+ * should provide a custom of_xlate function that reads the *args* and returns
+ * the appropriate phy.
+ */
+struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
+       *args)
+{
+       struct phy *phy;
+       struct class_dev_iter iter;
+       struct device_node *node = dev->of_node;
+       struct device_node *child;
+
+       class_dev_iter_init(&iter, phy_class, NULL, NULL);
+       while ((dev = class_dev_iter_next(&iter))) {
+               phy = to_phy(dev);
+               if (node != phy->dev.of_node) {
+                       for_each_child_of_node(node, child) {
+                               if (child == phy->dev.of_node)
+                                       goto phy_found;
+                       }
+                       continue;
+               }
+
+phy_found:
+               class_dev_iter_exit(&iter);
+               return phy;
+       }
+
+       class_dev_iter_exit(&iter);
+       return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(of_phy_simple_xlate);
+
+/**
+ * phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * -ENODEV if there is no such phy.  The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *phy_get(struct device *dev, const char *string)
+{
+       int index = 0;
+       struct phy *phy;
+
+       if (string == NULL) {
+               dev_WARN(dev, "missing string\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       if (dev->of_node) {
+               index = of_property_match_string(dev->of_node, "phy-names",
+                       string);
+               phy = _of_phy_get(dev->of_node, index);
+       } else {
+               phy = phy_lookup(dev, string);
+       }
+       if (IS_ERR(phy))
+               return phy;
+
+       if (!try_module_get(phy->ops->owner))
+               return ERR_PTR(-EPROBE_DEFER);
+
+       get_device(&phy->dev);
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(phy_get);
+
+/**
+ * phy_optional_get() - lookup and obtain a reference to an optional phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or the name of the controller
+ * port for non-dt case
+ *
+ * Returns the phy driver, after getting a refcount to it; or
+ * NULL if there is no such phy.  The caller is responsible for
+ * calling phy_put() to release that count.
+ */
+struct phy *phy_optional_get(struct device *dev, const char *string)
+{
+       struct phy *phy = phy_get(dev, string);
+
+       if (PTR_ERR(phy) == -ENODEV)
+               phy = NULL;
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(phy_optional_get);
+
+/**
+ * devm_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or phy device name
+ * for non-dt case
+ *
+ * Gets the phy using phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_get(struct device *dev, const char *string)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = phy_get(dev, string);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_get);
+
+/**
+ * devm_phy_optional_get() - lookup and obtain a reference to an optional phy.
+ * @dev: device that requests this phy
+ * @string: the phy name as given in the dt data or phy device name
+ * for non-dt case
+ *
+ * Gets the phy using phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres
+ * data, then, devres data is freed. This differs to devm_phy_get() in
+ * that if the phy does not exist, it is not considered an error and
+ * -ENODEV will not be returned. Instead the NULL phy is returned,
+ * which can be passed to all other phy consumer calls.
+ */
+struct phy *devm_phy_optional_get(struct device *dev, const char *string)
+{
+       struct phy *phy = devm_phy_get(dev, string);
+
+       if (PTR_ERR(phy) == -ENODEV)
+               phy = NULL;
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_optional_get);
+
+/**
+ * devm_of_phy_get() - lookup and obtain a reference to a phy.
+ * @dev: device that requests this phy
+ * @np: node containing the phy
+ * @con_id: name of the phy from device's point of view
+ *
+ * Gets the phy using of_phy_get(), and associates a device with it using
+ * devres. On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
+                           const char *con_id)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = of_phy_get(np, con_id);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_get);
+
+/**
+ * phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @node: device node of the phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Called to create a phy using phy framework.
+ */
+struct phy *phy_create(struct device *dev, struct device_node *node,
+                      const struct phy_ops *ops,
+                      struct phy_init_data *init_data)
+{
+       int ret;
+       int id;
+       struct phy *phy;
+
+       if (WARN_ON(!dev))
+               return ERR_PTR(-EINVAL);
+
+       phy = kzalloc(sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return ERR_PTR(-ENOMEM);
+
+       id = ida_simple_get(&phy_ida, 0, 0, GFP_KERNEL);
+       if (id < 0) {
+               dev_err(dev, "unable to get id\n");
+               ret = id;
+               goto free_phy;
+       }
+
+       /* phy-supply */
+       phy->pwr = regulator_get_optional(dev, "phy");
+       if (IS_ERR(phy->pwr)) {
+               if (PTR_ERR(phy->pwr) == -EPROBE_DEFER) {
+                       ret = -EPROBE_DEFER;
+                       goto free_ida;
+               }
+               phy->pwr = NULL;
+       }
+
+       device_initialize(&phy->dev);
+       mutex_init(&phy->mutex);
+
+       phy->dev.class = phy_class;
+       phy->dev.parent = dev;
+       phy->dev.of_node = node ?: dev->of_node;
+       phy->id = id;
+       phy->ops = ops;
+       phy->init_data = init_data;
+
+       ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
+       if (ret)
+               goto put_dev;
+
+       ret = device_add(&phy->dev);
+       if (ret)
+               goto put_dev;
+
+       if (pm_runtime_enabled(dev)) {
+               pm_runtime_enable(&phy->dev);
+               pm_runtime_no_callbacks(&phy->dev);
+       }
+
+       return phy;
+
+put_dev:
+       put_device(&phy->dev);  /* calls phy_release() which frees resources */
+       return ERR_PTR(ret);
+
+free_ida:
+       ida_simple_remove(&phy_ida, phy->id);
+
+free_phy:
+       kfree(phy);
+       return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(phy_create);
+
+/**
+ * devm_phy_create() - create a new phy
+ * @dev: device that is creating the new phy
+ * @node: device node of the phy
+ * @ops: function pointers for performing phy operations
+ * @init_data: contains the list of PHY consumers or NULL
+ *
+ * Creates a new PHY device adding it to the PHY class.
+ * While at that, it also associates the device with the phy using devres.
+ * On driver detach, release function is invoked on the devres data,
+ * then, devres data is freed.
+ */
+struct phy *devm_phy_create(struct device *dev, struct device_node *node,
+                           const struct phy_ops *ops,
+                           struct phy_init_data *init_data)
+{
+       struct phy **ptr, *phy;
+
+       ptr = devres_alloc(devm_phy_consume, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy = phy_create(dev, node, ops, init_data);
+       if (!IS_ERR(phy)) {
+               *ptr = phy;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy;
+}
+EXPORT_SYMBOL_GPL(devm_phy_create);
+
+/**
+ * phy_destroy() - destroy the phy
+ * @phy: the phy to be destroyed
+ *
+ * Called to destroy the phy.
+ */
+void phy_destroy(struct phy *phy)
+{
+       pm_runtime_disable(&phy->dev);
+       device_unregister(&phy->dev);
+}
+EXPORT_SYMBOL_GPL(phy_destroy);
+
+/**
+ * devm_phy_destroy() - destroy the PHY
+ * @dev: device that wants to release this phy
+ * @phy: the phy returned by devm_phy_get()
+ *
+ * destroys the devres associated with this phy and invokes phy_destroy
+ * to destroy the phy.
+ */
+void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+       int r;
+
+       r = devres_destroy(dev, devm_phy_consume, devm_phy_match, phy);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_phy_destroy);
+
+/**
+ * __of_phy_provider_register() - create/register phy provider with the framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider.
+ */
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       struct phy_provider *phy_provider;
+
+       phy_provider = kzalloc(sizeof(*phy_provider), GFP_KERNEL);
+       if (!phy_provider)
+               return ERR_PTR(-ENOMEM);
+
+       phy_provider->dev = dev;
+       phy_provider->owner = owner;
+       phy_provider->of_xlate = of_xlate;
+
+       mutex_lock(&phy_provider_mutex);
+       list_add_tail(&phy_provider->list, &phy_provider_list);
+       mutex_unlock(&phy_provider_mutex);
+
+       return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__of_phy_provider_register);
+
+/**
+ * __devm_of_phy_provider_register() - create/register phy provider with the
+ * framework
+ * @dev: struct device of the phy provider
+ * @owner: the module owner containing of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy provider
+ *
+ * Creates struct phy_provider from dev and of_xlate function pointer.
+ * This is used in the case of dt boot for finding the phy instance from
+ * phy provider. While at that, it also associates the device with the
+ * phy provider using devres. On driver detach, release function is invoked
+ * on the devres data, then, devres data is freed.
+ */
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       struct phy_provider **ptr, *phy_provider;
+
+       ptr = devres_alloc(devm_phy_provider_release, sizeof(*ptr), GFP_KERNEL);
+       if (!ptr)
+               return ERR_PTR(-ENOMEM);
+
+       phy_provider = __of_phy_provider_register(dev, owner, of_xlate);
+       if (!IS_ERR(phy_provider)) {
+               *ptr = phy_provider;
+               devres_add(dev, ptr);
+       } else {
+               devres_free(ptr);
+       }
+
+       return phy_provider;
+}
+EXPORT_SYMBOL_GPL(__devm_of_phy_provider_register);
+
+/**
+ * of_phy_provider_unregister() - unregister phy provider from the framework
+ * @phy_provider: phy provider returned by of_phy_provider_register()
+ *
+ * Removes the phy_provider created using of_phy_provider_register().
+ */
+void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+       if (IS_ERR(phy_provider))
+               return;
+
+       mutex_lock(&phy_provider_mutex);
+       list_del(&phy_provider->list);
+       kfree(phy_provider);
+       mutex_unlock(&phy_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(of_phy_provider_unregister);
+
+/**
+ * devm_of_phy_provider_unregister() - remove phy provider from the framework
+ * @dev: struct device of the phy provider
+ *
+ * destroys the devres associated with this phy provider and invokes
+ * of_phy_provider_unregister to unregister the phy provider.
+ */
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider) {
+       int r;
+
+       r = devres_destroy(dev, devm_phy_provider_release, devm_phy_match,
+               phy_provider);
+       dev_WARN_ONCE(dev, r, "couldn't find PHY provider device resource\n");
+}
+EXPORT_SYMBOL_GPL(devm_of_phy_provider_unregister);
+
+/**
+ * phy_release() - release the phy
+ * @dev: the dev member within phy
+ *
+ * When the last reference to the device is removed, it is called
+ * from the embedded kobject as release method.
+ */
+static void phy_release(struct device *dev)
+{
+       struct phy *phy;
+
+       phy = to_phy(dev);
+       dev_vdbg(dev, "releasing '%s'\n", dev_name(dev));
+       regulator_put(phy->pwr);
+       ida_simple_remove(&phy_ida, phy->id);
+       kfree(phy);
+}
+
+static int __init phy_core_init(void)
+{
+       phy_class = class_create(THIS_MODULE, "phy");
+       if (IS_ERR(phy_class)) {
+               pr_err("failed to create phy class --> %ld\n",
+                       PTR_ERR(phy_class));
+               return PTR_ERR(phy_class);
+       }
+
+       phy_class->dev_release = phy_release;
+
+       return 0;
+}
+module_init(phy_core_init);
+
+static void __exit phy_core_exit(void)
+{
+       class_destroy(phy_class);
+}
+module_exit(phy_core_exit);
+
+MODULE_DESCRIPTION("Generic PHY Framework");
+MODULE_AUTHOR("Kishon Vijay Abraham I <kishon@ti.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-dp-video.c b/drivers/phy/phy-exynos-dp-video.c
new file mode 100644 (file)
index 0000000..84f49e5
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ * Samsung EXYNOS SoC series Display Port PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Jingoo Han <jg1.han@samsung.com>
+ *
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/exynos5-pmu.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+struct exynos_dp_video_phy_drvdata {
+       u32 phy_ctrl_offset;
+};
+
+struct exynos_dp_video_phy {
+       struct regmap *regs;
+       const struct exynos_dp_video_phy_drvdata *drvdata;
+};
+
+static void exynos_dp_video_phy_pwr_isol(struct exynos_dp_video_phy *state,
+                                                       unsigned int on)
+{
+       unsigned int val;
+
+       if (IS_ERR(state->regs))
+               return;
+
+       val = on ? 0 : EXYNOS5_PHY_ENABLE;
+
+       regmap_update_bits(state->regs, state->drvdata->phy_ctrl_offset,
+                          EXYNOS5_PHY_ENABLE, val);
+}
+
+static int exynos_dp_video_phy_power_on(struct phy *phy)
+{
+       struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+       /* Disable power isolation on DP-PHY */
+       exynos_dp_video_phy_pwr_isol(state, 0);
+
+       return 0;
+}
+
+static int exynos_dp_video_phy_power_off(struct phy *phy)
+{
+       struct exynos_dp_video_phy *state = phy_get_drvdata(phy);
+
+       /* Enable power isolation on DP-PHY */
+       exynos_dp_video_phy_pwr_isol(state, 1);
+
+       return 0;
+}
+
+static struct phy_ops exynos_dp_video_phy_ops = {
+       .power_on       = exynos_dp_video_phy_power_on,
+       .power_off      = exynos_dp_video_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static const struct exynos_dp_video_phy_drvdata exynos5250_dp_video_phy = {
+       .phy_ctrl_offset        = EXYNOS5_DPTX_PHY_CONTROL,
+};
+
+static const struct exynos_dp_video_phy_drvdata exynos5420_dp_video_phy = {
+       .phy_ctrl_offset        = EXYNOS5420_DPTX_PHY_CONTROL,
+};
+
+static const struct of_device_id exynos_dp_video_phy_of_match[] = {
+       {
+               .compatible = "samsung,exynos5250-dp-video-phy",
+               .data = &exynos5250_dp_video_phy,
+       }, {
+               .compatible = "samsung,exynos5420-dp-video-phy",
+               .data = &exynos5420_dp_video_phy,
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_dp_video_phy_of_match);
+
+static int exynos_dp_video_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_dp_video_phy *state;
+       struct device *dev = &pdev->dev;
+       const struct of_device_id *match;
+       struct phy_provider *phy_provider;
+       struct phy *phy;
+
+       state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       state->regs = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                     "samsung,pmu-syscon");
+       if (IS_ERR(state->regs)) {
+               dev_err(dev, "Failed to lookup PMU regmap\n");
+               return PTR_ERR(state->regs);
+       }
+
+       match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node);
+       state->drvdata = match->data;
+
+       phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL);
+       if (IS_ERR(phy)) {
+               dev_err(dev, "failed to create Display Port PHY\n");
+               return PTR_ERR(phy);
+       }
+       phy_set_drvdata(phy, state);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver exynos_dp_video_phy_driver = {
+       .probe  = exynos_dp_video_phy_probe,
+       .driver = {
+               .name   = "exynos-dp-video-phy",
+               .of_match_table = exynos_dp_video_phy_of_match,
+       }
+};
+module_platform_driver(exynos_dp_video_phy_driver);
+
+MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
+MODULE_DESCRIPTION("Samsung EXYNOS SoC DP PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos-mipi-video.c b/drivers/phy/phy-exynos-mipi-video.c
new file mode 100644 (file)
index 0000000..6a9bef1
--- /dev/null
@@ -0,0 +1,174 @@
+/*
+ * Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
+#define EXYNOS_MIPI_PHY_CONTROL(n)     ((n) * 4)
+#define EXYNOS_MIPI_PHY_ENABLE         (1 << 0)
+#define EXYNOS_MIPI_PHY_SRESETN                (1 << 1)
+#define EXYNOS_MIPI_PHY_MRESETN                (1 << 2)
+#define EXYNOS_MIPI_PHY_RESET_MASK     (3 << 1)
+
+enum exynos_mipi_phy_id {
+       EXYNOS_MIPI_PHY_ID_CSIS0,
+       EXYNOS_MIPI_PHY_ID_DSIM0,
+       EXYNOS_MIPI_PHY_ID_CSIS1,
+       EXYNOS_MIPI_PHY_ID_DSIM1,
+       EXYNOS_MIPI_PHYS_NUM
+};
+
+#define is_mipi_dsim_phy_id(id) \
+       ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
+
+struct exynos_mipi_video_phy {
+       spinlock_t slock;
+       struct video_phy_desc {
+               struct phy *phy;
+               unsigned int index;
+       } phys[EXYNOS_MIPI_PHYS_NUM];
+       void __iomem *regs;
+};
+
+static int __set_phy_state(struct exynos_mipi_video_phy *state,
+                       enum exynos_mipi_phy_id id, unsigned int on)
+{
+       void __iomem *addr;
+       u32 reg, reset;
+
+       addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+
+       if (is_mipi_dsim_phy_id(id))
+               reset = EXYNOS_MIPI_PHY_MRESETN;
+       else
+               reset = EXYNOS_MIPI_PHY_SRESETN;
+
+       spin_lock(&state->slock);
+       reg = readl(addr);
+       if (on)
+               reg |= reset;
+       else
+               reg &= ~reset;
+       writel(reg, addr);
+
+       /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
+       if (on)
+               reg |= EXYNOS_MIPI_PHY_ENABLE;
+       else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
+               reg &= ~EXYNOS_MIPI_PHY_ENABLE;
+
+       writel(reg, addr);
+       spin_unlock(&state->slock);
+       return 0;
+}
+
+#define to_mipi_video_phy(desc) \
+       container_of((desc), struct exynos_mipi_video_phy, phys[(desc)->index]);
+
+static int exynos_mipi_video_phy_power_on(struct phy *phy)
+{
+       struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+       struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+       return __set_phy_state(state, phy_desc->index, 1);
+}
+
+static int exynos_mipi_video_phy_power_off(struct phy *phy)
+{
+       struct video_phy_desc *phy_desc = phy_get_drvdata(phy);
+       struct exynos_mipi_video_phy *state = to_mipi_video_phy(phy_desc);
+
+       return __set_phy_state(state, phy_desc->index, 0);
+}
+
+static struct phy *exynos_mipi_video_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct exynos_mipi_video_phy *state = dev_get_drvdata(dev);
+
+       if (WARN_ON(args->args[0] >= EXYNOS_MIPI_PHYS_NUM))
+               return ERR_PTR(-ENODEV);
+
+       return state->phys[args->args[0]].phy;
+}
+
+static struct phy_ops exynos_mipi_video_phy_ops = {
+       .power_on       = exynos_mipi_video_phy_power_on,
+       .power_off      = exynos_mipi_video_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_mipi_video_phy *state;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       unsigned int i;
+
+       state = devm_kzalloc(dev, sizeof(*state), GFP_KERNEL);
+       if (!state)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       state->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(state->regs))
+               return PTR_ERR(state->regs);
+
+       dev_set_drvdata(dev, state);
+       spin_lock_init(&state->slock);
+
+       for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
+               struct phy *phy = devm_phy_create(dev, NULL,
+                                       &exynos_mipi_video_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", i);
+                       return PTR_ERR(phy);
+               }
+
+               state->phys[i].phy = phy;
+               state->phys[i].index = i;
+               phy_set_drvdata(phy, &state->phys[i]);
+       }
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                       exynos_mipi_video_phy_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id exynos_mipi_video_phy_of_match[] = {
+       { .compatible = "samsung,s5pv210-mipi-video-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_mipi_video_phy_of_match);
+
+static struct platform_driver exynos_mipi_video_phy_driver = {
+       .probe  = exynos_mipi_video_phy_probe,
+       .driver = {
+               .of_match_table = exynos_mipi_video_phy_of_match,
+               .name  = "exynos-mipi-video-phy",
+       }
+};
+module_platform_driver(exynos_mipi_video_phy_driver);
+
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC MIPI CSI-2/DSI PHY driver");
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-exynos4210-usb2.c b/drivers/phy/phy-exynos4210-usb2.c
new file mode 100644 (file)
index 0000000..236a52a
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4210 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4210_UPHYPWR                    0x0
+
+#define EXYNOS_4210_UPHYPWR_PHY0_SUSPEND       BIT(0)
+#define EXYNOS_4210_UPHYPWR_PHY0_PWR           BIT(3)
+#define EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR       BIT(4)
+#define EXYNOS_4210_UPHYPWR_PHY0_SLEEP         BIT(5)
+#define EXYNOS_4210_UPHYPWR_PHY0       ( \
+       EXYNOS_4210_UPHYPWR_PHY0_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_PHY0_PWR | \
+       EXYNOS_4210_UPHYPWR_PHY0_OTG_PWR | \
+       EXYNOS_4210_UPHYPWR_PHY0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_PHY1_SUSPEND       BIT(6)
+#define EXYNOS_4210_UPHYPWR_PHY1_PWR           BIT(7)
+#define EXYNOS_4210_UPHYPWR_PHY1_SLEEP         BIT(8)
+#define EXYNOS_4210_UPHYPWR_PHY1 ( \
+       EXYNOS_4210_UPHYPWR_PHY1_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_PHY1_PWR | \
+       EXYNOS_4210_UPHYPWR_PHY1_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND      BIT(9)
+#define EXYNOS_4210_UPHYPWR_HSIC0_SLEEP                BIT(10)
+#define EXYNOS_4210_UPHYPWR_HSIC0 ( \
+       EXYNOS_4210_UPHYPWR_HSIC0_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_HSIC0_SLEEP)
+
+#define EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND      BIT(11)
+#define EXYNOS_4210_UPHYPWR_HSIC1_SLEEP                BIT(12)
+#define EXYNOS_4210_UPHYPWR_HSIC1 ( \
+       EXYNOS_4210_UPHYPWR_HSIC1_SUSPEND | \
+       EXYNOS_4210_UPHYPWR_HSIC1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4210_UPHYCLK                    0x4
+
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_MASK       (0x3 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET     0
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ      (0x0 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ      (0x3 << 0)
+#define EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ      (0x2 << 0)
+
+#define EXYNOS_4210_UPHYCLK_PHY0_ID_PULLUP     BIT(2)
+#define EXYNOS_4210_UPHYCLK_PHY0_COMMON_ON     BIT(4)
+#define EXYNOS_4210_UPHYCLK_PHY1_COMMON_ON     BIT(7)
+
+/* PHY reset control */
+#define EXYNOS_4210_UPHYRST                    0x8
+
+#define EXYNOS_4210_URSTCON_PHY0               BIT(0)
+#define EXYNOS_4210_URSTCON_OTG_HLINK          BIT(1)
+#define EXYNOS_4210_URSTCON_OTG_PHYLINK                BIT(2)
+#define EXYNOS_4210_URSTCON_PHY1_ALL           BIT(3)
+#define EXYNOS_4210_URSTCON_PHY1_P0            BIT(4)
+#define EXYNOS_4210_URSTCON_PHY1_P1P2          BIT(5)
+#define EXYNOS_4210_URSTCON_HOST_LINK_ALL      BIT(6)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P0       BIT(7)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P1       BIT(8)
+#define EXYNOS_4210_URSTCON_HOST_LINK_P2       BIT(9)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4210_USB_ISOL_DEVICE_OFFSET     0x704
+#define EXYNOS_4210_USB_ISOL_DEVICE            BIT(0)
+#define EXYNOS_4210_USB_ISOL_HOST_OFFSET       0x708
+#define EXYNOS_4210_USB_ISOL_HOST              BIT(0)
+
+/* USBYPHY1 Floating prevention */
+#define EXYNOS_4210_UPHY1CON                   0x34
+#define EXYNOS_4210_UPHY1CON_FLOAT_PREVENTION  0x1
+
+/* Mode switching SUB Device <-> Host */
+#define EXYNOS_4210_MODE_SWITCH_OFFSET         0x21c
+#define EXYNOS_4210_MODE_SWITCH_MASK           1
+#define EXYNOS_4210_MODE_SWITCH_DEVICE         0
+#define EXYNOS_4210_MODE_SWITCH_HOST           1
+
+enum exynos4210_phy_id {
+       EXYNOS4210_DEVICE,
+       EXYNOS4210_HOST,
+       EXYNOS4210_HSIC0,
+       EXYNOS4210_HSIC1,
+       EXYNOS4210_NUM_PHYS,
+};
+
+/*
+ * exynos4210_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos4210_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       switch (rate) {
+       case 12 * MHZ:
+               *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_12MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_24MHZ;
+               break;
+       case 48 * MHZ:
+               *reg = EXYNOS_4210_UPHYCLK_PHYFSEL_48MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void exynos4210_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 offset;
+       u32 mask;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4210_DEVICE:
+               offset = EXYNOS_4210_USB_ISOL_DEVICE_OFFSET;
+               mask = EXYNOS_4210_USB_ISOL_DEVICE;
+               break;
+       case EXYNOS4210_HOST:
+               offset = EXYNOS_4210_USB_ISOL_HOST_OFFSET;
+               mask = EXYNOS_4210_USB_ISOL_HOST;
+               break;
+       default:
+               return;
+       };
+
+       regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void exynos4210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 rstbits = 0;
+       u32 phypwr = 0;
+       u32 rst;
+       u32 pwr;
+       u32 clk;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4210_DEVICE:
+               phypwr =        EXYNOS_4210_UPHYPWR_PHY0;
+               rstbits =       EXYNOS_4210_URSTCON_PHY0;
+               break;
+       case EXYNOS4210_HOST:
+               phypwr =        EXYNOS_4210_UPHYPWR_PHY1;
+               rstbits =       EXYNOS_4210_URSTCON_PHY1_ALL |
+                               EXYNOS_4210_URSTCON_PHY1_P0 |
+                               EXYNOS_4210_URSTCON_PHY1_P1P2 |
+                               EXYNOS_4210_URSTCON_HOST_LINK_ALL |
+                               EXYNOS_4210_URSTCON_HOST_LINK_P0;
+               writel(on, drv->reg_phy + EXYNOS_4210_UPHY1CON);
+               break;
+       case EXYNOS4210_HSIC0:
+               phypwr =        EXYNOS_4210_UPHYPWR_HSIC0;
+               rstbits =       EXYNOS_4210_URSTCON_PHY1_P1P2 |
+                               EXYNOS_4210_URSTCON_HOST_LINK_P1;
+               break;
+       case EXYNOS4210_HSIC1:
+               phypwr =        EXYNOS_4210_UPHYPWR_HSIC1;
+               rstbits =       EXYNOS_4210_URSTCON_PHY1_P1P2 |
+                               EXYNOS_4210_URSTCON_HOST_LINK_P2;
+               break;
+       };
+
+       if (on) {
+               clk = readl(drv->reg_phy + EXYNOS_4210_UPHYCLK);
+               clk &= ~EXYNOS_4210_UPHYCLK_PHYFSEL_MASK;
+               clk |= drv->ref_reg_val << EXYNOS_4210_UPHYCLK_PHYFSEL_OFFSET;
+               writel(clk, drv->reg_phy + EXYNOS_4210_UPHYCLK);
+
+               pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
+               pwr &= ~phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
+
+               rst = readl(drv->reg_phy + EXYNOS_4210_UPHYRST);
+               rst |= rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
+               udelay(10);
+               rst &= ~rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4210_UPHYRST);
+               /* The following delay is necessary for the reset sequence to be
+                * completed */
+               udelay(80);
+       } else {
+               pwr = readl(drv->reg_phy + EXYNOS_4210_UPHYPWR);
+               pwr |= phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4210_UPHYPWR);
+       }
+}
+
+static int exynos4210_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       /* Order of initialisation is important - first power then isolation */
+       exynos4210_phy_pwr(inst, 1);
+       exynos4210_isol(inst, 0);
+
+       return 0;
+}
+
+static int exynos4210_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       exynos4210_isol(inst, 1);
+       exynos4210_phy_pwr(inst, 0);
+
+       return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos4210_phys[] = {
+       {
+               .label          = "device",
+               .id             = EXYNOS4210_DEVICE,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "host",
+               .id             = EXYNOS4210_HOST,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "hsic0",
+               .id             = EXYNOS4210_HSIC0,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {
+               .label          = "hsic1",
+               .id             = EXYNOS4210_HSIC1,
+               .power_on       = exynos4210_power_on,
+               .power_off      = exynos4210_power_off,
+       },
+       {},
+};
+
+const struct samsung_usb2_phy_config exynos4210_usb2_phy_config = {
+       .has_mode_switch        = 0,
+       .num_phys               = EXYNOS4210_NUM_PHYS,
+       .phys                   = exynos4210_phys,
+       .rate_to_clk            = exynos4210_rate_to_clk,
+};
diff --git a/drivers/phy/phy-exynos4x12-usb2.c b/drivers/phy/phy-exynos4x12-usb2.c
new file mode 100644 (file)
index 0000000..0b9de88
--- /dev/null
@@ -0,0 +1,379 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 4x12 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define EXYNOS_4x12_UPHYPWR                    0x0
+
+#define EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND       BIT(0)
+#define EXYNOS_4x12_UPHYPWR_PHY0_PWR           BIT(3)
+#define EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR       BIT(4)
+#define EXYNOS_4x12_UPHYPWR_PHY0_SLEEP         BIT(5)
+#define EXYNOS_4x12_UPHYPWR_PHY0 ( \
+       EXYNOS_4x12_UPHYPWR_PHY0_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_PHY0_PWR | \
+       EXYNOS_4x12_UPHYPWR_PHY0_OTG_PWR | \
+       EXYNOS_4x12_UPHYPWR_PHY0_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND       BIT(6)
+#define EXYNOS_4x12_UPHYPWR_PHY1_PWR           BIT(7)
+#define EXYNOS_4x12_UPHYPWR_PHY1_SLEEP         BIT(8)
+#define EXYNOS_4x12_UPHYPWR_PHY1 ( \
+       EXYNOS_4x12_UPHYPWR_PHY1_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_PHY1_PWR | \
+       EXYNOS_4x12_UPHYPWR_PHY1_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND      BIT(9)
+#define EXYNOS_4x12_UPHYPWR_HSIC0_PWR          BIT(10)
+#define EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP                BIT(11)
+#define EXYNOS_4x12_UPHYPWR_HSIC0 ( \
+       EXYNOS_4x12_UPHYPWR_HSIC0_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_HSIC0_PWR | \
+       EXYNOS_4x12_UPHYPWR_HSIC0_SLEEP)
+
+#define EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND      BIT(12)
+#define EXYNOS_4x12_UPHYPWR_HSIC1_PWR          BIT(13)
+#define EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP                BIT(14)
+#define EXYNOS_4x12_UPHYPWR_HSIC1 ( \
+       EXYNOS_4x12_UPHYPWR_HSIC1_SUSPEND | \
+       EXYNOS_4x12_UPHYPWR_HSIC1_PWR | \
+       EXYNOS_4x12_UPHYPWR_HSIC1_SLEEP)
+
+/* PHY clock control */
+#define EXYNOS_4x12_UPHYCLK                    0x4
+
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK       (0x7 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET     0
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6      (0x0 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ      (0x1 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ      (0x2 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2     (0x3 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ      (0x4 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ      (0x5 << 0)
+#define EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ      (0x7 << 0)
+
+#define EXYNOS_3250_UPHYCLK_REFCLKSEL          (0x2 << 8)
+
+#define EXYNOS_4x12_UPHYCLK_PHY0_ID_PULLUP     BIT(3)
+#define EXYNOS_4x12_UPHYCLK_PHY0_COMMON_ON     BIT(4)
+#define EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON     BIT(7)
+
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_MASK   (0x7f << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_OFFSET  10
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_12MHZ  (0x24 << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_15MHZ  (0x1c << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_16MHZ  (0x1a << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_19MHZ2 (0x15 << 10)
+#define EXYNOS_4x12_UPHYCLK_HSIC_REFCLK_20MHZ  (0x14 << 10)
+
+/* PHY reset control */
+#define EXYNOS_4x12_UPHYRST                    0x8
+
+#define EXYNOS_4x12_URSTCON_PHY0               BIT(0)
+#define EXYNOS_4x12_URSTCON_OTG_HLINK          BIT(1)
+#define EXYNOS_4x12_URSTCON_OTG_PHYLINK                BIT(2)
+#define EXYNOS_4x12_URSTCON_HOST_PHY           BIT(3)
+/* The following bit defines are presented in the
+ * order taken from the Exynos4412 reference manual.
+ *
+ * During experiments with the hardware and debugging
+ * it was determined that the hardware behaves contrary
+ * to the manual.
+ *
+ * The following bit values were chaned accordingly to the
+ * results of real hardware experiments.
+ */
+#define EXYNOS_4x12_URSTCON_PHY1               BIT(4)
+#define EXYNOS_4x12_URSTCON_HSIC0              BIT(6)
+#define EXYNOS_4x12_URSTCON_HSIC1              BIT(5)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL      BIT(7)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P0       BIT(10)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P1       BIT(9)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P2       BIT(8)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_4x12_USB_ISOL_OFFSET            0x704
+#define EXYNOS_4x12_USB_ISOL_OTG               BIT(0)
+#define EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET      0x708
+#define EXYNOS_4x12_USB_ISOL_HSIC0             BIT(0)
+#define EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET      0x70c
+#define EXYNOS_4x12_USB_ISOL_HSIC1             BIT(0)
+
+/* Mode switching SUB Device <-> Host */
+#define EXYNOS_4x12_MODE_SWITCH_OFFSET         0x21c
+#define EXYNOS_4x12_MODE_SWITCH_MASK           1
+#define EXYNOS_4x12_MODE_SWITCH_DEVICE         0
+#define EXYNOS_4x12_MODE_SWITCH_HOST           1
+
+enum exynos4x12_phy_id {
+       EXYNOS4x12_DEVICE,
+       EXYNOS4x12_HOST,
+       EXYNOS4x12_HSIC0,
+       EXYNOS4x12_HSIC1,
+       EXYNOS4x12_NUM_PHYS,
+};
+
+/*
+ * exynos4x12_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos4x12_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       /* EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK */
+
+       switch (rate) {
+       case 9600 * KHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_9MHZ6;
+               break;
+       case 10 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_10MHZ;
+               break;
+       case 12 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_12MHZ;
+               break;
+       case 19200 * KHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_19MHZ2;
+               break;
+       case 20 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_20MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_24MHZ;
+               break;
+       case 50 * MHZ:
+               *reg = EXYNOS_4x12_UPHYCLK_PHYFSEL_50MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void exynos4x12_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 offset;
+       u32 mask;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4x12_DEVICE:
+       case EXYNOS4x12_HOST:
+               offset = EXYNOS_4x12_USB_ISOL_OFFSET;
+               mask = EXYNOS_4x12_USB_ISOL_OTG;
+               break;
+       case EXYNOS4x12_HSIC0:
+               offset = EXYNOS_4x12_USB_ISOL_HSIC0_OFFSET;
+               mask = EXYNOS_4x12_USB_ISOL_HSIC0;
+               break;
+       case EXYNOS4x12_HSIC1:
+               offset = EXYNOS_4x12_USB_ISOL_HSIC1_OFFSET;
+               mask = EXYNOS_4x12_USB_ISOL_HSIC1;
+               break;
+       default:
+               return;
+       };
+
+       regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static void exynos4x12_setup_clk(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 clk;
+
+       clk = readl(drv->reg_phy + EXYNOS_4x12_UPHYCLK);
+       clk &= ~EXYNOS_4x12_UPHYCLK_PHYFSEL_MASK;
+
+       if (drv->cfg->has_refclk_sel)
+               clk = EXYNOS_3250_UPHYCLK_REFCLKSEL;
+
+       clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
+       clk |= EXYNOS_4x12_UPHYCLK_PHY1_COMMON_ON;
+       writel(clk, drv->reg_phy + EXYNOS_4x12_UPHYCLK);
+}
+
+static void exynos4x12_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 rstbits = 0;
+       u32 phypwr = 0;
+       u32 rst;
+       u32 pwr;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4x12_DEVICE:
+               phypwr =        EXYNOS_4x12_UPHYPWR_PHY0;
+               rstbits =       EXYNOS_4x12_URSTCON_PHY0;
+               break;
+       case EXYNOS4x12_HOST:
+               phypwr =        EXYNOS_4x12_UPHYPWR_PHY1;
+               rstbits =       EXYNOS_4x12_URSTCON_HOST_PHY |
+                               EXYNOS_4x12_URSTCON_PHY1 |
+                               EXYNOS_4x12_URSTCON_HOST_LINK_P0;
+               break;
+       case EXYNOS4x12_HSIC0:
+               phypwr =        EXYNOS_4x12_UPHYPWR_HSIC0;
+               rstbits =       EXYNOS_4x12_URSTCON_HSIC0 |
+                               EXYNOS_4x12_URSTCON_HOST_LINK_P1;
+               break;
+       case EXYNOS4x12_HSIC1:
+               phypwr =        EXYNOS_4x12_UPHYPWR_HSIC1;
+               rstbits =       EXYNOS_4x12_URSTCON_HSIC1 |
+                               EXYNOS_4x12_URSTCON_HOST_LINK_P1;
+               break;
+       };
+
+       if (on) {
+               pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+               pwr &= ~phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+
+               rst = readl(drv->reg_phy + EXYNOS_4x12_UPHYRST);
+               rst |= rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
+               udelay(10);
+               rst &= ~rstbits;
+               writel(rst, drv->reg_phy + EXYNOS_4x12_UPHYRST);
+               /* The following delay is necessary for the reset sequence to be
+                * completed */
+               udelay(80);
+       } else {
+               pwr = readl(drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+               pwr |= phypwr;
+               writel(pwr, drv->reg_phy + EXYNOS_4x12_UPHYPWR);
+       }
+}
+
+static void exynos4x12_power_on_int(struct samsung_usb2_phy_instance *inst)
+{
+       if (inst->int_cnt++ > 0)
+               return;
+
+       exynos4x12_setup_clk(inst);
+       exynos4x12_isol(inst, 0);
+       exynos4x12_phy_pwr(inst, 1);
+}
+
+static int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+
+       if (inst->ext_cnt++ > 0)
+               return 0;
+
+       if (inst->cfg->id == EXYNOS4x12_HOST) {
+               regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
+                                               EXYNOS_4x12_MODE_SWITCH_MASK,
+                                               EXYNOS_4x12_MODE_SWITCH_HOST);
+               exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
+       }
+
+       if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
+               regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
+                                               EXYNOS_4x12_MODE_SWITCH_MASK,
+                                               EXYNOS_4x12_MODE_SWITCH_DEVICE);
+
+       if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
+               inst->cfg->id == EXYNOS4x12_HSIC1) {
+               exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_DEVICE]);
+               exynos4x12_power_on_int(&drv->instances[EXYNOS4x12_HOST]);
+       }
+
+       exynos4x12_power_on_int(inst);
+
+       return 0;
+}
+
+static void exynos4x12_power_off_int(struct samsung_usb2_phy_instance *inst)
+{
+       if (inst->int_cnt-- > 1)
+               return;
+
+       exynos4x12_isol(inst, 1);
+       exynos4x12_phy_pwr(inst, 0);
+}
+
+static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+
+       if (inst->ext_cnt-- > 1)
+               return 0;
+
+       if (inst->cfg->id == EXYNOS4x12_DEVICE && drv->cfg->has_mode_switch)
+               regmap_update_bits(drv->reg_sys, EXYNOS_4x12_MODE_SWITCH_OFFSET,
+                                               EXYNOS_4x12_MODE_SWITCH_MASK,
+                                               EXYNOS_4x12_MODE_SWITCH_HOST);
+
+       if (inst->cfg->id == EXYNOS4x12_HOST)
+               exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
+
+       if (inst->cfg->id == EXYNOS4x12_HSIC0 ||
+               inst->cfg->id == EXYNOS4x12_HSIC1) {
+               exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_DEVICE]);
+               exynos4x12_power_off_int(&drv->instances[EXYNOS4x12_HOST]);
+       }
+
+       exynos4x12_power_off_int(inst);
+
+       return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos4x12_phys[] = {
+       {
+               .label          = "device",
+               .id             = EXYNOS4x12_DEVICE,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {
+               .label          = "host",
+               .id             = EXYNOS4x12_HOST,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {
+               .label          = "hsic0",
+               .id             = EXYNOS4x12_HSIC0,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {
+               .label          = "hsic1",
+               .id             = EXYNOS4x12_HSIC1,
+               .power_on       = exynos4x12_power_on,
+               .power_off      = exynos4x12_power_off,
+       },
+       {},
+};
+
+const struct samsung_usb2_phy_config exynos3250_usb2_phy_config = {
+       .has_refclk_sel         = 1,
+       .num_phys               = 1,
+       .phys                   = exynos4x12_phys,
+       .rate_to_clk            = exynos4x12_rate_to_clk,
+};
+
+const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config = {
+       .has_mode_switch        = 1,
+       .num_phys               = EXYNOS4x12_NUM_PHYS,
+       .phys                   = exynos4x12_phys,
+       .rate_to_clk            = exynos4x12_rate_to_clk,
+};
diff --git a/drivers/phy/phy-exynos5-usbdrd.c b/drivers/phy/phy-exynos5-usbdrd.c
new file mode 100644 (file)
index 0000000..f756aca
--- /dev/null
@@ -0,0 +1,677 @@
+/*
+ * Samsung EXYNOS5 SoC series USB DRD PHY driver
+ *
+ * Phy provider for USB 3.0 DRD controller on Exynos5 SoC series
+ *
+ * Copyright (C) 2014 Samsung Electronics Co., Ltd.
+ * Author: Vivek Gautam <gautam.vivek@samsung.com>
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/mfd/syscon.h>
+#include <linux/mfd/syscon/exynos5-pmu.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+/* Exynos USB PHY registers */
+#define EXYNOS5_FSEL_9MHZ6             0x0
+#define EXYNOS5_FSEL_10MHZ             0x1
+#define EXYNOS5_FSEL_12MHZ             0x2
+#define EXYNOS5_FSEL_19MHZ2            0x3
+#define EXYNOS5_FSEL_20MHZ             0x4
+#define EXYNOS5_FSEL_24MHZ             0x5
+#define EXYNOS5_FSEL_50MHZ             0x7
+
+/* EXYNOS5: USB 3.0 DRD PHY registers */
+#define EXYNOS5_DRD_LINKSYSTEM                 0x04
+
+#define LINKSYSTEM_FLADJ_MASK                  (0x3f << 1)
+#define LINKSYSTEM_FLADJ(_x)                   ((_x) << 1)
+#define LINKSYSTEM_XHCI_VERSION_CONTROL                BIT(27)
+
+#define EXYNOS5_DRD_PHYUTMI                    0x08
+
+#define PHYUTMI_OTGDISABLE                     BIT(6)
+#define PHYUTMI_FORCESUSPEND                   BIT(1)
+#define PHYUTMI_FORCESLEEP                     BIT(0)
+
+#define EXYNOS5_DRD_PHYPIPE                    0x0c
+
+#define EXYNOS5_DRD_PHYCLKRST                  0x10
+
+#define PHYCLKRST_EN_UTMISUSPEND               BIT(31)
+
+#define PHYCLKRST_SSC_REFCLKSEL_MASK           (0xff << 23)
+#define PHYCLKRST_SSC_REFCLKSEL(_x)            ((_x) << 23)
+
+#define PHYCLKRST_SSC_RANGE_MASK               (0x03 << 21)
+#define PHYCLKRST_SSC_RANGE(_x)                        ((_x) << 21)
+
+#define PHYCLKRST_SSC_EN                       BIT(20)
+#define PHYCLKRST_REF_SSP_EN                   BIT(19)
+#define PHYCLKRST_REF_CLKDIV2                  BIT(18)
+
+#define PHYCLKRST_MPLL_MULTIPLIER_MASK         (0x7f << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER_100MHZ_REF   (0x19 << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER_50M_REF      (0x32 << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF    (0x68 << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF    (0x7d << 11)
+#define PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF (0x02 << 11)
+
+#define PHYCLKRST_FSEL_UTMI_MASK               (0x7 << 5)
+#define PHYCLKRST_FSEL_PIPE_MASK               (0x7 << 8)
+#define PHYCLKRST_FSEL(_x)                     ((_x) << 5)
+#define PHYCLKRST_FSEL_PAD_100MHZ              (0x27 << 5)
+#define PHYCLKRST_FSEL_PAD_24MHZ               (0x2a << 5)
+#define PHYCLKRST_FSEL_PAD_20MHZ               (0x31 << 5)
+#define PHYCLKRST_FSEL_PAD_19_2MHZ             (0x38 << 5)
+
+#define PHYCLKRST_RETENABLEN                   BIT(4)
+
+#define PHYCLKRST_REFCLKSEL_MASK               (0x03 << 2)
+#define PHYCLKRST_REFCLKSEL_PAD_REFCLK         (0x2 << 2)
+#define PHYCLKRST_REFCLKSEL_EXT_REFCLK         (0x3 << 2)
+
+#define PHYCLKRST_PORTRESET                    BIT(1)
+#define PHYCLKRST_COMMONONN                    BIT(0)
+
+#define EXYNOS5_DRD_PHYREG0                    0x14
+#define EXYNOS5_DRD_PHYREG1                    0x18
+
+#define EXYNOS5_DRD_PHYPARAM0                  0x1c
+
+#define PHYPARAM0_REF_USE_PAD                  BIT(31)
+#define PHYPARAM0_REF_LOSLEVEL_MASK            (0x1f << 26)
+#define PHYPARAM0_REF_LOSLEVEL                 (0x9 << 26)
+
+#define EXYNOS5_DRD_PHYPARAM1                  0x20
+
+#define PHYPARAM1_PCS_TXDEEMPH_MASK            (0x1f << 0)
+#define PHYPARAM1_PCS_TXDEEMPH                 (0x1c)
+
+#define EXYNOS5_DRD_PHYTERM                    0x24
+
+#define EXYNOS5_DRD_PHYTEST                    0x28
+
+#define PHYTEST_POWERDOWN_SSP                  BIT(3)
+#define PHYTEST_POWERDOWN_HSP                  BIT(2)
+
+#define EXYNOS5_DRD_PHYADP                     0x2c
+
+#define EXYNOS5_DRD_PHYUTMICLKSEL              0x30
+
+#define PHYUTMICLKSEL_UTMI_CLKSEL              BIT(2)
+
+#define EXYNOS5_DRD_PHYRESUME                  0x34
+#define EXYNOS5_DRD_LINKPORT                   0x44
+
+#define KHZ    1000
+#define MHZ    (KHZ * KHZ)
+
+enum exynos5_usbdrd_phy_id {
+       EXYNOS5_DRDPHY_UTMI,
+       EXYNOS5_DRDPHY_PIPE3,
+       EXYNOS5_DRDPHYS_NUM,
+};
+
+struct phy_usb_instance;
+struct exynos5_usbdrd_phy;
+
+struct exynos5_usbdrd_phy_config {
+       u32 id;
+       void (*phy_isol)(struct phy_usb_instance *inst, u32 on);
+       void (*phy_init)(struct exynos5_usbdrd_phy *phy_drd);
+       unsigned int (*set_refclk)(struct phy_usb_instance *inst);
+};
+
+struct exynos5_usbdrd_phy_drvdata {
+       const struct exynos5_usbdrd_phy_config *phy_cfg;
+       u32 pmu_offset_usbdrd0_phy;
+       u32 pmu_offset_usbdrd1_phy;
+};
+
+/**
+ * struct exynos5_usbdrd_phy - driver data for USB 3.0 PHY
+ * @dev: pointer to device instance of this platform device
+ * @reg_phy: usb phy controller register memory base
+ * @clk: phy clock for register access
+ * @drv_data: pointer to SoC level driver data structure
+ * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
+ *         instances each with its 'phy' and 'phy_cfg'.
+ * @extrefclk: frequency select settings when using 'separate
+ *            reference clocks' for SS and HS operations
+ * @ref_clk: reference clock to PHY block from which PHY's
+ *          operational clocks are derived
+ * @ref_rate: rate of above reference clock
+ */
+struct exynos5_usbdrd_phy {
+       struct device *dev;
+       void __iomem *reg_phy;
+       struct clk *clk;
+       const struct exynos5_usbdrd_phy_drvdata *drv_data;
+       struct phy_usb_instance {
+               struct phy *phy;
+               u32 index;
+               struct regmap *reg_pmu;
+               u32 pmu_offset;
+               const struct exynos5_usbdrd_phy_config *phy_cfg;
+       } phys[EXYNOS5_DRDPHYS_NUM];
+       u32 extrefclk;
+       struct clk *ref_clk;
+       struct regulator *vbus;
+};
+
+static inline
+struct exynos5_usbdrd_phy *to_usbdrd_phy(struct phy_usb_instance *inst)
+{
+       return container_of((inst), struct exynos5_usbdrd_phy,
+                           phys[(inst)->index]);
+}
+
+/*
+ * exynos5_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static unsigned int exynos5_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       /* EXYNOS5_FSEL_MASK */
+
+       switch (rate) {
+       case 9600 * KHZ:
+               *reg = EXYNOS5_FSEL_9MHZ6;
+               break;
+       case 10 * MHZ:
+               *reg = EXYNOS5_FSEL_10MHZ;
+               break;
+       case 12 * MHZ:
+               *reg = EXYNOS5_FSEL_12MHZ;
+               break;
+       case 19200 * KHZ:
+               *reg = EXYNOS5_FSEL_19MHZ2;
+               break;
+       case 20 * MHZ:
+               *reg = EXYNOS5_FSEL_20MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = EXYNOS5_FSEL_24MHZ;
+               break;
+       case 50 * MHZ:
+               *reg = EXYNOS5_FSEL_50MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void exynos5_usbdrd_phy_isol(struct phy_usb_instance *inst,
+                                               unsigned int on)
+{
+       unsigned int val;
+
+       if (!inst->reg_pmu)
+               return;
+
+       val = on ? 0 : EXYNOS5_PHY_ENABLE;
+
+       regmap_update_bits(inst->reg_pmu, inst->pmu_offset,
+                          EXYNOS5_PHY_ENABLE, val);
+}
+
+/*
+ * Sets the pipe3 phy's clk as EXTREFCLK (XXTI) which is internal clock
+ * from clock core. Further sets multiplier values and spread spectrum
+ * clock settings for SuperSpeed operations.
+ */
+static unsigned int
+exynos5_usbdrd_pipe3_set_refclk(struct phy_usb_instance *inst)
+{
+       static u32 reg;
+       struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+       /* restore any previous reference clock settings */
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
+
+       /* Use EXTREFCLK as ref clock */
+       reg &= ~PHYCLKRST_REFCLKSEL_MASK;
+       reg |=  PHYCLKRST_REFCLKSEL_EXT_REFCLK;
+
+       /* FSEL settings corresponding to reference clock */
+       reg &= ~PHYCLKRST_FSEL_PIPE_MASK |
+               PHYCLKRST_MPLL_MULTIPLIER_MASK |
+               PHYCLKRST_SSC_REFCLKSEL_MASK;
+       switch (phy_drd->extrefclk) {
+       case EXYNOS5_FSEL_50MHZ:
+               reg |= (PHYCLKRST_MPLL_MULTIPLIER_50M_REF |
+                       PHYCLKRST_SSC_REFCLKSEL(0x00));
+               break;
+       case EXYNOS5_FSEL_24MHZ:
+               reg |= (PHYCLKRST_MPLL_MULTIPLIER_24MHZ_REF |
+                       PHYCLKRST_SSC_REFCLKSEL(0x88));
+               break;
+       case EXYNOS5_FSEL_20MHZ:
+               reg |= (PHYCLKRST_MPLL_MULTIPLIER_20MHZ_REF |
+                       PHYCLKRST_SSC_REFCLKSEL(0x00));
+               break;
+       case EXYNOS5_FSEL_19MHZ2:
+               reg |= (PHYCLKRST_MPLL_MULTIPLIER_19200KHZ_REF |
+                       PHYCLKRST_SSC_REFCLKSEL(0x88));
+               break;
+       default:
+               dev_dbg(phy_drd->dev, "unsupported ref clk\n");
+               break;
+       }
+
+       return reg;
+}
+
+/*
+ * Sets the utmi phy's clk as EXTREFCLK (XXTI) which is internal clock
+ * from clock core. Further sets the FSEL values for HighSpeed operations.
+ */
+static unsigned int
+exynos5_usbdrd_utmi_set_refclk(struct phy_usb_instance *inst)
+{
+       static u32 reg;
+       struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+       /* restore any previous reference clock settings */
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
+
+       reg &= ~PHYCLKRST_REFCLKSEL_MASK;
+       reg |=  PHYCLKRST_REFCLKSEL_EXT_REFCLK;
+
+       reg &= ~PHYCLKRST_FSEL_UTMI_MASK |
+               PHYCLKRST_MPLL_MULTIPLIER_MASK |
+               PHYCLKRST_SSC_REFCLKSEL_MASK;
+       reg |= PHYCLKRST_FSEL(phy_drd->extrefclk);
+
+       return reg;
+}
+
+static void exynos5_usbdrd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd)
+{
+       u32 reg;
+
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
+       /* Set Tx De-Emphasis level */
+       reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
+       reg |=  PHYPARAM1_PCS_TXDEEMPH;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
+
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+       reg &= ~PHYTEST_POWERDOWN_SSP;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+}
+
+static void exynos5_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd)
+{
+       u32 reg;
+
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
+       /* Set Loss-of-Signal Detector sensitivity */
+       reg &= ~PHYPARAM0_REF_LOSLEVEL_MASK;
+       reg |=  PHYPARAM0_REF_LOSLEVEL;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
+
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
+       /* Set Tx De-Emphasis level */
+       reg &= ~PHYPARAM1_PCS_TXDEEMPH_MASK;
+       reg |=  PHYPARAM1_PCS_TXDEEMPH;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM1);
+
+       /* UTMI Power Control */
+       writel(PHYUTMI_OTGDISABLE, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI);
+
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+       reg &= ~PHYTEST_POWERDOWN_HSP;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+}
+
+static int exynos5_usbdrd_phy_init(struct phy *phy)
+{
+       int ret;
+       u32 reg;
+       struct phy_usb_instance *inst = phy_get_drvdata(phy);
+       struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+       ret = clk_prepare_enable(phy_drd->clk);
+       if (ret)
+               return ret;
+
+       /* Reset USB 3.0 PHY */
+       writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYREG0);
+       writel(0x0, phy_drd->reg_phy + EXYNOS5_DRD_PHYRESUME);
+
+       /*
+        * Setting the Frame length Adj value[6:1] to default 0x20
+        * See xHCI 1.0 spec, 5.2.4
+        */
+       reg =   LINKSYSTEM_XHCI_VERSION_CONTROL |
+               LINKSYSTEM_FLADJ(0x20);
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_LINKSYSTEM);
+
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
+       /* Select PHY CLK source */
+       reg &= ~PHYPARAM0_REF_USE_PAD;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYPARAM0);
+
+       /* This bit must be set for both HS and SS operations */
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL);
+       reg |= PHYUTMICLKSEL_UTMI_CLKSEL;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMICLKSEL);
+
+       /* UTMI or PIPE3 specific init */
+       inst->phy_cfg->phy_init(phy_drd);
+
+       /* reference clock settings */
+       reg = inst->phy_cfg->set_refclk(inst);
+
+               /* Digital power supply in normal operating mode */
+       reg |=  PHYCLKRST_RETENABLEN |
+               /* Enable ref clock for SS function */
+               PHYCLKRST_REF_SSP_EN |
+               /* Enable spread spectrum */
+               PHYCLKRST_SSC_EN |
+               /* Power down HS Bias and PLL blocks in suspend mode */
+               PHYCLKRST_COMMONONN |
+               /* Reset the port */
+               PHYCLKRST_PORTRESET;
+
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
+
+       udelay(10);
+
+       reg &= ~PHYCLKRST_PORTRESET;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
+
+       clk_disable_unprepare(phy_drd->clk);
+
+       return 0;
+}
+
+static int exynos5_usbdrd_phy_exit(struct phy *phy)
+{
+       int ret;
+       u32 reg;
+       struct phy_usb_instance *inst = phy_get_drvdata(phy);
+       struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+       ret = clk_prepare_enable(phy_drd->clk);
+       if (ret)
+               return ret;
+
+       reg =   PHYUTMI_OTGDISABLE |
+               PHYUTMI_FORCESUSPEND |
+               PHYUTMI_FORCESLEEP;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYUTMI);
+
+       /* Resetting the PHYCLKRST enable bits to reduce leakage current */
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
+       reg &= ~(PHYCLKRST_REF_SSP_EN |
+                PHYCLKRST_SSC_EN |
+                PHYCLKRST_COMMONONN);
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYCLKRST);
+
+       /* Control PHYTEST to remove leakage current */
+       reg = readl(phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+       reg |=  PHYTEST_POWERDOWN_SSP |
+               PHYTEST_POWERDOWN_HSP;
+       writel(reg, phy_drd->reg_phy + EXYNOS5_DRD_PHYTEST);
+
+       clk_disable_unprepare(phy_drd->clk);
+
+       return 0;
+}
+
+static int exynos5_usbdrd_phy_power_on(struct phy *phy)
+{
+       int ret;
+       struct phy_usb_instance *inst = phy_get_drvdata(phy);
+       struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+       dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
+
+       clk_prepare_enable(phy_drd->ref_clk);
+
+       /* Enable VBUS supply */
+       if (phy_drd->vbus) {
+               ret = regulator_enable(phy_drd->vbus);
+               if (ret) {
+                       dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
+                       goto fail_vbus;
+               }
+       }
+
+       /* Power-on PHY*/
+       inst->phy_cfg->phy_isol(inst, 0);
+
+       return 0;
+
+fail_vbus:
+       clk_disable_unprepare(phy_drd->ref_clk);
+
+       return ret;
+}
+
+static int exynos5_usbdrd_phy_power_off(struct phy *phy)
+{
+       struct phy_usb_instance *inst = phy_get_drvdata(phy);
+       struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst);
+
+       dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n");
+
+       /* Power-off the PHY */
+       inst->phy_cfg->phy_isol(inst, 1);
+
+       /* Disable VBUS supply */
+       if (phy_drd->vbus)
+               regulator_disable(phy_drd->vbus);
+
+       clk_disable_unprepare(phy_drd->ref_clk);
+
+       return 0;
+}
+
+static struct phy *exynos5_usbdrd_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct exynos5_usbdrd_phy *phy_drd = dev_get_drvdata(dev);
+
+       if (WARN_ON(args->args[0] > EXYNOS5_DRDPHYS_NUM))
+               return ERR_PTR(-ENODEV);
+
+       return phy_drd->phys[args->args[0]].phy;
+}
+
+static struct phy_ops exynos5_usbdrd_phy_ops = {
+       .init           = exynos5_usbdrd_phy_init,
+       .exit           = exynos5_usbdrd_phy_exit,
+       .power_on       = exynos5_usbdrd_phy_power_on,
+       .power_off      = exynos5_usbdrd_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
+       {
+               .id             = EXYNOS5_DRDPHY_UTMI,
+               .phy_isol       = exynos5_usbdrd_phy_isol,
+               .phy_init       = exynos5_usbdrd_utmi_init,
+               .set_refclk     = exynos5_usbdrd_utmi_set_refclk,
+       },
+       {
+               .id             = EXYNOS5_DRDPHY_PIPE3,
+               .phy_isol       = exynos5_usbdrd_phy_isol,
+               .phy_init       = exynos5_usbdrd_pipe3_init,
+               .set_refclk     = exynos5_usbdrd_pipe3_set_refclk,
+       },
+};
+
+static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
+       .phy_cfg                = phy_cfg_exynos5,
+       .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
+       .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
+};
+
+static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
+       .phy_cfg                = phy_cfg_exynos5,
+       .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
+};
+
+static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
+       {
+               .compatible = "samsung,exynos5250-usbdrd-phy",
+               .data = &exynos5250_usbdrd_phy
+       }, {
+               .compatible = "samsung,exynos5420-usbdrd-phy",
+               .data = &exynos5420_usbdrd_phy
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos5_usbdrd_phy_of_match);
+
+static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_node *node = dev->of_node;
+       struct exynos5_usbdrd_phy *phy_drd;
+       struct phy_provider *phy_provider;
+       struct resource *res;
+       const struct of_device_id *match;
+       const struct exynos5_usbdrd_phy_drvdata *drv_data;
+       struct regmap *reg_pmu;
+       u32 pmu_offset;
+       unsigned long ref_rate;
+       int i, ret;
+       int channel;
+
+       phy_drd = devm_kzalloc(dev, sizeof(*phy_drd), GFP_KERNEL);
+       if (!phy_drd)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, phy_drd);
+       phy_drd->dev = dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy_drd->reg_phy = devm_ioremap_resource(dev, res);
+       if (IS_ERR(phy_drd->reg_phy))
+               return PTR_ERR(phy_drd->reg_phy);
+
+       match = of_match_node(exynos5_usbdrd_phy_of_match, pdev->dev.of_node);
+
+       drv_data = match->data;
+       phy_drd->drv_data = drv_data;
+
+       phy_drd->clk = devm_clk_get(dev, "phy");
+       if (IS_ERR(phy_drd->clk)) {
+               dev_err(dev, "Failed to get clock of phy controller\n");
+               return PTR_ERR(phy_drd->clk);
+       }
+
+       phy_drd->ref_clk = devm_clk_get(dev, "ref");
+       if (IS_ERR(phy_drd->ref_clk)) {
+               dev_err(dev, "Failed to get reference clock of usbdrd phy\n");
+               return PTR_ERR(phy_drd->ref_clk);
+       }
+       ref_rate = clk_get_rate(phy_drd->ref_clk);
+
+       ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
+       if (ret) {
+               dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
+                       ref_rate);
+               return ret;
+       }
+
+       reg_pmu = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                                  "samsung,pmu-syscon");
+       if (IS_ERR(reg_pmu)) {
+               dev_err(dev, "Failed to lookup PMU regmap\n");
+               return PTR_ERR(reg_pmu);
+       }
+
+       /*
+        * Exynos5420 SoC has multiple channels for USB 3.0 PHY, with
+        * each having separate power control registers.
+        * 'channel' facilitates to set such registers.
+        */
+       channel = of_alias_get_id(node, "usbdrdphy");
+       if (channel < 0)
+               dev_dbg(dev, "Not a multi-controller usbdrd phy\n");
+
+       switch (channel) {
+       case 1:
+               pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd1_phy;
+               break;
+       case 0:
+       default:
+               pmu_offset = phy_drd->drv_data->pmu_offset_usbdrd0_phy;
+               break;
+       }
+
+       /* Get Vbus regulator */
+       phy_drd->vbus = devm_regulator_get(dev, "vbus");
+       if (IS_ERR(phy_drd->vbus)) {
+               ret = PTR_ERR(phy_drd->vbus);
+               if (ret == -EPROBE_DEFER)
+                       return ret;
+
+               dev_warn(dev, "Failed to get VBUS supply regulator\n");
+               phy_drd->vbus = NULL;
+       }
+
+       dev_vdbg(dev, "Creating usbdrd_phy phy\n");
+
+       for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
+               struct phy *phy = devm_phy_create(dev, NULL,
+                                                 &exynos5_usbdrd_phy_ops,
+                                                 NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "Failed to create usbdrd_phy phy\n");
+                       return PTR_ERR(phy);
+               }
+
+               phy_drd->phys[i].phy = phy;
+               phy_drd->phys[i].index = i;
+               phy_drd->phys[i].reg_pmu = reg_pmu;
+               phy_drd->phys[i].pmu_offset = pmu_offset;
+               phy_drd->phys[i].phy_cfg = &drv_data->phy_cfg[i];
+               phy_set_drvdata(phy, &phy_drd->phys[i]);
+       }
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                                    exynos5_usbdrd_phy_xlate);
+       if (IS_ERR(phy_provider)) {
+               dev_err(phy_drd->dev, "Failed to register phy provider\n");
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static struct platform_driver exynos5_usb3drd_phy = {
+       .probe  = exynos5_usbdrd_phy_probe,
+       .driver = {
+               .of_match_table = exynos5_usbdrd_phy_of_match,
+               .name           = "exynos5_usb3drd_phy",
+       }
+};
+
+module_platform_driver(exynos5_usb3drd_phy);
+MODULE_DESCRIPTION("Samsung EXYNOS5 SoCs USB 3.0 DRD controller PHY driver");
+MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:exynos5_usb3drd_phy");
diff --git a/drivers/phy/phy-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c
new file mode 100644 (file)
index 0000000..54cf4ae
--- /dev/null
@@ -0,0 +1,250 @@
+/*
+ * Samsung SATA SerDes(PHY) driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Authors: Girish K S <ks.giri@samsung.com>
+ *         Yuvaraj Kumar C D <yuvaraj.cd@samsung.com>
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
+
+#define SATAPHY_CONTROL_OFFSET         0x0724
+#define EXYNOS5_SATAPHY_PMU_ENABLE     BIT(0)
+#define EXYNOS5_SATA_RESET             0x4
+#define RESET_GLOBAL_RST_N             BIT(0)
+#define RESET_CMN_RST_N                        BIT(1)
+#define RESET_CMN_BLOCK_RST_N          BIT(2)
+#define RESET_CMN_I2C_RST_N            BIT(3)
+#define RESET_TX_RX_PIPE_RST_N         BIT(4)
+#define RESET_TX_RX_BLOCK_RST_N                BIT(5)
+#define RESET_TX_RX_I2C_RST_N          (BIT(6) | BIT(7))
+#define LINK_RESET                     0xf0000
+#define EXYNOS5_SATA_MODE0             0x10
+#define SATA_SPD_GEN3                  BIT(1)
+#define EXYNOS5_SATA_CTRL0             0x14
+#define CTRL0_P0_PHY_CALIBRATED_SEL    BIT(9)
+#define CTRL0_P0_PHY_CALIBRATED                BIT(8)
+#define EXYNOS5_SATA_PHSATA_CTRLM      0xe0
+#define PHCTRLM_REF_RATE               BIT(1)
+#define PHCTRLM_HIGH_SPEED             BIT(0)
+#define EXYNOS5_SATA_PHSATA_STATM      0xf0
+#define PHSTATM_PLL_LOCKED             BIT(0)
+
+#define PHY_PLL_TIMEOUT (usecs_to_jiffies(1000))
+
+struct exynos_sata_phy {
+       struct phy *phy;
+       struct clk *phyclk;
+       void __iomem *regs;
+       struct regmap *pmureg;
+       struct i2c_client *client;
+};
+
+static int wait_for_reg_status(void __iomem *base, u32 reg, u32 checkbit,
+                               u32 status)
+{
+       unsigned long timeout = jiffies + PHY_PLL_TIMEOUT;
+
+       while (time_before(jiffies, timeout)) {
+               if ((readl(base + reg) & checkbit) == status)
+                       return 0;
+       }
+
+       return -EFAULT;
+}
+
+static int exynos_sata_phy_power_on(struct phy *phy)
+{
+       struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
+
+       return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
+                       EXYNOS5_SATAPHY_PMU_ENABLE, true);
+
+}
+
+static int exynos_sata_phy_power_off(struct phy *phy)
+{
+       struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
+
+       return regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
+                       EXYNOS5_SATAPHY_PMU_ENABLE, false);
+
+}
+
+static int exynos_sata_phy_init(struct phy *phy)
+{
+       u32 val = 0;
+       int ret = 0;
+       u8 buf[] = { 0x3a, 0x0b };
+       struct exynos_sata_phy *sata_phy = phy_get_drvdata(phy);
+
+       ret = regmap_update_bits(sata_phy->pmureg, SATAPHY_CONTROL_OFFSET,
+                       EXYNOS5_SATAPHY_PMU_ENABLE, true);
+       if (ret != 0)
+               dev_err(&sata_phy->phy->dev, "phy init failed\n");
+
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= RESET_GLOBAL_RST_N | RESET_CMN_RST_N | RESET_CMN_BLOCK_RST_N
+               | RESET_CMN_I2C_RST_N | RESET_TX_RX_PIPE_RST_N
+               | RESET_TX_RX_BLOCK_RST_N | RESET_TX_RX_I2C_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= LINK_RESET;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= RESET_CMN_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+       val &= ~PHCTRLM_REF_RATE;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+
+       /* High speed enable for Gen3 */
+       val = readl(sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+       val |= PHCTRLM_HIGH_SPEED;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_PHSATA_CTRLM);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_CTRL0);
+       val |= CTRL0_P0_PHY_CALIBRATED_SEL | CTRL0_P0_PHY_CALIBRATED;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_CTRL0);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_MODE0);
+       val |= SATA_SPD_GEN3;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_MODE0);
+
+       ret = i2c_master_send(sata_phy->client, buf, sizeof(buf));
+       if (ret < 0)
+               return ret;
+
+       /* release cmu reset */
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val &= ~RESET_CMN_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       val = readl(sata_phy->regs + EXYNOS5_SATA_RESET);
+       val |= RESET_CMN_RST_N;
+       writel(val, sata_phy->regs + EXYNOS5_SATA_RESET);
+
+       ret = wait_for_reg_status(sata_phy->regs,
+                               EXYNOS5_SATA_PHSATA_STATM,
+                               PHSTATM_PLL_LOCKED, 1);
+       if (ret < 0)
+               dev_err(&sata_phy->phy->dev,
+                       "PHY PLL locking failed\n");
+       return ret;
+}
+
+static struct phy_ops exynos_sata_phy_ops = {
+       .init           = exynos_sata_phy_init,
+       .power_on       = exynos_sata_phy_power_on,
+       .power_off      = exynos_sata_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int exynos_sata_phy_probe(struct platform_device *pdev)
+{
+       struct exynos_sata_phy *sata_phy;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct device_node *node;
+       int ret = 0;
+
+       sata_phy = devm_kzalloc(dev, sizeof(*sata_phy), GFP_KERNEL);
+       if (!sata_phy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       sata_phy->regs = devm_ioremap_resource(dev, res);
+       if (IS_ERR(sata_phy->regs))
+               return PTR_ERR(sata_phy->regs);
+
+       sata_phy->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                       "samsung,syscon-phandle");
+       if (IS_ERR(sata_phy->pmureg)) {
+               dev_err(dev, "syscon regmap lookup failed.\n");
+               return PTR_ERR(sata_phy->pmureg);
+       }
+
+       node = of_parse_phandle(dev->of_node,
+                       "samsung,exynos-sataphy-i2c-phandle", 0);
+       if (!node)
+               return -EINVAL;
+
+       sata_phy->client = of_find_i2c_device_by_node(node);
+       if (!sata_phy->client)
+               return -EPROBE_DEFER;
+
+       dev_set_drvdata(dev, sata_phy);
+
+       sata_phy->phyclk = devm_clk_get(dev, "sata_phyctrl");
+       if (IS_ERR(sata_phy->phyclk)) {
+               dev_err(dev, "failed to get clk for PHY\n");
+               return PTR_ERR(sata_phy->phyclk);
+       }
+
+       ret = clk_prepare_enable(sata_phy->phyclk);
+       if (ret < 0) {
+               dev_err(dev, "failed to enable source clk\n");
+               return ret;
+       }
+
+       sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL);
+       if (IS_ERR(sata_phy->phy)) {
+               clk_disable_unprepare(sata_phy->phyclk);
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(sata_phy->phy);
+       }
+
+       phy_set_drvdata(sata_phy->phy, sata_phy);
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                       of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               clk_disable_unprepare(sata_phy->phyclk);
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static const struct of_device_id exynos_sata_phy_of_match[] = {
+       { .compatible = "samsung,exynos5250-sata-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, exynos_sata_phy_of_match);
+
+static struct platform_driver exynos_sata_phy_driver = {
+       .probe  = exynos_sata_phy_probe,
+       .driver = {
+               .of_match_table = exynos_sata_phy_of_match,
+               .name  = "samsung,sata-phy",
+       }
+};
+module_platform_driver(exynos_sata_phy_driver);
+
+MODULE_DESCRIPTION("Samsung SerDes PHY driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Girish K S <ks.giri@samsung.com>");
+MODULE_AUTHOR("Yuvaraj C D <yuvaraj.cd@samsung.com>");
diff --git a/drivers/phy/phy-exynos5250-usb2.c b/drivers/phy/phy-exynos5250-usb2.c
new file mode 100644 (file)
index 0000000..1c139aa
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - Exynos 5250 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+#define EXYNOS_5250_REFCLKSEL_CRYSTAL  0x0
+#define EXYNOS_5250_REFCLKSEL_XO       0x1
+#define EXYNOS_5250_REFCLKSEL_CLKCORE  0x2
+
+#define EXYNOS_5250_FSEL_9MHZ6         0x0
+#define EXYNOS_5250_FSEL_10MHZ         0x1
+#define EXYNOS_5250_FSEL_12MHZ         0x2
+#define EXYNOS_5250_FSEL_19MHZ2                0x3
+#define EXYNOS_5250_FSEL_20MHZ         0x4
+#define EXYNOS_5250_FSEL_24MHZ         0x5
+#define EXYNOS_5250_FSEL_50MHZ         0x7
+
+/* Normal host */
+#define EXYNOS_5250_HOSTPHYCTRL0                       0x0
+
+#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL           BIT(31)
+#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT       19
+#define EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_MASK        \
+               (0x3 << EXYNOS_5250_HOSTPHYCTRL0_REFCLKSEL_SHIFT)
+#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT            16
+#define EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK \
+               (0x7 << EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT)
+#define EXYNOS_5250_HOSTPHYCTRL0_TESTBURNIN            BIT(11)
+#define EXYNOS_5250_HOSTPHYCTRL0_RETENABLE             BIT(10)
+#define EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N           BIT(9)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_MASK                (0x3 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_DUAL                (0x0 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ID0         (0x1 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_VATESTENB_ANALOGTEST  (0x2 << 7)
+#define EXYNOS_5250_HOSTPHYCTRL0_SIDDQ                 BIT(6)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP            BIT(5)
+#define EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND          BIT(4)
+#define EXYNOS_5250_HOSTPHYCTRL0_WORDINTERFACE         BIT(3)
+#define EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST             BIT(2)
+#define EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST             BIT(1)
+#define EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST              BIT(0)
+
+/* HSIC0 & HSIC1 */
+#define EXYNOS_5250_HSICPHYCTRL1                       0x10
+#define EXYNOS_5250_HSICPHYCTRL2                       0x20
+
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_MASK                (0x3 << 23)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT     (0x2 << 23)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_MASK                (0x7f << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12          (0x24 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_15          (0x1c << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_16          (0x1a << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_19_2                (0x15 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_20          (0x14 << 16)
+#define EXYNOS_5250_HSICPHYCTRLX_SIDDQ                 BIT(6)
+#define EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP            BIT(5)
+#define EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND          BIT(4)
+#define EXYNOS_5250_HSICPHYCTRLX_WORDINTERFACE         BIT(3)
+#define EXYNOS_5250_HSICPHYCTRLX_UTMISWRST             BIT(2)
+#define EXYNOS_5250_HSICPHYCTRLX_PHYSWRST              BIT(0)
+
+/* EHCI control */
+#define EXYNOS_5250_HOSTEHCICTRL                       0x30
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN         BIT(29)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR4              BIT(28)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR8              BIT(27)
+#define EXYNOS_5250_HOSTEHCICTRL_ENAINCR16             BIT(26)
+#define EXYNOS_5250_HOSTEHCICTRL_AUTOPPDONOVRCUREN     BIT(25)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT       19
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK        \
+               (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT       13
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_MASK        \
+               (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL1_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL2_SHIFT       7
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_MASK        \
+               (0x3f << EXYNOS_5250_HOSTEHCICTRL_FLADJVAL0_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT    1
+#define EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_MASK \
+               (0x1 << EXYNOS_5250_HOSTEHCICTRL_FLADJVALHOST_SHIFT)
+#define EXYNOS_5250_HOSTEHCICTRL_SIMULATIONMODE                BIT(0)
+
+/* OHCI control */
+#define EXYNOS_5250_HOSTOHCICTRL                        0x34
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT     1
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_MASK \
+               (0x3ff << EXYNOS_5250_HOSTOHCICTRL_FRAMELENVAL_SHIFT)
+#define EXYNOS_5250_HOSTOHCICTRL_FRAMELENVALEN         BIT(0)
+
+/* USBOTG */
+#define EXYNOS_5250_USBOTGSYS                          0x38
+#define EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET         BIT(14)
+#define EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG         BIT(13)
+#define EXYNOS_5250_USBOTGSYS_PHY_SW_RST               BIT(12)
+#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT          9
+#define EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK \
+               (0x3 << EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT)
+#define EXYNOS_5250_USBOTGSYS_ID_PULLUP                        BIT(8)
+#define EXYNOS_5250_USBOTGSYS_COMMON_ON                        BIT(7)
+#define EXYNOS_5250_USBOTGSYS_FSEL_SHIFT               4
+#define EXYNOS_5250_USBOTGSYS_FSEL_MASK \
+               (0x3 << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT)
+#define EXYNOS_5250_USBOTGSYS_FORCE_SLEEP              BIT(3)
+#define EXYNOS_5250_USBOTGSYS_OTGDISABLE               BIT(2)
+#define EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG               BIT(1)
+#define EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND            BIT(0)
+
+/* Isolation, configured in the power management unit */
+#define EXYNOS_5250_USB_ISOL_OTG_OFFSET                0x704
+#define EXYNOS_5250_USB_ISOL_OTG               BIT(0)
+#define EXYNOS_5250_USB_ISOL_HOST_OFFSET       0x708
+#define EXYNOS_5250_USB_ISOL_HOST              BIT(0)
+
+/* Mode swtich register */
+#define EXYNOS_5250_MODE_SWITCH_OFFSET         0x230
+#define EXYNOS_5250_MODE_SWITCH_MASK           1
+#define EXYNOS_5250_MODE_SWITCH_DEVICE         0
+#define EXYNOS_5250_MODE_SWITCH_HOST           1
+
+enum exynos4x12_phy_id {
+       EXYNOS5250_DEVICE,
+       EXYNOS5250_HOST,
+       EXYNOS5250_HSIC0,
+       EXYNOS5250_HSIC1,
+       EXYNOS5250_NUM_PHYS,
+};
+
+/*
+ * exynos5250_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int exynos5250_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       /* EXYNOS_5250_FSEL_MASK */
+
+       switch (rate) {
+       case 9600 * KHZ:
+               *reg = EXYNOS_5250_FSEL_9MHZ6;
+               break;
+       case 10 * MHZ:
+               *reg = EXYNOS_5250_FSEL_10MHZ;
+               break;
+       case 12 * MHZ:
+               *reg = EXYNOS_5250_FSEL_12MHZ;
+               break;
+       case 19200 * KHZ:
+               *reg = EXYNOS_5250_FSEL_19MHZ2;
+               break;
+       case 20 * MHZ:
+               *reg = EXYNOS_5250_FSEL_20MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = EXYNOS_5250_FSEL_24MHZ;
+               break;
+       case 50 * MHZ:
+               *reg = EXYNOS_5250_FSEL_50MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void exynos5250_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 offset;
+       u32 mask;
+
+       switch (inst->cfg->id) {
+       case EXYNOS5250_DEVICE:
+               offset = EXYNOS_5250_USB_ISOL_OTG_OFFSET;
+               mask = EXYNOS_5250_USB_ISOL_OTG;
+               break;
+       case EXYNOS5250_HOST:
+               offset = EXYNOS_5250_USB_ISOL_HOST_OFFSET;
+               mask = EXYNOS_5250_USB_ISOL_HOST;
+               break;
+       default:
+               return;
+       };
+
+       regmap_update_bits(drv->reg_pmu, offset, mask, on ? 0 : mask);
+}
+
+static int exynos5250_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 ctrl0;
+       u32 otg;
+       u32 ehci;
+       u32 ohci;
+       u32 hsic;
+
+       switch (inst->cfg->id) {
+       case EXYNOS5250_DEVICE:
+               regmap_update_bits(drv->reg_sys,
+                                  EXYNOS_5250_MODE_SWITCH_OFFSET,
+                                  EXYNOS_5250_MODE_SWITCH_MASK,
+                                  EXYNOS_5250_MODE_SWITCH_DEVICE);
+
+               /* OTG configuration */
+               otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               /* The clock */
+               otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
+               otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+               /* Reset */
+               otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+                       EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+                       EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+               otg |=  EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_OTGDISABLE;
+               /* Ref clock */
+               otg &=  ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
+               otg |=  EXYNOS_5250_REFCLKSEL_CLKCORE <<
+                                       EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               udelay(100);
+               otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+                       EXYNOS_5250_USBOTGSYS_OTGDISABLE);
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+
+
+               break;
+       case EXYNOS5250_HOST:
+       case EXYNOS5250_HSIC0:
+       case EXYNOS5250_HSIC1:
+               /* Host registers configuration */
+               ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               /* The clock */
+               ctrl0 &= ~EXYNOS_5250_HOSTPHYCTRL0_FSEL_MASK;
+               ctrl0 |= drv->ref_reg_val <<
+                                       EXYNOS_5250_HOSTPHYCTRL0_FSEL_SHIFT;
+
+               /* Reset */
+               ctrl0 &=        ~(EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL |
+                               EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP);
+               ctrl0 |=        EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_COMMON_ON_N;
+               writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               udelay(10);
+               ctrl0 &=        ~(EXYNOS_5250_HOSTPHYCTRL0_LINKSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_UTMISWRST);
+               writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+
+               /* OTG configuration */
+               otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               /* The clock */
+               otg &= ~EXYNOS_5250_USBOTGSYS_FSEL_MASK;
+               otg |= drv->ref_reg_val << EXYNOS_5250_USBOTGSYS_FSEL_SHIFT;
+               /* Reset */
+               otg &= ~(EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+                       EXYNOS_5250_USBOTGSYS_FORCE_SLEEP |
+                       EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG);
+               otg |=  EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_OTGDISABLE;
+               /* Ref clock */
+               otg &=  ~EXYNOS_5250_USBOTGSYS_REFCLKSEL_MASK;
+               otg |=  EXYNOS_5250_REFCLKSEL_CLKCORE <<
+                                       EXYNOS_5250_USBOTGSYS_REFCLKSEL_SHIFT;
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               udelay(10);
+               otg &= ~(EXYNOS_5250_USBOTGSYS_PHY_SW_RST |
+                       EXYNOS_5250_USBOTGSYS_LINK_SW_RST_UOTG |
+                       EXYNOS_5250_USBOTGSYS_PHYLINK_SW_RESET);
+
+               /* HSIC phy configuration */
+               hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
+                               EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
+                               EXYNOS_5250_HSICPHYCTRLX_PHYSWRST);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+               udelay(10);
+               hsic &= ~EXYNOS_5250_HSICPHYCTRLX_PHYSWRST;
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+               /* The following delay is necessary for the reset sequence to be
+                * completed */
+               udelay(80);
+
+               /* Enable EHCI DMA burst */
+               ehci = readl(drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
+               ehci |= EXYNOS_5250_HOSTEHCICTRL_ENAINCRXALIGN |
+                       EXYNOS_5250_HOSTEHCICTRL_ENAINCR4 |
+                       EXYNOS_5250_HOSTEHCICTRL_ENAINCR8 |
+                       EXYNOS_5250_HOSTEHCICTRL_ENAINCR16;
+               writel(ehci, drv->reg_phy + EXYNOS_5250_HOSTEHCICTRL);
+
+               /* OHCI settings */
+               ohci = readl(drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
+               /* Following code is based on the old driver */
+               ohci |= 0x1 << 3;
+               writel(ohci, drv->reg_phy + EXYNOS_5250_HOSTOHCICTRL);
+
+               break;
+       }
+       exynos5250_isol(inst, 0);
+
+       return 0;
+}
+
+static int exynos5250_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 ctrl0;
+       u32 otg;
+       u32 hsic;
+
+       exynos5250_isol(inst, 1);
+
+       switch (inst->cfg->id) {
+       case EXYNOS5250_DEVICE:
+               otg = readl(drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               otg |= (EXYNOS_5250_USBOTGSYS_FORCE_SUSPEND |
+                       EXYNOS_5250_USBOTGSYS_SIDDQ_UOTG |
+                       EXYNOS_5250_USBOTGSYS_FORCE_SLEEP);
+               writel(otg, drv->reg_phy + EXYNOS_5250_USBOTGSYS);
+               break;
+       case EXYNOS5250_HOST:
+               ctrl0 = readl(drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               ctrl0 |= (EXYNOS_5250_HOSTPHYCTRL0_SIDDQ |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESUSPEND |
+                               EXYNOS_5250_HOSTPHYCTRL0_FORCESLEEP |
+                               EXYNOS_5250_HOSTPHYCTRL0_PHYSWRST |
+                               EXYNOS_5250_HOSTPHYCTRL0_PHYSWRSTALL);
+               writel(ctrl0, drv->reg_phy + EXYNOS_5250_HOSTPHYCTRL0);
+               break;
+       case EXYNOS5250_HSIC0:
+       case EXYNOS5250_HSIC1:
+               hsic = (EXYNOS_5250_HSICPHYCTRLX_REFCLKDIV_12 |
+                               EXYNOS_5250_HSICPHYCTRLX_REFCLKSEL_DEFAULT |
+                               EXYNOS_5250_HSICPHYCTRLX_SIDDQ |
+                               EXYNOS_5250_HSICPHYCTRLX_FORCESLEEP |
+                               EXYNOS_5250_HSICPHYCTRLX_FORCESUSPEND
+                               );
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL1);
+               writel(hsic, drv->reg_phy + EXYNOS_5250_HSICPHYCTRL2);
+               break;
+       }
+
+       return 0;
+}
+
+
+static const struct samsung_usb2_common_phy exynos5250_phys[] = {
+       {
+               .label          = "device",
+               .id             = EXYNOS5250_DEVICE,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {
+               .label          = "host",
+               .id             = EXYNOS5250_HOST,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {
+               .label          = "hsic0",
+               .id             = EXYNOS5250_HSIC0,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {
+               .label          = "hsic1",
+               .id             = EXYNOS5250_HSIC1,
+               .power_on       = exynos5250_power_on,
+               .power_off      = exynos5250_power_off,
+       },
+       {},
+};
+
+const struct samsung_usb2_phy_config exynos5250_usb2_phy_config = {
+       .has_mode_switch        = 1,
+       .num_phys               = EXYNOS5250_NUM_PHYS,
+       .phys                   = exynos5250_phys,
+       .rate_to_clk            = exynos5250_rate_to_clk,
+};
diff --git a/drivers/phy/phy-hix5hd2-sata.c b/drivers/phy/phy-hix5hd2-sata.c
new file mode 100644 (file)
index 0000000..d5d9780
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2014 Linaro Ltd.
+ * Copyright (c) 2014 Hisilicon Limited.
+ *
+ * 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.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+
+#define SATA_PHY0_CTLL         0xa0
+#define MPLL_MULTIPLIER_SHIFT  1
+#define MPLL_MULTIPLIER_MASK   0xfe
+#define MPLL_MULTIPLIER_50M    0x3c
+#define MPLL_MULTIPLIER_100M   0x1e
+#define PHY_RESET              BIT(0)
+#define REF_SSP_EN             BIT(9)
+#define SSC_EN                 BIT(10)
+#define REF_USE_PAD            BIT(23)
+
+#define SATA_PORT_PHYCTL       0x174
+#define SPEED_MODE_MASK                0x6f0000
+#define HALF_RATE_SHIFT                16
+#define PHY_CONFIG_SHIFT       18
+#define GEN2_EN_SHIFT          21
+#define SPEED_CTRL             BIT(20)
+
+#define SATA_PORT_PHYCTL1      0x148
+#define AMPLITUDE_MASK         0x3ffffe
+#define AMPLITUDE_GEN3         0x68
+#define AMPLITUDE_GEN3_SHIFT   15
+#define AMPLITUDE_GEN2         0x56
+#define AMPLITUDE_GEN2_SHIFT   8
+#define AMPLITUDE_GEN1         0x56
+#define AMPLITUDE_GEN1_SHIFT   1
+
+#define SATA_PORT_PHYCTL2      0x14c
+#define PREEMPH_MASK           0x3ffff
+#define PREEMPH_GEN3           0x20
+#define PREEMPH_GEN3_SHIFT     12
+#define PREEMPH_GEN2           0x15
+#define PREEMPH_GEN2_SHIFT     6
+#define PREEMPH_GEN1           0x5
+#define PREEMPH_GEN1_SHIFT     0
+
+struct hix5hd2_priv {
+       void __iomem    *base;
+       struct regmap   *peri_ctrl;
+};
+
+enum phy_speed_mode {
+       SPEED_MODE_GEN1 = 0,
+       SPEED_MODE_GEN2 = 1,
+       SPEED_MODE_GEN3 = 2,
+};
+
+static int hix5hd2_sata_phy_init(struct phy *phy)
+{
+       struct hix5hd2_priv *priv = phy_get_drvdata(phy);
+       u32 val, data[2];
+       int ret;
+
+       if (priv->peri_ctrl) {
+               ret = of_property_read_u32_array(phy->dev.of_node,
+                                                "hisilicon,power-reg",
+                                                &data[0], 2);
+               if (ret) {
+                       dev_err(&phy->dev, "Fail read hisilicon,power-reg\n");
+                       return ret;
+               }
+
+               regmap_update_bits(priv->peri_ctrl, data[0],
+                                  BIT(data[1]), BIT(data[1]));
+       }
+
+       /* reset phy */
+       val = readl_relaxed(priv->base + SATA_PHY0_CTLL);
+       val &= ~(MPLL_MULTIPLIER_MASK | REF_USE_PAD);
+       val |= MPLL_MULTIPLIER_50M << MPLL_MULTIPLIER_SHIFT |
+              REF_SSP_EN | PHY_RESET;
+       writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
+       msleep(20);
+       val &= ~PHY_RESET;
+       writel_relaxed(val, priv->base + SATA_PHY0_CTLL);
+
+       val = readl_relaxed(priv->base + SATA_PORT_PHYCTL1);
+       val &= ~AMPLITUDE_MASK;
+       val |= AMPLITUDE_GEN3 << AMPLITUDE_GEN3_SHIFT |
+              AMPLITUDE_GEN2 << AMPLITUDE_GEN2_SHIFT |
+              AMPLITUDE_GEN1 << AMPLITUDE_GEN1_SHIFT;
+       writel_relaxed(val, priv->base + SATA_PORT_PHYCTL1);
+
+       val = readl_relaxed(priv->base + SATA_PORT_PHYCTL2);
+       val &= ~PREEMPH_MASK;
+       val |= PREEMPH_GEN3 << PREEMPH_GEN3_SHIFT |
+              PREEMPH_GEN2 << PREEMPH_GEN2_SHIFT |
+              PREEMPH_GEN1 << PREEMPH_GEN1_SHIFT;
+       writel_relaxed(val, priv->base + SATA_PORT_PHYCTL2);
+
+       /* ensure PHYCTRL setting takes effect */
+       val = readl_relaxed(priv->base + SATA_PORT_PHYCTL);
+       val &= ~SPEED_MODE_MASK;
+       val |= SPEED_MODE_GEN1 << HALF_RATE_SHIFT |
+              SPEED_MODE_GEN1 << PHY_CONFIG_SHIFT |
+              SPEED_MODE_GEN1 << GEN2_EN_SHIFT | SPEED_CTRL;
+       writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
+
+       msleep(20);
+       val &= ~SPEED_MODE_MASK;
+       val |= SPEED_MODE_GEN3 << HALF_RATE_SHIFT |
+              SPEED_MODE_GEN3 << PHY_CONFIG_SHIFT |
+              SPEED_MODE_GEN3 << GEN2_EN_SHIFT | SPEED_CTRL;
+       writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
+
+       val &= ~(SPEED_MODE_MASK | SPEED_CTRL);
+       val |= SPEED_MODE_GEN2 << HALF_RATE_SHIFT |
+              SPEED_MODE_GEN2 << PHY_CONFIG_SHIFT |
+              SPEED_MODE_GEN2 << GEN2_EN_SHIFT;
+       writel_relaxed(val, priv->base + SATA_PORT_PHYCTL);
+
+       return 0;
+}
+
+static struct phy_ops hix5hd2_sata_phy_ops = {
+       .init           = hix5hd2_sata_phy_init,
+       .owner          = THIS_MODULE,
+};
+
+static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy *phy;
+       struct hix5hd2_priv *priv;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap(dev, res->start, resource_size(res));
+       if (!priv->base)
+               return -ENOMEM;
+
+       priv->peri_ctrl = syscon_regmap_lookup_by_phandle(dev->of_node,
+                                       "hisilicon,peripheral-syscon");
+       if (IS_ERR(priv->peri_ctrl))
+               priv->peri_ctrl = NULL;
+
+       phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL);
+       if (IS_ERR(phy)) {
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       phy_set_drvdata(phy, priv);
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       return 0;
+}
+
+static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
+       {.compatible = "hisilicon,hix5hd2-sata-phy",},
+       { },
+};
+MODULE_DEVICE_TABLE(of, hix5hd2_sata_phy_of_match);
+
+static struct platform_driver hix5hd2_sata_phy_driver = {
+       .probe  = hix5hd2_sata_phy_probe,
+       .driver = {
+               .name   = "hix5hd2-sata-phy",
+               .of_match_table = hix5hd2_sata_phy_of_match,
+       }
+};
+module_platform_driver(hix5hd2_sata_phy_driver);
+
+MODULE_AUTHOR("Jiancheng Xue <xuejiancheng@huawei.com>");
+MODULE_DESCRIPTION("HISILICON HIX5HD2 SATA PHY driver");
+MODULE_ALIAS("platform:hix5hd2-sata-phy");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-miphy365x.c b/drivers/phy/phy-miphy365x.c
new file mode 100644 (file)
index 0000000..801afaf
--- /dev/null
@@ -0,0 +1,636 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics â€“ All Rights Reserved
+ *
+ * STMicroelectronics PHY driver MiPHY365 (for SoC STiH416).
+ *
+ * Authors: Alexandre Torgue <alexandre.torgue@st.com>
+ *          Lee Jones <lee.jones@linaro.org>
+ *
+ * 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/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+
+#include <dt-bindings/phy/phy-miphy365x.h>
+
+#define HFC_TIMEOUT            100
+
+#define SYSCFG_SELECT_SATA_MASK        BIT(1)
+#define SYSCFG_SELECT_SATA_POS 1
+
+/* MiPHY365x register definitions */
+#define RESET_REG              0x00
+#define RST_PLL                        BIT(1)
+#define RST_PLL_CAL            BIT(2)
+#define RST_RX                 BIT(4)
+#define RST_MACRO              BIT(7)
+
+#define STATUS_REG             0x01
+#define IDLL_RDY               BIT(0)
+#define PLL_RDY                        BIT(1)
+#define DES_BIT_LOCK           BIT(2)
+#define DES_SYMBOL_LOCK                BIT(3)
+
+#define CTRL_REG               0x02
+#define TERM_EN                        BIT(0)
+#define PCI_EN                 BIT(2)
+#define DES_BIT_LOCK_EN                BIT(3)
+#define TX_POL                 BIT(5)
+
+#define INT_CTRL_REG           0x03
+
+#define BOUNDARY1_REG          0x10
+#define SPDSEL_SEL             BIT(0)
+
+#define BOUNDARY3_REG          0x12
+#define TX_SPDSEL_GEN1_VAL     0
+#define TX_SPDSEL_GEN2_VAL     0x01
+#define TX_SPDSEL_GEN3_VAL     0x02
+#define RX_SPDSEL_GEN1_VAL     0
+#define RX_SPDSEL_GEN2_VAL     (0x01 << 3)
+#define RX_SPDSEL_GEN3_VAL     (0x02 << 3)
+
+#define PCIE_REG               0x16
+
+#define BUF_SEL_REG            0x20
+#define CONF_GEN_SEL_GEN3      0x02
+#define CONF_GEN_SEL_GEN2      0x01
+#define PD_VDDTFILTER          BIT(4)
+
+#define TXBUF1_REG             0x21
+#define SWING_VAL              0x04
+#define SWING_VAL_GEN1         0x03
+#define PREEMPH_VAL            (0x3 << 5)
+
+#define TXBUF2_REG             0x22
+#define TXSLEW_VAL             0x2
+#define TXSLEW_VAL_GEN1                0x4
+
+#define RXBUF_OFFSET_CTRL_REG  0x23
+
+#define RXBUF_REG              0x25
+#define SDTHRES_VAL            0x01
+#define EQ_ON3                 (0x03 << 4)
+#define EQ_ON1                 (0x01 << 4)
+
+#define COMP_CTRL1_REG         0x40
+#define START_COMSR            BIT(0)
+#define START_COMZC            BIT(1)
+#define COMSR_DONE             BIT(2)
+#define COMZC_DONE             BIT(3)
+#define COMP_AUTO_LOAD         BIT(4)
+
+#define COMP_CTRL2_REG         0x41
+#define COMP_2MHZ_RAT_GEN1     0x1e
+#define COMP_2MHZ_RAT          0xf
+
+#define COMP_CTRL3_REG         0x42
+#define COMSR_COMP_REF         0x33
+
+#define COMP_IDLL_REG          0x47
+#define COMZC_IDLL             0x2a
+
+#define PLL_CTRL1_REG          0x50
+#define PLL_START_CAL          BIT(0)
+#define BUF_EN                 BIT(2)
+#define SYNCHRO_TX             BIT(3)
+#define SSC_EN                 BIT(6)
+#define CONFIG_PLL             BIT(7)
+
+#define PLL_CTRL2_REG          0x51
+#define BYPASS_PLL_CAL         BIT(1)
+
+#define PLL_RAT_REG            0x52
+
+#define PLL_SSC_STEP_MSB_REG   0x56
+#define PLL_SSC_STEP_MSB_VAL   0x03
+
+#define PLL_SSC_STEP_LSB_REG   0x57
+#define PLL_SSC_STEP_LSB_VAL   0x63
+
+#define PLL_SSC_PER_MSB_REG    0x58
+#define PLL_SSC_PER_MSB_VAL    0
+
+#define PLL_SSC_PER_LSB_REG    0x59
+#define PLL_SSC_PER_LSB_VAL    0xf1
+
+#define IDLL_TEST_REG          0x72
+#define START_CLK_HF           BIT(6)
+
+#define DES_BITLOCK_REG                0x86
+#define BIT_LOCK_LEVEL         0x01
+#define BIT_LOCK_CNT_512       (0x03 << 5)
+
+struct miphy365x_phy {
+       struct phy *phy;
+       void __iomem *base;
+       bool pcie_tx_pol_inv;
+       bool sata_tx_pol_inv;
+       u32 sata_gen;
+       u64 ctrlreg;
+       u8 type;
+};
+
+struct miphy365x_dev {
+       struct device *dev;
+       struct regmap *regmap;
+       struct mutex miphy_mutex;
+       struct miphy365x_phy **phys;
+};
+
+/*
+ * These values are represented in Device tree. They are considered to be ABI
+ * and although they can be extended any existing values must not change.
+ */
+enum miphy_sata_gen {
+       SATA_GEN1 = 1,
+       SATA_GEN2,
+       SATA_GEN3
+};
+
+static u8 rx_tx_spd[] = {
+       0, /* GEN0 doesn't exist. */
+       TX_SPDSEL_GEN1_VAL | RX_SPDSEL_GEN1_VAL,
+       TX_SPDSEL_GEN2_VAL | RX_SPDSEL_GEN2_VAL,
+       TX_SPDSEL_GEN3_VAL | RX_SPDSEL_GEN3_VAL
+};
+
+/*
+ * This function selects the system configuration,
+ * either two SATA, one SATA and one PCIe, or two PCIe lanes.
+ */
+static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
+                             struct miphy365x_dev *miphy_dev)
+{
+       bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
+
+       return regmap_update_bits(miphy_dev->regmap,
+                                 (unsigned int)miphy_phy->ctrlreg,
+                                 SYSCFG_SELECT_SATA_MASK,
+                                 sata << SYSCFG_SELECT_SATA_POS);
+}
+
+static int miphy365x_init_pcie_port(struct miphy365x_phy *miphy_phy,
+                                   struct miphy365x_dev *miphy_dev)
+{
+       u8 val;
+
+       if (miphy_phy->pcie_tx_pol_inv) {
+               /* Invert Tx polarity and clear pci_txdetect_pol bit */
+               val = TERM_EN | PCI_EN | DES_BIT_LOCK_EN | TX_POL;
+               writeb_relaxed(val, miphy_phy->base + CTRL_REG);
+               writeb_relaxed(0x00, miphy_phy->base + PCIE_REG);
+       }
+
+       return 0;
+}
+
+static inline int miphy365x_hfc_not_rdy(struct miphy365x_phy *miphy_phy,
+                                       struct miphy365x_dev *miphy_dev)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
+       u8 mask = IDLL_RDY | PLL_RDY;
+       u8 regval;
+
+       do {
+               regval = readb_relaxed(miphy_phy->base + STATUS_REG);
+               if (!(regval & mask))
+                       return 0;
+
+               usleep_range(2000, 2500);
+       } while (time_before(jiffies, timeout));
+
+       dev_err(miphy_dev->dev, "HFC ready timeout!\n");
+       return -EBUSY;
+}
+
+static inline int miphy365x_rdy(struct miphy365x_phy *miphy_phy,
+                               struct miphy365x_dev *miphy_dev)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(HFC_TIMEOUT);
+       u8 mask = IDLL_RDY | PLL_RDY;
+       u8 regval;
+
+       do {
+               regval = readb_relaxed(miphy_phy->base + STATUS_REG);
+               if ((regval & mask) == mask)
+                       return 0;
+
+               usleep_range(2000, 2500);
+       } while (time_before(jiffies, timeout));
+
+       dev_err(miphy_dev->dev, "PHY not ready timeout!\n");
+       return -EBUSY;
+}
+
+static inline void miphy365x_set_comp(struct miphy365x_phy *miphy_phy,
+                                     struct miphy365x_dev *miphy_dev)
+{
+       u8 val, mask;
+
+       if (miphy_phy->sata_gen == SATA_GEN1)
+               writeb_relaxed(COMP_2MHZ_RAT_GEN1,
+                              miphy_phy->base + COMP_CTRL2_REG);
+       else
+               writeb_relaxed(COMP_2MHZ_RAT,
+                              miphy_phy->base + COMP_CTRL2_REG);
+
+       if (miphy_phy->sata_gen != SATA_GEN3) {
+               writeb_relaxed(COMSR_COMP_REF,
+                              miphy_phy->base + COMP_CTRL3_REG);
+               /*
+                * Force VCO current to value defined by address 0x5A
+                * and disable PCIe100Mref bit
+                * Enable auto load compensation for pll_i_bias
+                */
+               writeb_relaxed(BYPASS_PLL_CAL, miphy_phy->base + PLL_CTRL2_REG);
+               writeb_relaxed(COMZC_IDLL, miphy_phy->base + COMP_IDLL_REG);
+       }
+
+       /*
+        * Force restart compensation and enable auto load
+        * for Comzc_Tx, Comzc_Rx and Comsr on macro
+        */
+       val = START_COMSR | START_COMZC | COMP_AUTO_LOAD;
+       writeb_relaxed(val, miphy_phy->base + COMP_CTRL1_REG);
+
+       mask = COMSR_DONE | COMZC_DONE;
+       while ((readb_relaxed(miphy_phy->base + COMP_CTRL1_REG) & mask) != mask)
+               cpu_relax();
+}
+
+static inline void miphy365x_set_ssc(struct miphy365x_phy *miphy_phy,
+                                    struct miphy365x_dev *miphy_dev)
+{
+       u8 val;
+
+       /*
+        * SSC Settings. SSC will be enabled through Link
+        * SSC Ampl. = 0.4%
+        * SSC Freq = 31KHz
+        */
+       writeb_relaxed(PLL_SSC_STEP_MSB_VAL,
+                      miphy_phy->base + PLL_SSC_STEP_MSB_REG);
+       writeb_relaxed(PLL_SSC_STEP_LSB_VAL,
+                      miphy_phy->base + PLL_SSC_STEP_LSB_REG);
+       writeb_relaxed(PLL_SSC_PER_MSB_VAL,
+                      miphy_phy->base + PLL_SSC_PER_MSB_REG);
+       writeb_relaxed(PLL_SSC_PER_LSB_VAL,
+                      miphy_phy->base + PLL_SSC_PER_LSB_REG);
+
+       /* SSC Settings complete */
+       if (miphy_phy->sata_gen == SATA_GEN1) {
+               val = PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
+               writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
+       } else {
+               val = SSC_EN | PLL_START_CAL | BUF_EN | SYNCHRO_TX | CONFIG_PLL;
+               writeb_relaxed(val, miphy_phy->base + PLL_CTRL1_REG);
+       }
+}
+
+static int miphy365x_init_sata_port(struct miphy365x_phy *miphy_phy,
+                                   struct miphy365x_dev *miphy_dev)
+{
+       int ret;
+       u8 val;
+
+       /*
+        * Force PHY macro reset, PLL calibration reset, PLL reset
+        * and assert Deserializer Reset
+        */
+       val = RST_PLL | RST_PLL_CAL | RST_RX | RST_MACRO;
+       writeb_relaxed(val, miphy_phy->base + RESET_REG);
+
+       if (miphy_phy->sata_tx_pol_inv)
+               writeb_relaxed(TX_POL, miphy_phy->base + CTRL_REG);
+
+       /*
+        * Force macro1 to use rx_lspd, tx_lspd
+        * Force Rx_Clock on first I-DLL phase
+        * Force Des in HP mode on macro, rx_lspd, tx_lspd for Gen2/3
+        */
+       writeb_relaxed(SPDSEL_SEL, miphy_phy->base + BOUNDARY1_REG);
+       writeb_relaxed(START_CLK_HF, miphy_phy->base + IDLL_TEST_REG);
+       val = rx_tx_spd[miphy_phy->sata_gen];
+       writeb_relaxed(val, miphy_phy->base + BOUNDARY3_REG);
+
+       /* Wait for HFC_READY = 0 */
+       ret = miphy365x_hfc_not_rdy(miphy_phy, miphy_dev);
+       if (ret)
+               return ret;
+
+       /* Compensation Recalibration */
+       miphy365x_set_comp(miphy_phy, miphy_dev);
+
+       switch (miphy_phy->sata_gen) {
+       case SATA_GEN3:
+               /*
+                * TX Swing target 550-600mv peak to peak diff
+                * Tx Slew target 90-110ps rising/falling time
+                * Rx Eq ON3, Sigdet threshold SDTH1
+                */
+               val = PD_VDDTFILTER | CONF_GEN_SEL_GEN3;
+               writeb_relaxed(val, miphy_phy->base + BUF_SEL_REG);
+               val = SWING_VAL | PREEMPH_VAL;
+               writeb_relaxed(val, miphy_phy->base + TXBUF1_REG);
+               writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
+               writeb_relaxed(0x00, miphy_phy->base + RXBUF_OFFSET_CTRL_REG);
+               val = SDTHRES_VAL | EQ_ON3;
+               writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
+               break;
+       case SATA_GEN2:
+               /*
+                * conf gen sel=0x1 to program Gen2 banked registers
+                * VDDT filter ON
+                * Tx Swing target 550-600mV peak-to-peak diff
+                * Tx Slew target 90-110 ps rising/falling time
+                * RX Equalization ON1, Sigdet threshold SDTH1
+                */
+               writeb_relaxed(CONF_GEN_SEL_GEN2,
+                              miphy_phy->base + BUF_SEL_REG);
+               writeb_relaxed(SWING_VAL, miphy_phy->base + TXBUF1_REG);
+               writeb_relaxed(TXSLEW_VAL, miphy_phy->base + TXBUF2_REG);
+               val = SDTHRES_VAL | EQ_ON1;
+               writeb_relaxed(val, miphy_phy->base + RXBUF_REG);
+               break;
+       case SATA_GEN1:
+               /*
+                * conf gen sel = 00b to program Gen1 banked registers
+                * VDDT filter ON
+                * Tx Swing target 500-550mV peak-to-peak diff
+                * Tx Slew target120-140 ps rising/falling time
+                */
+               writeb_relaxed(PD_VDDTFILTER, miphy_phy->base + BUF_SEL_REG);
+               writeb_relaxed(SWING_VAL_GEN1, miphy_phy->base + TXBUF1_REG);
+               writeb_relaxed(TXSLEW_VAL_GEN1, miphy_phy->base + TXBUF2_REG);
+               break;
+       default:
+               break;
+       }
+
+       /* Force Macro1 in partial mode & release pll cal reset */
+       writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
+       usleep_range(100, 150);
+
+       miphy365x_set_ssc(miphy_phy, miphy_dev);
+
+       /* Wait for phy_ready */
+       ret = miphy365x_rdy(miphy_phy, miphy_dev);
+       if (ret)
+               return ret;
+
+       /*
+        * Enable macro1 to use rx_lspd & tx_lspd
+        * Release Rx_Clock on first I-DLL phase on macro1
+        * Assert deserializer reset
+        * des_bit_lock_en is set
+        * bit lock detection strength
+        * Deassert deserializer reset
+        */
+       writeb_relaxed(0x00, miphy_phy->base + BOUNDARY1_REG);
+       writeb_relaxed(0x00, miphy_phy->base + IDLL_TEST_REG);
+       writeb_relaxed(RST_RX, miphy_phy->base + RESET_REG);
+       val = miphy_phy->sata_tx_pol_inv ?
+               (TX_POL | DES_BIT_LOCK_EN) : DES_BIT_LOCK_EN;
+       writeb_relaxed(val, miphy_phy->base + CTRL_REG);
+
+       val = BIT_LOCK_CNT_512 | BIT_LOCK_LEVEL;
+       writeb_relaxed(val, miphy_phy->base + DES_BITLOCK_REG);
+       writeb_relaxed(0x00, miphy_phy->base + RESET_REG);
+
+       return 0;
+}
+
+static int miphy365x_init(struct phy *phy)
+{
+       struct miphy365x_phy *miphy_phy = phy_get_drvdata(phy);
+       struct miphy365x_dev *miphy_dev = dev_get_drvdata(phy->dev.parent);
+       int ret = 0;
+
+       mutex_lock(&miphy_dev->miphy_mutex);
+
+       ret = miphy365x_set_path(miphy_phy, miphy_dev);
+       if (ret) {
+               mutex_unlock(&miphy_dev->miphy_mutex);
+               return ret;
+       }
+
+       /* Initialise Miphy for PCIe or SATA */
+       if (miphy_phy->type == MIPHY_TYPE_PCIE)
+               ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
+       else
+               ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
+
+       mutex_unlock(&miphy_dev->miphy_mutex);
+
+       return ret;
+}
+
+int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
+                      int index)
+{
+       struct device_node *phynode = miphy_phy->phy->dev.of_node;
+       const char *name;
+       const __be32 *taddr;
+       int type = miphy_phy->type;
+       int ret;
+
+       ret = of_property_read_string_index(phynode, "reg-names", index, &name);
+       if (ret) {
+               dev_err(dev, "no reg-names property not found\n");
+               return ret;
+       }
+
+       if (!strncmp(name, "syscfg", 6)) {
+               taddr = of_get_address(phynode, index, NULL, NULL);
+               if (!taddr) {
+                       dev_err(dev, "failed to fetch syscfg address\n");
+                       return -EINVAL;
+               }
+
+               miphy_phy->ctrlreg = of_translate_address(phynode, taddr);
+               if (miphy_phy->ctrlreg == OF_BAD_ADDR) {
+                       dev_err(dev, "failed to translate syscfg address\n");
+                       return -EINVAL;
+               }
+
+               return 0;
+       }
+
+       if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
+             (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
+               return 0;
+
+       miphy_phy->base = of_iomap(phynode, index);
+       if (!miphy_phy->base) {
+               dev_err(dev, "Failed to map %s\n", phynode->full_name);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct phy *miphy365x_xlate(struct device *dev,
+                                  struct of_phandle_args *args)
+{
+       struct miphy365x_dev *miphy_dev = dev_get_drvdata(dev);
+       struct miphy365x_phy *miphy_phy = NULL;
+       struct device_node *phynode = args->np;
+       int ret, index;
+
+       if (!of_device_is_available(phynode)) {
+               dev_warn(dev, "Requested PHY is disabled\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       if (args->args_count != 1) {
+               dev_err(dev, "Invalid number of cells in 'phy' property\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       for (index = 0; index < of_get_child_count(dev->of_node); index++)
+               if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
+                       miphy_phy = miphy_dev->phys[index];
+                       break;
+               }
+
+       if (!miphy_phy) {
+               dev_err(dev, "Failed to find appropriate phy\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       miphy_phy->type = args->args[0];
+
+       if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
+             miphy_phy->type == MIPHY_TYPE_PCIE)) {
+               dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
+               return ERR_PTR(-EINVAL);
+       }
+
+       /* Each port handles SATA and PCIE - third entry is always sysconf. */
+       for (index = 0; index < 3; index++) {
+               ret = miphy365x_get_addr(dev, miphy_phy, index);
+               if (ret < 0)
+                       return ERR_PTR(ret);
+       }
+
+       return miphy_phy->phy;
+}
+
+static struct phy_ops miphy365x_ops = {
+       .init           = miphy365x_init,
+       .owner          = THIS_MODULE,
+};
+
+static int miphy365x_of_probe(struct device_node *phynode,
+                             struct miphy365x_phy *miphy_phy)
+{
+       of_property_read_u32(phynode, "st,sata-gen", &miphy_phy->sata_gen);
+       if (!miphy_phy->sata_gen)
+               miphy_phy->sata_gen = SATA_GEN1;
+
+       miphy_phy->pcie_tx_pol_inv =
+               of_property_read_bool(phynode, "st,pcie-tx-pol-inv");
+
+       miphy_phy->sata_tx_pol_inv =
+               of_property_read_bool(phynode, "st,sata-tx-pol-inv");
+
+       return 0;
+}
+
+static int miphy365x_probe(struct platform_device *pdev)
+{
+       struct device_node *child, *np = pdev->dev.of_node;
+       struct miphy365x_dev *miphy_dev;
+       struct phy_provider *provider;
+       struct phy *phy;
+       int chancount, port = 0;
+       int ret;
+
+       miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
+       if (!miphy_dev)
+               return -ENOMEM;
+
+       chancount = of_get_child_count(np);
+       miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
+                                      GFP_KERNEL);
+       if (!miphy_dev->phys)
+               return -ENOMEM;
+
+       miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+       if (IS_ERR(miphy_dev->regmap)) {
+               dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
+               return PTR_ERR(miphy_dev->regmap);
+       }
+
+       miphy_dev->dev = &pdev->dev;
+
+       dev_set_drvdata(&pdev->dev, miphy_dev);
+
+       mutex_init(&miphy_dev->miphy_mutex);
+
+       for_each_child_of_node(np, child) {
+               struct miphy365x_phy *miphy_phy;
+
+               miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
+                                        GFP_KERNEL);
+               if (!miphy_phy)
+                       return -ENOMEM;
+
+               miphy_dev->phys[port] = miphy_phy;
+
+               phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(&pdev->dev, "failed to create PHY\n");
+                       return PTR_ERR(phy);
+               }
+
+               miphy_dev->phys[port]->phy = phy;
+
+               ret = miphy365x_of_probe(child, miphy_phy);
+               if (ret)
+                       return ret;
+
+               phy_set_drvdata(phy, miphy_dev->phys[port]);
+               port++;
+       }
+
+       provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
+       if (IS_ERR(provider))
+               return PTR_ERR(provider);
+
+       return 0;
+}
+
+static const struct of_device_id miphy365x_of_match[] = {
+       { .compatible = "st,miphy365x-phy", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, miphy365x_of_match);
+
+static struct platform_driver miphy365x_driver = {
+       .probe  = miphy365x_probe,
+       .driver = {
+               .name   = "miphy365x-phy",
+               .of_match_table = miphy365x_of_match,
+       }
+};
+module_platform_driver(miphy365x_driver);
+
+MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics miphy365x driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-mvebu-sata.c b/drivers/phy/phy-mvebu-sata.c
new file mode 100644 (file)
index 0000000..d395558
--- /dev/null
@@ -0,0 +1,138 @@
+/*
+ *     phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs.
+ *
+ *     Copyright (C) 2013 Andrew Lunn <andrew@lunn.ch>
+ *
+ *     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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+
+struct priv {
+       struct clk      *clk;
+       void __iomem    *base;
+};
+
+#define SATA_PHY_MODE_2        0x0330
+#define  MODE_2_FORCE_PU_TX    BIT(0)
+#define  MODE_2_FORCE_PU_RX    BIT(1)
+#define  MODE_2_PU_PLL         BIT(2)
+#define  MODE_2_PU_IVREF       BIT(3)
+#define SATA_IF_CTRL   0x0050
+#define  CTRL_PHY_SHUTDOWN     BIT(9)
+
+static int phy_mvebu_sata_power_on(struct phy *phy)
+{
+       struct priv *priv = phy_get_drvdata(phy);
+       u32 reg;
+
+       clk_prepare_enable(priv->clk);
+
+       /* Enable PLL and IVREF */
+       reg = readl(priv->base + SATA_PHY_MODE_2);
+       reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX |
+               MODE_2_PU_PLL | MODE_2_PU_IVREF);
+       writel(reg , priv->base + SATA_PHY_MODE_2);
+
+       /* Enable PHY */
+       reg = readl(priv->base + SATA_IF_CTRL);
+       reg &= ~CTRL_PHY_SHUTDOWN;
+       writel(reg, priv->base + SATA_IF_CTRL);
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static int phy_mvebu_sata_power_off(struct phy *phy)
+{
+       struct priv *priv = phy_get_drvdata(phy);
+       u32 reg;
+
+       clk_prepare_enable(priv->clk);
+
+       /* Disable PLL and IVREF */
+       reg = readl(priv->base + SATA_PHY_MODE_2);
+       reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX |
+                MODE_2_PU_PLL | MODE_2_PU_IVREF);
+       writel(reg, priv->base + SATA_PHY_MODE_2);
+
+       /* Disable PHY */
+       reg = readl(priv->base + SATA_IF_CTRL);
+       reg |= CTRL_PHY_SHUTDOWN;
+       writel(reg, priv->base + SATA_IF_CTRL);
+
+       clk_disable_unprepare(priv->clk);
+
+       return 0;
+}
+
+static struct phy_ops phy_mvebu_sata_ops = {
+       .power_on       = phy_mvebu_sata_power_on,
+       .power_off      = phy_mvebu_sata_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int phy_mvebu_sata_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct resource *res;
+       struct priv *priv;
+       struct phy *phy;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       priv->clk = devm_clk_get(&pdev->dev, "sata");
+       if (IS_ERR(priv->clk))
+               return PTR_ERR(priv->clk);
+
+       phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL);
+       if (IS_ERR(phy))
+               return PTR_ERR(phy);
+
+       phy_set_drvdata(phy, priv);
+
+       phy_provider = devm_of_phy_provider_register(&pdev->dev,
+                                                    of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       /* The boot loader may of left it on. Turn it off. */
+       phy_mvebu_sata_power_off(phy);
+
+       return 0;
+}
+
+static const struct of_device_id phy_mvebu_sata_of_match[] = {
+       { .compatible = "marvell,mvebu-sata-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match);
+
+static struct platform_driver phy_mvebu_sata_driver = {
+       .probe  = phy_mvebu_sata_probe,
+       .driver = {
+               .name   = "phy-mvebu-sata",
+               .of_match_table = phy_mvebu_sata_of_match,
+       }
+};
+module_platform_driver(phy_mvebu_sata_driver);
+
+MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
+MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-omap-control.c b/drivers/phy/phy-omap-control.c
new file mode 100644 (file)
index 0000000..c96e818
--- /dev/null
@@ -0,0 +1,367 @@
+/*
+ * omap-control-phy.c - The PHY part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/phy/omap_control_phy.h>
+
+/**
+ * omap_control_pcie_pcs - set the PCS delay count
+ * @dev: the control module device
+ * @id: index of the pcie PHY (should be 1 or 2)
+ * @delay: 8 bit delay value
+ */
+void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+{
+       u32 val;
+       struct omap_control_phy *control_phy;
+
+       if (IS_ERR(dev) || !dev) {
+               pr_err("%s: invalid device\n", __func__);
+               return;
+       }
+
+       control_phy = dev_get_drvdata(dev);
+       if (!control_phy) {
+               dev_err(dev, "%s: invalid control phy device\n", __func__);
+               return;
+       }
+
+       if (control_phy->type != OMAP_CTRL_TYPE_PCIE) {
+               dev_err(dev, "%s: unsupported operation\n", __func__);
+               return;
+       }
+
+       val = readl(control_phy->pcie_pcs);
+       val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<
+               (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT));
+       val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
+       writel(val, control_phy->pcie_pcs);
+}
+EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);
+
+/**
+ * omap_control_phy_power - power on/off the phy using control module reg
+ * @dev: the control module device
+ * @on: 0 or 1, based on powering on or off the PHY
+ */
+void omap_control_phy_power(struct device *dev, int on)
+{
+       u32 val;
+       unsigned long rate;
+       struct omap_control_phy *control_phy;
+
+       if (IS_ERR(dev) || !dev) {
+               pr_err("%s: invalid device\n", __func__);
+               return;
+       }
+
+       control_phy = dev_get_drvdata(dev);
+       if (!control_phy) {
+               dev_err(dev, "%s: invalid control phy device\n", __func__);
+               return;
+       }
+
+       if (control_phy->type == OMAP_CTRL_TYPE_OTGHS)
+               return;
+
+       val = readl(control_phy->power);
+
+       switch (control_phy->type) {
+       case OMAP_CTRL_TYPE_USB2:
+               if (on)
+                       val &= ~OMAP_CTRL_DEV_PHY_PD;
+               else
+                       val |= OMAP_CTRL_DEV_PHY_PD;
+               break;
+
+       case OMAP_CTRL_TYPE_PCIE:
+       case OMAP_CTRL_TYPE_PIPE3:
+               rate = clk_get_rate(control_phy->sys_clk);
+               rate = rate/1000000;
+
+               if (on) {
+                       val &= ~(OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK |
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK);
+                       val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON <<
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+                       val |= rate <<
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT;
+               } else {
+                       val &= ~OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK;
+                       val |= OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF <<
+                               OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT;
+               }
+               break;
+
+       case OMAP_CTRL_TYPE_DRA7USB2:
+               if (on)
+                       val &= ~OMAP_CTRL_USB2_PHY_PD;
+               else
+                       val |= OMAP_CTRL_USB2_PHY_PD;
+               break;
+
+       case OMAP_CTRL_TYPE_AM437USB2:
+               if (on) {
+                       val &= ~(AM437X_CTRL_USB2_PHY_PD |
+                                       AM437X_CTRL_USB2_OTG_PD);
+                       val |= (AM437X_CTRL_USB2_OTGVDET_EN |
+                                       AM437X_CTRL_USB2_OTGSESSEND_EN);
+               } else {
+                       val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |
+                                       AM437X_CTRL_USB2_OTGSESSEND_EN);
+                       val |= (AM437X_CTRL_USB2_PHY_PD |
+                                        AM437X_CTRL_USB2_OTG_PD);
+               }
+               break;
+       default:
+               dev_err(dev, "%s: type %d not recognized\n",
+                       __func__, control_phy->type);
+               break;
+       }
+
+       writel(val, control_phy->power);
+}
+EXPORT_SYMBOL_GPL(omap_control_phy_power);
+
+/**
+ * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core that a usb
+ * device has been connected.
+ */
+static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy)
+{
+       u32 val;
+
+       val = readl(ctrl_phy->otghs_control);
+       val &= ~(OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND);
+       val |= OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID;
+       writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
+ * impedance
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core that it has been
+ * connected to a usb host.
+ */
+static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy)
+{
+       u32 val;
+
+       val = readl(ctrl_phy->otghs_control);
+       val &= ~OMAP_CTRL_DEV_SESSEND;
+       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_AVALID |
+               OMAP_CTRL_DEV_VBUSVALID;
+       writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
+ * impedance
+ * @ctrl_phy: struct omap_control_phy *
+ *
+ * Writes to the mailbox register to notify the usb core it's now in
+ * disconnected state.
+ */
+static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy)
+{
+       u32 val;
+
+       val = readl(ctrl_phy->otghs_control);
+       val &= ~(OMAP_CTRL_DEV_AVALID | OMAP_CTRL_DEV_VBUSVALID);
+       val |= OMAP_CTRL_DEV_IDDIG | OMAP_CTRL_DEV_SESSEND;
+       writel(val, ctrl_phy->otghs_control);
+}
+
+/**
+ * omap_control_usb_set_mode - Calls to functions to set USB in one of host mode
+ * or device mode or to denote disconnected state
+ * @dev: the control module device
+ * @mode: The mode to which usb should be configured
+ *
+ * This is an API to write to the mailbox register to notify the usb core that
+ * a usb device has been connected.
+ */
+void omap_control_usb_set_mode(struct device *dev,
+       enum omap_control_usb_mode mode)
+{
+       struct omap_control_phy *ctrl_phy;
+
+       if (IS_ERR(dev) || !dev)
+               return;
+
+       ctrl_phy = dev_get_drvdata(dev);
+
+       if (!ctrl_phy) {
+               dev_err(dev, "Invalid control phy device\n");
+               return;
+       }
+
+       if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS)
+               return;
+
+       switch (mode) {
+       case USB_MODE_HOST:
+               omap_control_usb_host_mode(ctrl_phy);
+               break;
+       case USB_MODE_DEVICE:
+               omap_control_usb_device_mode(ctrl_phy);
+               break;
+       case USB_MODE_DISCONNECT:
+               omap_control_usb_set_sessionend(ctrl_phy);
+               break;
+       default:
+               dev_vdbg(dev, "invalid omap control usb mode\n");
+       }
+}
+EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
+
+#ifdef CONFIG_OF
+
+static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
+static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
+static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
+static const enum omap_control_phy_type pcie_data = OMAP_CTRL_TYPE_PCIE;
+static const enum omap_control_phy_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
+static const enum omap_control_phy_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
+
+static const struct of_device_id omap_control_phy_id_table[] = {
+       {
+               .compatible = "ti,control-phy-otghs",
+               .data = &otghs_data,
+       },
+       {
+               .compatible = "ti,control-phy-usb2",
+               .data = &usb2_data,
+       },
+       {
+               .compatible = "ti,control-phy-pipe3",
+               .data = &pipe3_data,
+       },
+       {
+               .compatible = "ti,control-phy-pcie",
+               .data = &pcie_data,
+       },
+       {
+               .compatible = "ti,control-phy-usb2-dra7",
+               .data = &dra7usb2_data,
+       },
+       {
+               .compatible = "ti,control-phy-usb2-am437",
+               .data = &am437usb2_data,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
+#endif
+
+
+static int omap_control_phy_probe(struct platform_device *pdev)
+{
+       struct resource *res;
+       const struct of_device_id *of_id;
+       struct omap_control_phy *control_phy;
+
+       of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
+                               &pdev->dev);
+       if (!of_id)
+               return -EINVAL;
+
+       control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy),
+               GFP_KERNEL);
+       if (!control_phy)
+               return -ENOMEM;
+
+       control_phy->dev = &pdev->dev;
+       control_phy->type = *(enum omap_control_phy_type *)of_id->data;
+
+       if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                       "otghs_control");
+               control_phy->otghs_control = devm_ioremap_resource(
+                       &pdev->dev, res);
+               if (IS_ERR(control_phy->otghs_control))
+                       return PTR_ERR(control_phy->otghs_control);
+       } else {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                               "power");
+               control_phy->power = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(control_phy->power)) {
+                       dev_err(&pdev->dev, "Couldn't get power register\n");
+                       return PTR_ERR(control_phy->power);
+               }
+       }
+
+       if (control_phy->type == OMAP_CTRL_TYPE_PIPE3 ||
+           control_phy->type == OMAP_CTRL_TYPE_PCIE) {
+               control_phy->sys_clk = devm_clk_get(control_phy->dev,
+                       "sys_clkin");
+               if (IS_ERR(control_phy->sys_clk)) {
+                       pr_err("%s: unable to get sys_clkin\n", __func__);
+                       return -EINVAL;
+               }
+       }
+
+       if (control_phy->type == OMAP_CTRL_TYPE_PCIE) {
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "pcie_pcs");
+               control_phy->pcie_pcs = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(control_phy->pcie_pcs))
+                       return PTR_ERR(control_phy->pcie_pcs);
+       }
+
+       dev_set_drvdata(control_phy->dev, control_phy);
+
+       return 0;
+}
+
+static struct platform_driver omap_control_phy_driver = {
+       .probe          = omap_control_phy_probe,
+       .driver         = {
+               .name   = "omap-control-phy",
+               .of_match_table = of_match_ptr(omap_control_phy_id_table),
+       },
+};
+
+static int __init omap_control_phy_init(void)
+{
+       return platform_driver_register(&omap_control_phy_driver);
+}
+subsys_initcall(omap_control_phy_init);
+
+static void __exit omap_control_phy_exit(void)
+{
+       platform_driver_unregister(&omap_control_phy_driver);
+}
+module_exit(omap_control_phy_exit);
+
+MODULE_ALIAS("platform: omap_control_phy");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-omap-usb2.c b/drivers/phy/phy-omap-usb2.c
new file mode 100644 (file)
index 0000000..08f2da2
--- /dev/null
@@ -0,0 +1,389 @@
+/*
+ * omap-usb2.c - USB PHY, talking to musb controller in OMAP.
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/phy/omap_usb.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/phy/omap_control_phy.h>
+#include <linux/phy/phy.h>
+#include <linux/of_platform.h>
+
+#define USB2PHY_DISCON_BYP_LATCH (1 << 31)
+#define USB2PHY_ANA_CONFIG1 0x4c
+
+/**
+ * omap_usb2_set_comparator - links the comparator present in the sytem with
+ *     this phy
+ * @comparator - the companion phy(comparator) for this phy
+ *
+ * The phy companion driver should call this API passing the phy_companion
+ * filled with set_vbus and start_srp to be used by usb phy.
+ *
+ * For use by phy companion driver
+ */
+int omap_usb2_set_comparator(struct phy_companion *comparator)
+{
+       struct omap_usb *phy;
+       struct usb_phy  *x = usb_get_phy(USB_PHY_TYPE_USB2);
+
+       if (IS_ERR(x))
+               return -ENODEV;
+
+       phy = phy_to_omapusb(x);
+       phy->comparator = comparator;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(omap_usb2_set_comparator);
+
+static int omap_usb_set_vbus(struct usb_otg *otg, bool enabled)
+{
+       struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
+
+       if (!phy->comparator)
+               return -ENODEV;
+
+       return phy->comparator->set_vbus(phy->comparator, enabled);
+}
+
+static int omap_usb_start_srp(struct usb_otg *otg)
+{
+       struct omap_usb *phy = phy_to_omapusb(otg->usb_phy);
+
+       if (!phy->comparator)
+               return -ENODEV;
+
+       return phy->comparator->start_srp(phy->comparator);
+}
+
+static int omap_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+       otg->host = host;
+       if (!host)
+               otg->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int omap_usb_set_peripheral(struct usb_otg *otg,
+               struct usb_gadget *gadget)
+{
+       otg->gadget = gadget;
+       if (!gadget)
+               otg->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int omap_usb_power_off(struct phy *x)
+{
+       struct omap_usb *phy = phy_get_drvdata(x);
+
+       omap_control_phy_power(phy->control_dev, 0);
+
+       return 0;
+}
+
+static int omap_usb_power_on(struct phy *x)
+{
+       struct omap_usb *phy = phy_get_drvdata(x);
+
+       omap_control_phy_power(phy->control_dev, 1);
+
+       return 0;
+}
+
+static int omap_usb_init(struct phy *x)
+{
+       struct omap_usb *phy = phy_get_drvdata(x);
+       u32 val;
+
+       if (phy->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
+               /*
+                *
+                * Reduce the sensitivity of internal PHY by enabling the
+                * DISCON_BYP_LATCH of the USB2PHY_ANA_CONFIG1 register. This
+                * resolves issues with certain devices which can otherwise
+                * be prone to false disconnects.
+                *
+                */
+               val = omap_usb_readl(phy->phy_base, USB2PHY_ANA_CONFIG1);
+               val |= USB2PHY_DISCON_BYP_LATCH;
+               omap_usb_writel(phy->phy_base, USB2PHY_ANA_CONFIG1, val);
+       }
+
+       return 0;
+}
+
+static struct phy_ops ops = {
+       .init           = omap_usb_init,
+       .power_on       = omap_usb_power_on,
+       .power_off      = omap_usb_power_off,
+       .owner          = THIS_MODULE,
+};
+
+#ifdef CONFIG_OF
+static const struct usb_phy_data omap_usb2_data = {
+       .label = "omap_usb2",
+       .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
+};
+
+static const struct usb_phy_data omap5_usb2_data = {
+       .label = "omap5_usb2",
+       .flags = 0,
+};
+
+static const struct usb_phy_data dra7x_usb2_data = {
+       .label = "dra7x_usb2",
+       .flags = OMAP_USB2_CALIBRATE_FALSE_DISCONNECT,
+};
+
+static const struct usb_phy_data am437x_usb2_data = {
+       .label = "am437x_usb2",
+       .flags =  0,
+};
+
+static const struct of_device_id omap_usb2_id_table[] = {
+       {
+               .compatible = "ti,omap-usb2",
+               .data = &omap_usb2_data,
+       },
+       {
+               .compatible = "ti,omap5-usb2",
+               .data = &omap5_usb2_data,
+       },
+       {
+               .compatible = "ti,dra7x-usb2",
+               .data = &dra7x_usb2_data,
+       },
+       {
+               .compatible = "ti,am437x-usb2",
+               .data = &am437x_usb2_data,
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
+#endif
+
+static int omap_usb2_probe(struct platform_device *pdev)
+{
+       struct omap_usb *phy;
+       struct phy *generic_phy;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct usb_otg *otg;
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *control_node;
+       struct platform_device *control_pdev;
+       const struct of_device_id *of_id;
+       struct usb_phy_data *phy_data;
+
+       of_id = of_match_device(of_match_ptr(omap_usb2_id_table), &pdev->dev);
+
+       if (!of_id)
+               return -EINVAL;
+
+       phy_data = (struct usb_phy_data *)of_id->data;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+       if (!otg)
+               return -ENOMEM;
+
+       phy->dev                = &pdev->dev;
+
+       phy->phy.dev            = phy->dev;
+       phy->phy.label          = phy_data->label;
+       phy->phy.otg            = otg;
+       phy->phy.type           = USB_PHY_TYPE_USB2;
+
+       if (phy_data->flags & OMAP_USB2_CALIBRATE_FALSE_DISCONNECT) {
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               phy->phy_base = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(phy->phy_base))
+                       return PTR_ERR(phy->phy_base);
+               phy->flags |= OMAP_USB2_CALIBRATE_FALSE_DISCONNECT;
+       }
+
+       control_node = of_parse_phandle(node, "ctrl-module", 0);
+       if (!control_node) {
+               dev_err(&pdev->dev, "Failed to get control device phandle\n");
+               return -EINVAL;
+       }
+
+       control_pdev = of_find_device_by_node(control_node);
+       if (!control_pdev) {
+               dev_err(&pdev->dev, "Failed to get control device\n");
+               return -EINVAL;
+       }
+
+       phy->control_dev = &control_pdev->dev;
+       omap_control_phy_power(phy->control_dev, 0);
+
+       otg->set_host           = omap_usb_set_host;
+       otg->set_peripheral     = omap_usb_set_peripheral;
+       if (phy_data->flags & OMAP_USB2_HAS_SET_VBUS)
+               otg->set_vbus           = omap_usb_set_vbus;
+       if (phy_data->flags & OMAP_USB2_HAS_START_SRP)
+               otg->start_srp          = omap_usb_start_srp;
+       otg->usb_phy            = &phy->phy;
+
+       platform_set_drvdata(pdev, phy);
+       pm_runtime_enable(phy->dev);
+
+       generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
+       if (IS_ERR(generic_phy)) {
+               pm_runtime_disable(phy->dev);
+               return PTR_ERR(generic_phy);
+       }
+
+       phy_set_drvdata(generic_phy, phy);
+
+       phy_provider = devm_of_phy_provider_register(phy->dev,
+                       of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               pm_runtime_disable(phy->dev);
+               return PTR_ERR(phy_provider);
+       }
+
+       phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
+       if (IS_ERR(phy->wkupclk)) {
+               dev_warn(&pdev->dev, "unable to get wkupclk, trying old name\n");
+               phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
+               if (IS_ERR(phy->wkupclk)) {
+                       dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
+                       return PTR_ERR(phy->wkupclk);
+               } else {
+                       dev_warn(&pdev->dev,
+                                "found usb_phy_cm_clk32k, please fix DTS\n");
+               }
+       }
+       clk_prepare(phy->wkupclk);
+
+       phy->optclk = devm_clk_get(phy->dev, "refclk");
+       if (IS_ERR(phy->optclk)) {
+               dev_dbg(&pdev->dev, "unable to get refclk, trying old name\n");
+               phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
+               if (IS_ERR(phy->optclk)) {
+                       dev_dbg(&pdev->dev,
+                               "unable to get usb_otg_ss_refclk960m\n");
+               } else {
+                       dev_warn(&pdev->dev,
+                                "found usb_otg_ss_refclk960m, please fix DTS\n");
+               }
+       } else {
+               clk_prepare(phy->optclk);
+       }
+
+       usb_add_phy_dev(&phy->phy);
+
+       return 0;
+}
+
+static int omap_usb2_remove(struct platform_device *pdev)
+{
+       struct omap_usb *phy = platform_get_drvdata(pdev);
+
+       clk_unprepare(phy->wkupclk);
+       if (!IS_ERR(phy->optclk))
+               clk_unprepare(phy->optclk);
+       usb_remove_phy(&phy->phy);
+       pm_runtime_disable(phy->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+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 = of_match_ptr(omap_usb2_id_table),
+       },
+};
+
+module_platform_driver(omap_usb2_driver);
+
+MODULE_ALIAS("platform: omap_usb2");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("OMAP USB2 phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-qcom-apq8064-sata.c b/drivers/phy/phy-qcom-apq8064-sata.c
new file mode 100644 (file)
index 0000000..7b3ddfb
--- /dev/null
@@ -0,0 +1,288 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+
+/* PHY registers */
+#define UNIPHY_PLL_REFCLK_CFG          0x000
+#define UNIPHY_PLL_PWRGEN_CFG          0x014
+#define UNIPHY_PLL_GLB_CFG             0x020
+#define UNIPHY_PLL_SDM_CFG0            0x038
+#define UNIPHY_PLL_SDM_CFG1            0x03C
+#define UNIPHY_PLL_SDM_CFG2            0x040
+#define UNIPHY_PLL_SDM_CFG3            0x044
+#define UNIPHY_PLL_SDM_CFG4            0x048
+#define UNIPHY_PLL_SSC_CFG0            0x04C
+#define UNIPHY_PLL_SSC_CFG1            0x050
+#define UNIPHY_PLL_SSC_CFG2            0x054
+#define UNIPHY_PLL_SSC_CFG3            0x058
+#define UNIPHY_PLL_LKDET_CFG0          0x05C
+#define UNIPHY_PLL_LKDET_CFG1          0x060
+#define UNIPHY_PLL_LKDET_CFG2          0x064
+#define UNIPHY_PLL_CAL_CFG0            0x06C
+#define UNIPHY_PLL_CAL_CFG8            0x08C
+#define UNIPHY_PLL_CAL_CFG9            0x090
+#define UNIPHY_PLL_CAL_CFG10           0x094
+#define UNIPHY_PLL_CAL_CFG11           0x098
+#define UNIPHY_PLL_STATUS              0x0C0
+
+#define SATA_PHY_SER_CTRL              0x100
+#define SATA_PHY_TX_DRIV_CTRL0         0x104
+#define SATA_PHY_TX_DRIV_CTRL1         0x108
+#define SATA_PHY_TX_IMCAL0             0x11C
+#define SATA_PHY_TX_IMCAL2             0x124
+#define SATA_PHY_RX_IMCAL0             0x128
+#define SATA_PHY_EQUAL                 0x13C
+#define SATA_PHY_OOB_TERM              0x144
+#define SATA_PHY_CDR_CTRL0             0x148
+#define SATA_PHY_CDR_CTRL1             0x14C
+#define SATA_PHY_CDR_CTRL2             0x150
+#define SATA_PHY_CDR_CTRL3             0x154
+#define SATA_PHY_PI_CTRL0              0x168
+#define SATA_PHY_POW_DWN_CTRL0         0x180
+#define SATA_PHY_POW_DWN_CTRL1         0x184
+#define SATA_PHY_TX_DATA_CTRL          0x188
+#define SATA_PHY_ALIGNP                        0x1A4
+#define SATA_PHY_TX_IMCAL_STAT         0x1E4
+#define SATA_PHY_RX_IMCAL_STAT         0x1E8
+
+#define UNIPHY_PLL_LOCK                BIT(0)
+#define SATA_PHY_TX_CAL                BIT(0)
+#define SATA_PHY_RX_CAL                BIT(0)
+
+/* default timeout set to 1 sec */
+#define TIMEOUT_MS             10000
+#define DELAY_INTERVAL_US      100
+
+struct qcom_apq8064_sata_phy {
+       void __iomem *mmio;
+       struct clk *cfg_clk;
+       struct device *dev;
+};
+
+/* Helper function to do poll and timeout */
+static int read_poll_timeout(void __iomem *addr, u32 mask)
+{
+       unsigned long timeout = jiffies + msecs_to_jiffies(TIMEOUT_MS);
+
+       do {
+               if (readl_relaxed(addr) & mask)
+                       return 0;
+
+                usleep_range(DELAY_INTERVAL_US, DELAY_INTERVAL_US + 50);
+       } while (!time_after(jiffies, timeout));
+
+       return (readl_relaxed(addr) & mask) ? 0 : -ETIMEDOUT;
+}
+
+static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
+{
+       struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
+       void __iomem *base = phy->mmio;
+       int ret = 0;
+
+       /* SATA phy initialization */
+       writel_relaxed(0x01, base + SATA_PHY_SER_CTRL);
+       writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0);
+       /* Make sure the power down happens before power up */
+       mb();
+       usleep_range(10, 60);
+
+       writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
+       writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
+       writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
+       writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
+       writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2);
+
+       /* Write UNIPHYPLL registers to configure PLL */
+       writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG);
+       writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG);
+
+       writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0);
+       writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8);
+       writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9);
+       writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10);
+       writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11);
+
+       writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0);
+       writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1);
+       writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2);
+       writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3);
+       writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4);
+
+       writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0);
+       writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1);
+       writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2);
+       writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3);
+
+       writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0);
+       writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1);
+
+       writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG);
+       /* make sure global config LDO power down happens before power up */
+       mb();
+
+       writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG);
+       writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
+
+       /* PLL Lock wait */
+       ret = read_poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
+       if (ret) {
+               dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");
+               return ret;
+       }
+
+       /* TX Calibration */
+       ret = read_poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
+       if (ret) {
+               dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
+               return ret;
+       }
+
+       /* RX Calibration */
+       ret = read_poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
+       if (ret) {
+               dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
+               return ret;
+       }
+
+       /* SATA phy calibrated succesfully, power up to functional mode */
+       writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
+       writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
+       writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
+
+       writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1);
+       writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0);
+       writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1);
+       writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2);
+       writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0);
+       writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3);
+       writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
+
+       writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL);
+       writel_relaxed(0x43, base + SATA_PHY_ALIGNP);
+       writel_relaxed(0x04, base + SATA_PHY_OOB_TERM);
+
+       writel_relaxed(0x01, base + SATA_PHY_EQUAL);
+       writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0);
+       writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1);
+
+       return 0;
+}
+
+static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy)
+{
+       struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
+       void __iomem *base = phy->mmio;
+
+       /* Power down PHY */
+       writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0);
+       writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1);
+
+       /* Power down PLL block */
+       writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG);
+
+       return 0;
+}
+
+static struct phy_ops qcom_apq8064_sata_phy_ops = {
+       .init           = qcom_apq8064_sata_phy_init,
+       .exit           = qcom_apq8064_sata_phy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
+{
+       struct qcom_apq8064_sata_phy *phy;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct phy *generic_phy;
+       int ret;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy->mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR(phy->mmio))
+               return PTR_ERR(phy->mmio);
+
+       generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops,
+                                     NULL);
+       if (IS_ERR(generic_phy)) {
+               dev_err(dev, "%s: failed to create phy\n", __func__);
+               return PTR_ERR(generic_phy);
+       }
+
+       phy->dev = dev;
+       phy_set_drvdata(generic_phy, phy);
+       platform_set_drvdata(pdev, phy);
+
+       phy->cfg_clk = devm_clk_get(dev, "cfg");
+       if (IS_ERR(phy->cfg_clk)) {
+               dev_err(dev, "Failed to get sata cfg clock\n");
+               return PTR_ERR(phy->cfg_clk);
+       }
+
+       ret = clk_prepare_enable(phy->cfg_clk);
+       if (ret)
+               return ret;
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               clk_disable_unprepare(phy->cfg_clk);
+               dev_err(dev, "%s: failed to register phy\n", __func__);
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static int qcom_apq8064_sata_phy_remove(struct platform_device *pdev)
+{
+       struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(phy->cfg_clk);
+
+       return 0;
+}
+
+static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = {
+       { .compatible = "qcom,apq8064-sata-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match);
+
+static struct platform_driver qcom_apq8064_sata_phy_driver = {
+       .probe  = qcom_apq8064_sata_phy_probe,
+       .remove = qcom_apq8064_sata_phy_remove,
+       .driver = {
+               .name   = "qcom-apq8064-sata-phy",
+               .of_match_table = qcom_apq8064_sata_phy_of_match,
+       }
+};
+module_platform_driver(qcom_apq8064_sata_phy_driver);
+
+MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-qcom-ipq806x-sata.c b/drivers/phy/phy-qcom-ipq806x-sata.c
new file mode 100644 (file)
index 0000000..759b0bf
--- /dev/null
@@ -0,0 +1,210 @@
+/*
+ * Copyright (c) 2014, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * 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/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/time.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/phy/phy.h>
+
+struct qcom_ipq806x_sata_phy {
+       void __iomem *mmio;
+       struct clk *cfg_clk;
+       struct device *dev;
+};
+
+#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))
+
+#define SATA_PHY_P0_PARAM0             0x200
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x)       __set(x, 17, 12)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK     GENMASK(17, 12)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x)       __set(x, 11, 6)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK     GENMASK(11, 6)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x)       __set(x, 5, 0)
+#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK     GENMASK(5, 0)
+
+#define SATA_PHY_P0_PARAM1             0x204
+#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x)       __set(x, 31, 21)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x)     __set(x, 20, 14)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK   GENMASK(20, 14)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x)     __set(x, 13, 7)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK   GENMASK(13, 7)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x)     __set(x, 6, 0)
+#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK   GENMASK(6, 0)
+
+#define SATA_PHY_P0_PARAM2             0x208
+#define SATA_PHY_P0_PARAM2_RX_EQ(x)    __set(x, 20, 18)
+#define SATA_PHY_P0_PARAM2_RX_EQ_MASK  GENMASK(20, 18)
+
+#define SATA_PHY_P0_PARAM3             0x20C
+#define SATA_PHY_SSC_EN                        0x8
+#define SATA_PHY_P0_PARAM4             0x210
+#define SATA_PHY_REF_SSP_EN            0x2
+#define SATA_PHY_RESET                 0x1
+
+static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
+{
+       struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
+       u32 reg;
+
+       /* Setting SSC_EN to 1 */
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
+       reg = reg | SATA_PHY_SSC_EN;
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
+
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
+                       ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
+                         SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
+                         SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
+       reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
+
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
+                       ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
+                         SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
+                         SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
+       reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
+               SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
+               SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
+
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
+               ~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
+       reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
+
+       /* Setting PHY_RESET to 1 */
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+       reg = reg | SATA_PHY_RESET;
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+       /* Setting REF_SSP_EN to 1 */
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+       reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+       /* make sure all changes complete before we let the PHY out of reset */
+       mb();
+
+       /* sleep for max. 50us more to combine processor wakeups */
+       usleep_range(20, 20 + 50);
+
+       /* Clearing PHY_RESET to 0 */
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+       reg = reg & ~SATA_PHY_RESET;
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+       return 0;
+}
+
+static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
+{
+       struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
+       u32 reg;
+
+       /* Setting PHY_RESET to 1 */
+       reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
+       reg = reg | SATA_PHY_RESET;
+       writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
+
+       return 0;
+}
+
+static struct phy_ops qcom_ipq806x_sata_phy_ops = {
+       .init           = qcom_ipq806x_sata_phy_init,
+       .exit           = qcom_ipq806x_sata_phy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
+{
+       struct qcom_ipq806x_sata_phy *phy;
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       struct phy_provider *phy_provider;
+       struct phy *generic_phy;
+       int ret;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy->mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR(phy->mmio))
+               return PTR_ERR(phy->mmio);
+
+       generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
+                                     NULL);
+       if (IS_ERR(generic_phy)) {
+               dev_err(dev, "%s: failed to create phy\n", __func__);
+               return PTR_ERR(generic_phy);
+       }
+
+       phy->dev = dev;
+       phy_set_drvdata(generic_phy, phy);
+       platform_set_drvdata(pdev, phy);
+
+       phy->cfg_clk = devm_clk_get(dev, "cfg");
+       if (IS_ERR(phy->cfg_clk)) {
+               dev_err(dev, "Failed to get sata cfg clock\n");
+               return PTR_ERR(phy->cfg_clk);
+       }
+
+       ret = clk_prepare_enable(phy->cfg_clk);
+       if (ret)
+               return ret;
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               clk_disable_unprepare(phy->cfg_clk);
+               dev_err(dev, "%s: failed to register phy\n", __func__);
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static int qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
+{
+       struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
+
+       clk_disable_unprepare(phy->cfg_clk);
+
+       return 0;
+}
+
+static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
+       { .compatible = "qcom,ipq806x-sata-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
+
+static struct platform_driver qcom_ipq806x_sata_phy_driver = {
+       .probe  = qcom_ipq806x_sata_phy_probe,
+       .remove = qcom_ipq806x_sata_phy_remove,
+       .driver = {
+               .name   = "qcom-ipq806x-sata-phy",
+               .of_match_table = qcom_ipq806x_sata_phy_of_match,
+       }
+};
+module_platform_driver(qcom_ipq806x_sata_phy_driver);
+
+MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c
new file mode 100644 (file)
index 0000000..2793af1
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * Renesas R-Car Gen2 PHY driver
+ *
+ * Copyright (C) 2014 Renesas Solutions Corp.
+ * Copyright (C) 2014 Cogent Embedded, Inc.
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+
+#include <asm/cmpxchg.h>
+
+#define USBHS_LPSTS                    0x02
+#define USBHS_UGCTRL                   0x80
+#define USBHS_UGCTRL2                  0x84
+#define USBHS_UGSTS                    0x88    /* The manuals have 0x90 */
+
+/* Low Power Status register (LPSTS) */
+#define USBHS_LPSTS_SUSPM              0x4000
+
+/* USB General control register (UGCTRL) */
+#define USBHS_UGCTRL_CONNECT           0x00000004
+#define USBHS_UGCTRL_PLLRESET          0x00000001
+
+/* USB General control register 2 (UGCTRL2) */
+#define USBHS_UGCTRL2_USB2SEL          0x80000000
+#define USBHS_UGCTRL2_USB2SEL_PCI      0x00000000
+#define USBHS_UGCTRL2_USB2SEL_USB30    0x80000000
+#define USBHS_UGCTRL2_USB0SEL          0x00000030
+#define USBHS_UGCTRL2_USB0SEL_PCI      0x00000010
+#define USBHS_UGCTRL2_USB0SEL_HS_USB   0x00000030
+
+/* USB General status register (UGSTS) */
+#define USBHS_UGSTS_LOCK               0x00000300 /* The manuals have 0x3 */
+
+#define PHYS_PER_CHANNEL       2
+
+struct rcar_gen2_phy {
+       struct phy *phy;
+       struct rcar_gen2_channel *channel;
+       int number;
+       u32 select_value;
+};
+
+struct rcar_gen2_channel {
+       struct device_node *of_node;
+       struct rcar_gen2_phy_driver *drv;
+       struct rcar_gen2_phy phys[PHYS_PER_CHANNEL];
+       int selected_phy;
+       u32 select_mask;
+};
+
+struct rcar_gen2_phy_driver {
+       void __iomem *base;
+       struct clk *clk;
+       spinlock_t lock;
+       int num_channels;
+       struct rcar_gen2_channel *channels;
+};
+
+static int rcar_gen2_phy_init(struct phy *p)
+{
+       struct rcar_gen2_phy *phy = phy_get_drvdata(p);
+       struct rcar_gen2_channel *channel = phy->channel;
+       struct rcar_gen2_phy_driver *drv = channel->drv;
+       unsigned long flags;
+       u32 ugctrl2;
+
+       /*
+        * Try to acquire exclusive access to PHY.  The first driver calling
+        * phy_init()  on a given channel wins, and all attempts  to use another
+        * PHY on this channel will fail until phy_exit() is called by the first
+        * driver.   Achieving this with cmpxcgh() should be SMP-safe.
+        */
+       if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1)
+               return -EBUSY;
+
+       clk_prepare_enable(drv->clk);
+
+       spin_lock_irqsave(&drv->lock, flags);
+       ugctrl2 = readl(drv->base + USBHS_UGCTRL2);
+       ugctrl2 &= ~channel->select_mask;
+       ugctrl2 |= phy->select_value;
+       writel(ugctrl2, drv->base + USBHS_UGCTRL2);
+       spin_unlock_irqrestore(&drv->lock, flags);
+       return 0;
+}
+
+static int rcar_gen2_phy_exit(struct phy *p)
+{
+       struct rcar_gen2_phy *phy = phy_get_drvdata(p);
+       struct rcar_gen2_channel *channel = phy->channel;
+
+       clk_disable_unprepare(channel->drv->clk);
+
+       channel->selected_phy = -1;
+
+       return 0;
+}
+
+static int rcar_gen2_phy_power_on(struct phy *p)
+{
+       struct rcar_gen2_phy *phy = phy_get_drvdata(p);
+       struct rcar_gen2_phy_driver *drv = phy->channel->drv;
+       void __iomem *base = drv->base;
+       unsigned long flags;
+       u32 value;
+       int err = 0, i;
+
+       /* Skip if it's not USBHS */
+       if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
+               return 0;
+
+       spin_lock_irqsave(&drv->lock, flags);
+
+       /* Power on USBHS PHY */
+       value = readl(base + USBHS_UGCTRL);
+       value &= ~USBHS_UGCTRL_PLLRESET;
+       writel(value, base + USBHS_UGCTRL);
+
+       value = readw(base + USBHS_LPSTS);
+       value |= USBHS_LPSTS_SUSPM;
+       writew(value, base + USBHS_LPSTS);
+
+       for (i = 0; i < 20; i++) {
+               value = readl(base + USBHS_UGSTS);
+               if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) {
+                       value = readl(base + USBHS_UGCTRL);
+                       value |= USBHS_UGCTRL_CONNECT;
+                       writel(value, base + USBHS_UGCTRL);
+                       goto out;
+               }
+               udelay(1);
+       }
+
+       /* Timed out waiting for the PLL lock */
+       err = -ETIMEDOUT;
+
+out:
+       spin_unlock_irqrestore(&drv->lock, flags);
+
+       return err;
+}
+
+static int rcar_gen2_phy_power_off(struct phy *p)
+{
+       struct rcar_gen2_phy *phy = phy_get_drvdata(p);
+       struct rcar_gen2_phy_driver *drv = phy->channel->drv;
+       void __iomem *base = drv->base;
+       unsigned long flags;
+       u32 value;
+
+       /* Skip if it's not USBHS */
+       if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
+               return 0;
+
+       spin_lock_irqsave(&drv->lock, flags);
+
+       /* Power off USBHS PHY */
+       value = readl(base + USBHS_UGCTRL);
+       value &= ~USBHS_UGCTRL_CONNECT;
+       writel(value, base + USBHS_UGCTRL);
+
+       value = readw(base + USBHS_LPSTS);
+       value &= ~USBHS_LPSTS_SUSPM;
+       writew(value, base + USBHS_LPSTS);
+
+       value = readl(base + USBHS_UGCTRL);
+       value |= USBHS_UGCTRL_PLLRESET;
+       writel(value, base + USBHS_UGCTRL);
+
+       spin_unlock_irqrestore(&drv->lock, flags);
+
+       return 0;
+}
+
+static struct phy_ops rcar_gen2_phy_ops = {
+       .init           = rcar_gen2_phy_init,
+       .exit           = rcar_gen2_phy_exit,
+       .power_on       = rcar_gen2_phy_power_on,
+       .power_off      = rcar_gen2_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static const struct of_device_id rcar_gen2_phy_match_table[] = {
+       { .compatible = "renesas,usb-phy-r8a7790" },
+       { .compatible = "renesas,usb-phy-r8a7791" },
+       { }
+};
+MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
+
+static struct phy *rcar_gen2_phy_xlate(struct device *dev,
+                                      struct of_phandle_args *args)
+{
+       struct rcar_gen2_phy_driver *drv;
+       struct device_node *np = args->np;
+       int i;
+
+       if (!of_device_is_available(np)) {
+               dev_warn(dev, "Requested PHY is disabled\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       drv = dev_get_drvdata(dev);
+       if (!drv)
+               return ERR_PTR(-EINVAL);
+
+       for (i = 0; i < drv->num_channels; i++) {
+               if (np == drv->channels[i].of_node)
+                       break;
+       }
+
+       if (i >= drv->num_channels || args->args[0] >= 2)
+               return ERR_PTR(-ENODEV);
+
+       return drv->channels[i].phys[args->args[0]].phy;
+}
+
+static const u32 select_mask[] = {
+       [0]     = USBHS_UGCTRL2_USB0SEL,
+       [2]     = USBHS_UGCTRL2_USB2SEL,
+};
+
+static const u32 select_value[][PHYS_PER_CHANNEL] = {
+       [0]     = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
+       [2]     = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
+};
+
+static int rcar_gen2_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rcar_gen2_phy_driver *drv;
+       struct phy_provider *provider;
+       struct device_node *np;
+       struct resource *res;
+       void __iomem *base;
+       struct clk *clk;
+       int i = 0;
+
+       if (!dev->of_node) {
+               dev_err(dev,
+                       "This driver is required to be instantiated from device tree\n");
+               return -EINVAL;
+       }
+
+       clk = devm_clk_get(dev, "usbhs");
+       if (IS_ERR(clk)) {
+               dev_err(dev, "Can't get USBHS clock\n");
+               return PTR_ERR(clk);
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
+       if (!drv)
+               return -ENOMEM;
+
+       spin_lock_init(&drv->lock);
+
+       drv->clk = clk;
+       drv->base = base;
+
+       drv->num_channels = of_get_child_count(dev->of_node);
+       drv->channels = devm_kcalloc(dev, drv->num_channels,
+                                    sizeof(struct rcar_gen2_channel),
+                                    GFP_KERNEL);
+       if (!drv->channels)
+               return -ENOMEM;
+
+       for_each_child_of_node(dev->of_node, np) {
+               struct rcar_gen2_channel *channel = drv->channels + i;
+               u32 channel_num;
+               int error, n;
+
+               channel->of_node = np;
+               channel->drv = drv;
+               channel->selected_phy = -1;
+
+               error = of_property_read_u32(np, "reg", &channel_num);
+               if (error || channel_num > 2) {
+                       dev_err(dev, "Invalid \"reg\" property\n");
+                       return error;
+               }
+               channel->select_mask = select_mask[channel_num];
+
+               for (n = 0; n < PHYS_PER_CHANNEL; n++) {
+                       struct rcar_gen2_phy *phy = &channel->phys[n];
+
+                       phy->channel = channel;
+                       phy->number = n;
+                       phy->select_value = select_value[channel_num][n];
+
+                       phy->phy = devm_phy_create(dev, NULL,
+                                                  &rcar_gen2_phy_ops, NULL);
+                       if (IS_ERR(phy->phy)) {
+                               dev_err(dev, "Failed to create PHY\n");
+                               return PTR_ERR(phy->phy);
+                       }
+                       phy_set_drvdata(phy->phy, phy);
+               }
+
+               i++;
+       }
+
+       provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate);
+       if (IS_ERR(provider)) {
+               dev_err(dev, "Failed to register PHY provider\n");
+               return PTR_ERR(provider);
+       }
+
+       dev_set_drvdata(dev, drv);
+
+       return 0;
+}
+
+static struct platform_driver rcar_gen2_phy_driver = {
+       .driver = {
+               .name           = "phy_rcar_gen2",
+               .of_match_table = rcar_gen2_phy_match_table,
+       },
+       .probe  = rcar_gen2_phy_probe,
+};
+
+module_platform_driver(rcar_gen2_phy_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY");
+MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
new file mode 100644 (file)
index 0000000..2586b76
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Rockchip usb PHY driver
+ *
+ * Copyright (C) 2014 Roy Li <lyz@rock-chips.com>
+ * Copyright (C) 2014 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/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define ROCKCHIP_RK3288_UOC(n) (0x320 + n * 0x14)
+
+#define SIDDQ_MSK              (1 << (13 + 16))
+#define SIDDQ_ON               (1 << 13)
+#define SIDDQ_OFF              (0 << 13)
+
+enum rk3288_phy_id {
+       RK3288_OTG,
+       RK3288_HOST0,
+       RK3288_HOST1,
+       RK3288_NUM_PHYS,
+};
+
+struct rockchip_usb_phy {
+       struct regmap *reg_base;
+       unsigned int reg_offset;
+       struct clk *clk;
+       struct phy *phy;
+};
+
+static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
+                                          bool siddq)
+{
+       return regmap_write(phy->reg_base, phy->reg_offset,
+                           SIDDQ_MSK | (siddq ? SIDDQ_ON : SIDDQ_OFF));
+}
+
+static int rockchip_usb_phy_power_off(struct phy *_phy)
+{
+       struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+       int ret = 0;
+
+       /* Power down usb phy analog blocks by set siddq 1*/
+       ret = rockchip_usb_phy_power(phy, 1);
+       if (ret)
+               return ret;
+
+       clk_disable_unprepare(phy->clk);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static int rockchip_usb_phy_power_on(struct phy *_phy)
+{
+       struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+       int ret = 0;
+
+       ret = clk_prepare_enable(phy->clk);
+       if (ret)
+               return ret;
+
+       /* Power up usb phy analog blocks by set siddq 0*/
+       ret = rockchip_usb_phy_power(phy, 0);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+static struct phy *rockchip_usb_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct rockchip_usb_phy *phy_array = dev_get_drvdata(dev);
+
+       if (WARN_ON(args->args[0] == 0 || args->args[0] >= RK3288_NUM_PHYS))
+               return ERR_PTR(-ENODEV);
+
+       return (phy_array + args->args[0])->phy;
+}
+
+static struct phy_ops ops = {
+       .power_on       = rockchip_usb_phy_power_on,
+       .power_off      = rockchip_usb_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static int rockchip_usb_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rockchip_usb_phy *rk_phy;
+       struct rockchip_usb_phy *phy_array;
+       struct phy_provider *phy_provider;
+       struct regmap *grf;
+       char clk_name[16];
+       int i;
+
+       grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+       if (IS_ERR(grf)) {
+               dev_err(&pdev->dev, "Missing rockchip,grf property\n");
+               return PTR_ERR(grf);
+       }
+
+       phy_array = devm_kzalloc(dev, RK3288_NUM_PHYS * sizeof(*rk_phy),
+                                GFP_KERNEL);
+       if (!phy_array)
+               return -ENOMEM;
+
+       for (i = 0; i < RK3288_NUM_PHYS; i++) {
+               rk_phy = &phy_array[i];
+
+               rk_phy->reg_base = grf;
+
+               rk_phy->reg_offset = ROCKCHIP_RK3288_UOC(i);
+
+               snprintf(clk_name, sizeof(clk_name), "usbphy%d", i);
+               rk_phy->clk = devm_clk_get(dev, clk_name);
+               if (IS_ERR(rk_phy->clk)) {
+                       dev_warn(dev, "failed to get clock %s\n", clk_name);
+                       rk_phy->clk = NULL;
+               }
+
+               rk_phy->phy = devm_phy_create(dev, NULL, &ops, NULL);
+               if (IS_ERR(rk_phy->phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", i);
+                       return PTR_ERR(rk_phy->phy);
+               }
+               phy_set_drvdata(rk_phy->phy, rk_phy);
+       }
+
+       platform_set_drvdata(pdev, phy_array);
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                                    rockchip_usb_phy_xlate);
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
+       { .compatible = "rockchip,rk3288-usb-phy" },
+       {}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
+
+static struct platform_driver rockchip_usb_driver = {
+       .probe          = rockchip_usb_phy_probe,
+       .driver         = {
+               .name   = "rockchip-usb-phy",
+               .owner  = THIS_MODULE,
+               .of_match_table = rockchip_usb_phy_dt_ids,
+       },
+};
+
+module_platform_driver(rockchip_usb_driver);
+
+MODULE_AUTHOR("Roy Li <lyz@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-s5pv210-usb2.c b/drivers/phy/phy-s5pv210-usb2.c
new file mode 100644 (file)
index 0000000..004d320
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver - S5PV210 support
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Authors: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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/delay.h>
+#include <linux/io.h>
+#include <linux/phy/phy.h>
+#include "phy-samsung-usb2.h"
+
+/* Exynos USB PHY registers */
+
+/* PHY power control */
+#define S5PV210_UPHYPWR                        0x0
+
+#define S5PV210_UPHYPWR_PHY0_SUSPEND   BIT(0)
+#define S5PV210_UPHYPWR_PHY0_PWR       BIT(3)
+#define S5PV210_UPHYPWR_PHY0_OTG_PWR   BIT(4)
+#define S5PV210_UPHYPWR_PHY0   ( \
+       S5PV210_UPHYPWR_PHY0_SUSPEND | \
+       S5PV210_UPHYPWR_PHY0_PWR | \
+       S5PV210_UPHYPWR_PHY0_OTG_PWR)
+
+#define S5PV210_UPHYPWR_PHY1_SUSPEND   BIT(6)
+#define S5PV210_UPHYPWR_PHY1_PWR       BIT(7)
+#define S5PV210_UPHYPWR_PHY1 ( \
+       S5PV210_UPHYPWR_PHY1_SUSPEND | \
+       S5PV210_UPHYPWR_PHY1_PWR)
+
+/* PHY clock control */
+#define S5PV210_UPHYCLK                        0x4
+
+#define S5PV210_UPHYCLK_PHYFSEL_MASK   (0x3 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_48MHZ  (0x0 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_24MHZ  (0x3 << 0)
+#define S5PV210_UPHYCLK_PHYFSEL_12MHZ  (0x2 << 0)
+
+#define S5PV210_UPHYCLK_PHY0_ID_PULLUP BIT(2)
+#define S5PV210_UPHYCLK_PHY0_COMMON_ON BIT(4)
+#define S5PV210_UPHYCLK_PHY1_COMMON_ON BIT(7)
+
+/* PHY reset control */
+#define S5PV210_UPHYRST                        0x8
+
+#define S5PV210_URSTCON_PHY0           BIT(0)
+#define S5PV210_URSTCON_OTG_HLINK      BIT(1)
+#define S5PV210_URSTCON_OTG_PHYLINK    BIT(2)
+#define S5PV210_URSTCON_PHY1_ALL       BIT(3)
+#define S5PV210_URSTCON_HOST_LINK_ALL  BIT(4)
+
+/* Isolation, configured in the power management unit */
+#define S5PV210_USB_ISOL_OFFSET                0x680c
+#define S5PV210_USB_ISOL_DEVICE                BIT(0)
+#define S5PV210_USB_ISOL_HOST          BIT(1)
+
+
+enum s5pv210_phy_id {
+       S5PV210_DEVICE,
+       S5PV210_HOST,
+       S5PV210_NUM_PHYS,
+};
+
+/*
+ * s5pv210_rate_to_clk() converts the supplied clock rate to the value that
+ * can be written to the phy register.
+ */
+static int s5pv210_rate_to_clk(unsigned long rate, u32 *reg)
+{
+       switch (rate) {
+       case 12 * MHZ:
+               *reg = S5PV210_UPHYCLK_PHYFSEL_12MHZ;
+               break;
+       case 24 * MHZ:
+               *reg = S5PV210_UPHYCLK_PHYFSEL_24MHZ;
+               break;
+       case 48 * MHZ:
+               *reg = S5PV210_UPHYCLK_PHYFSEL_48MHZ;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static void s5pv210_isol(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 mask;
+
+       switch (inst->cfg->id) {
+       case S5PV210_DEVICE:
+               mask = S5PV210_USB_ISOL_DEVICE;
+               break;
+       case S5PV210_HOST:
+               mask = S5PV210_USB_ISOL_HOST;
+               break;
+       default:
+               return;
+       };
+
+       regmap_update_bits(drv->reg_pmu, S5PV210_USB_ISOL_OFFSET,
+                                                       mask, on ? 0 : mask);
+}
+
+static void s5pv210_phy_pwr(struct samsung_usb2_phy_instance *inst, bool on)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       u32 rstbits = 0;
+       u32 phypwr = 0;
+       u32 rst;
+       u32 pwr;
+
+       switch (inst->cfg->id) {
+       case S5PV210_DEVICE:
+               phypwr =        S5PV210_UPHYPWR_PHY0;
+               rstbits =       S5PV210_URSTCON_PHY0;
+               break;
+       case S5PV210_HOST:
+               phypwr =        S5PV210_UPHYPWR_PHY1;
+               rstbits =       S5PV210_URSTCON_PHY1_ALL |
+                               S5PV210_URSTCON_HOST_LINK_ALL;
+               break;
+       };
+
+       if (on) {
+               writel(drv->ref_reg_val, drv->reg_phy + S5PV210_UPHYCLK);
+
+               pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
+               pwr &= ~phypwr;
+               writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
+
+               rst = readl(drv->reg_phy + S5PV210_UPHYRST);
+               rst |= rstbits;
+               writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+               udelay(10);
+               rst &= ~rstbits;
+               writel(rst, drv->reg_phy + S5PV210_UPHYRST);
+       } else {
+               pwr = readl(drv->reg_phy + S5PV210_UPHYPWR);
+               pwr |= phypwr;
+               writel(pwr, drv->reg_phy + S5PV210_UPHYPWR);
+       }
+}
+
+static int s5pv210_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       s5pv210_isol(inst, 0);
+       s5pv210_phy_pwr(inst, 1);
+
+       return 0;
+}
+
+static int s5pv210_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       s5pv210_phy_pwr(inst, 0);
+       s5pv210_isol(inst, 1);
+
+       return 0;
+}
+
+static const struct samsung_usb2_common_phy s5pv210_phys[S5PV210_NUM_PHYS] = {
+       [S5PV210_DEVICE] = {
+               .label          = "device",
+               .id             = S5PV210_DEVICE,
+               .power_on       = s5pv210_power_on,
+               .power_off      = s5pv210_power_off,
+       },
+       [S5PV210_HOST] = {
+               .label          = "host",
+               .id             = S5PV210_HOST,
+               .power_on       = s5pv210_power_on,
+               .power_off      = s5pv210_power_off,
+       },
+};
+
+const struct samsung_usb2_phy_config s5pv210_usb2_phy_config = {
+       .num_phys       = ARRAY_SIZE(s5pv210_phys),
+       .phys           = s5pv210_phys,
+       .rate_to_clk    = s5pv210_rate_to_clk,
+};
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
new file mode 100644 (file)
index 0000000..908949d
--- /dev/null
@@ -0,0 +1,241 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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/clk.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/spinlock.h>
+#include "phy-samsung-usb2.h"
+
+static int samsung_usb2_phy_power_on(struct phy *phy)
+{
+       struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       int ret;
+
+       dev_dbg(drv->dev, "Request to power_on \"%s\" usb phy\n",
+               inst->cfg->label);
+       ret = clk_prepare_enable(drv->clk);
+       if (ret)
+               goto err_main_clk;
+       ret = clk_prepare_enable(drv->ref_clk);
+       if (ret)
+               goto err_instance_clk;
+       if (inst->cfg->power_on) {
+               spin_lock(&drv->lock);
+               ret = inst->cfg->power_on(inst);
+               spin_unlock(&drv->lock);
+       }
+
+       return 0;
+
+err_instance_clk:
+       clk_disable_unprepare(drv->clk);
+err_main_clk:
+       return ret;
+}
+
+static int samsung_usb2_phy_power_off(struct phy *phy)
+{
+       struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       int ret = 0;
+
+       dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
+               inst->cfg->label);
+       if (inst->cfg->power_off) {
+               spin_lock(&drv->lock);
+               ret = inst->cfg->power_off(inst);
+               spin_unlock(&drv->lock);
+       }
+       clk_disable_unprepare(drv->ref_clk);
+       clk_disable_unprepare(drv->clk);
+       return ret;
+}
+
+static struct phy_ops samsung_usb2_phy_ops = {
+       .power_on       = samsung_usb2_phy_power_on,
+       .power_off      = samsung_usb2_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *samsung_usb2_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct samsung_usb2_phy_driver *drv;
+
+       drv = dev_get_drvdata(dev);
+       if (!drv)
+               return ERR_PTR(-EINVAL);
+
+       if (WARN_ON(args->args[0] >= drv->cfg->num_phys))
+               return ERR_PTR(-ENODEV);
+
+       return drv->instances[args->args[0]].phy;
+}
+
+static const struct of_device_id samsung_usb2_phy_of_match[] = {
+#ifdef CONFIG_PHY_EXYNOS4X12_USB2
+       {
+               .compatible = "samsung,exynos3250-usb2-phy",
+               .data = &exynos3250_usb2_phy_config,
+       },
+#endif
+#ifdef CONFIG_PHY_EXYNOS4210_USB2
+       {
+               .compatible = "samsung,exynos4210-usb2-phy",
+               .data = &exynos4210_usb2_phy_config,
+       },
+#endif
+#ifdef CONFIG_PHY_EXYNOS4X12_USB2
+       {
+               .compatible = "samsung,exynos4x12-usb2-phy",
+               .data = &exynos4x12_usb2_phy_config,
+       },
+#endif
+#ifdef CONFIG_PHY_EXYNOS5250_USB2
+       {
+               .compatible = "samsung,exynos5250-usb2-phy",
+               .data = &exynos5250_usb2_phy_config,
+       },
+#endif
+#ifdef CONFIG_PHY_S5PV210_USB2
+       {
+               .compatible = "samsung,s5pv210-usb2-phy",
+               .data = &s5pv210_usb2_phy_config,
+       },
+#endif
+       { },
+};
+MODULE_DEVICE_TABLE(of, samsung_usb2_phy_of_match);
+
+static int samsung_usb2_phy_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match;
+       const struct samsung_usb2_phy_config *cfg;
+       struct device *dev = &pdev->dev;
+       struct phy_provider *phy_provider;
+       struct resource *mem;
+       struct samsung_usb2_phy_driver *drv;
+       int i, ret;
+
+       if (!pdev->dev.of_node) {
+               dev_err(dev, "This driver is required to be instantiated from device tree\n");
+               return -EINVAL;
+       }
+
+       match = of_match_node(samsung_usb2_phy_of_match, pdev->dev.of_node);
+       if (!match) {
+               dev_err(dev, "of_match_node() failed\n");
+               return -EINVAL;
+       }
+       cfg = match->data;
+
+       drv = devm_kzalloc(dev, sizeof(struct samsung_usb2_phy_driver) +
+               cfg->num_phys * sizeof(struct samsung_usb2_phy_instance),
+                                                               GFP_KERNEL);
+       if (!drv)
+               return -ENOMEM;
+
+       dev_set_drvdata(dev, drv);
+       spin_lock_init(&drv->lock);
+
+       drv->cfg = cfg;
+       drv->dev = dev;
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       drv->reg_phy = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(drv->reg_phy)) {
+               dev_err(dev, "Failed to map register memory (phy)\n");
+               return PTR_ERR(drv->reg_phy);
+       }
+
+       drv->reg_pmu = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+               "samsung,pmureg-phandle");
+       if (IS_ERR(drv->reg_pmu)) {
+               dev_err(dev, "Failed to map PMU registers (via syscon)\n");
+               return PTR_ERR(drv->reg_pmu);
+       }
+
+       if (drv->cfg->has_mode_switch) {
+               drv->reg_sys = syscon_regmap_lookup_by_phandle(
+                               pdev->dev.of_node, "samsung,sysreg-phandle");
+               if (IS_ERR(drv->reg_sys)) {
+                       dev_err(dev, "Failed to map system registers (via syscon)\n");
+                       return PTR_ERR(drv->reg_sys);
+               }
+       }
+
+       drv->clk = devm_clk_get(dev, "phy");
+       if (IS_ERR(drv->clk)) {
+               dev_err(dev, "Failed to get clock of phy controller\n");
+               return PTR_ERR(drv->clk);
+       }
+
+       drv->ref_clk = devm_clk_get(dev, "ref");
+       if (IS_ERR(drv->ref_clk)) {
+               dev_err(dev, "Failed to get reference clock for the phy controller\n");
+               return PTR_ERR(drv->ref_clk);
+       }
+
+       drv->ref_rate = clk_get_rate(drv->ref_clk);
+       if (drv->cfg->rate_to_clk) {
+               ret = drv->cfg->rate_to_clk(drv->ref_rate, &drv->ref_reg_val);
+               if (ret)
+                       return ret;
+       }
+
+       for (i = 0; i < drv->cfg->num_phys; i++) {
+               char *label = drv->cfg->phys[i].label;
+               struct samsung_usb2_phy_instance *p = &drv->instances[i];
+
+               dev_dbg(dev, "Creating phy \"%s\"\n", label);
+               p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops,
+                                        NULL);
+               if (IS_ERR(p->phy)) {
+                       dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
+                               label);
+                       return PTR_ERR(p->phy);
+               }
+
+               p->cfg = &drv->cfg->phys[i];
+               p->drv = drv;
+               phy_set_bus_width(p->phy, 8);
+               phy_set_drvdata(p->phy, p);
+       }
+
+       phy_provider = devm_of_phy_provider_register(dev,
+                                                       samsung_usb2_phy_xlate);
+       if (IS_ERR(phy_provider)) {
+               dev_err(drv->dev, "Failed to register phy provider\n");
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static struct platform_driver samsung_usb2_phy_driver = {
+       .probe  = samsung_usb2_phy_probe,
+       .driver = {
+               .of_match_table = samsung_usb2_phy_of_match,
+               .name           = "samsung-usb2-phy",
+       }
+};
+
+module_platform_driver(samsung_usb2_phy_driver);
+MODULE_DESCRIPTION("Samsung S5P/EXYNOS SoC USB PHY driver");
+MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:samsung-usb2-phy");
diff --git a/drivers/phy/phy-samsung-usb2.h b/drivers/phy/phy-samsung-usb2.h
new file mode 100644 (file)
index 0000000..44bead9
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * Samsung SoC USB 1.1/2.0 PHY driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Kamil Debski <k.debski@samsung.com>
+ *
+ * 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.
+ */
+
+#ifndef _PHY_EXYNOS_USB2_H
+#define _PHY_EXYNOS_USB2_H
+
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/spinlock.h>
+
+#define KHZ 1000
+#define MHZ (KHZ * KHZ)
+
+struct samsung_usb2_phy_driver;
+struct samsung_usb2_phy_instance;
+struct samsung_usb2_phy_config;
+
+struct samsung_usb2_phy_instance {
+       const struct samsung_usb2_common_phy *cfg;
+       struct phy *phy;
+       struct samsung_usb2_phy_driver *drv;
+       int int_cnt;
+       int ext_cnt;
+};
+
+struct samsung_usb2_phy_driver {
+       const struct samsung_usb2_phy_config *cfg;
+       struct clk *clk;
+       struct clk *ref_clk;
+       unsigned long ref_rate;
+       u32 ref_reg_val;
+       struct device *dev;
+       void __iomem *reg_phy;
+       struct regmap *reg_pmu;
+       struct regmap *reg_sys;
+       spinlock_t lock;
+       struct samsung_usb2_phy_instance instances[0];
+};
+
+struct samsung_usb2_common_phy {
+       int (*power_on)(struct samsung_usb2_phy_instance *);
+       int (*power_off)(struct samsung_usb2_phy_instance *);
+       unsigned int id;
+       char *label;
+};
+
+
+struct samsung_usb2_phy_config {
+       const struct samsung_usb2_common_phy *phys;
+       int (*rate_to_clk)(unsigned long, u32 *);
+       unsigned int num_phys;
+       bool has_mode_switch;
+       bool has_refclk_sel;
+};
+
+extern const struct samsung_usb2_phy_config exynos3250_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos4210_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos4x12_usb2_phy_config;
+extern const struct samsung_usb2_phy_config exynos5250_usb2_phy_config;
+extern const struct samsung_usb2_phy_config s5pv210_usb2_phy_config;
+#endif
diff --git a/drivers/phy/phy-spear1310-miphy.c b/drivers/phy/phy-spear1310-miphy.c
new file mode 100644 (file)
index 0000000..5f4c586
--- /dev/null
@@ -0,0 +1,261 @@
+/*
+ * ST SPEAr1310-miphy driver
+ *
+ * Copyright (C) 2014 ST Microelectronics
+ * Pratyush Anand <pratyush.anand@st.com>
+ * Mohit Kumar <mohit.kumar@st.com>
+ *
+ * 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+/* SPEAr1310 Registers */
+#define SPEAR1310_PCIE_SATA_CFG                        0x3A4
+       #define SPEAR1310_PCIE_SATA2_SEL_PCIE           (0 << 31)
+       #define SPEAR1310_PCIE_SATA1_SEL_PCIE           (0 << 30)
+       #define SPEAR1310_PCIE_SATA0_SEL_PCIE           (0 << 29)
+       #define SPEAR1310_PCIE_SATA2_SEL_SATA           BIT(31)
+       #define SPEAR1310_PCIE_SATA1_SEL_SATA           BIT(30)
+       #define SPEAR1310_PCIE_SATA0_SEL_SATA           BIT(29)
+       #define SPEAR1310_SATA2_CFG_TX_CLK_EN           BIT(27)
+       #define SPEAR1310_SATA2_CFG_RX_CLK_EN           BIT(26)
+       #define SPEAR1310_SATA2_CFG_POWERUP_RESET       BIT(25)
+       #define SPEAR1310_SATA2_CFG_PM_CLK_EN           BIT(24)
+       #define SPEAR1310_SATA1_CFG_TX_CLK_EN           BIT(23)
+       #define SPEAR1310_SATA1_CFG_RX_CLK_EN           BIT(22)
+       #define SPEAR1310_SATA1_CFG_POWERUP_RESET       BIT(21)
+       #define SPEAR1310_SATA1_CFG_PM_CLK_EN           BIT(20)
+       #define SPEAR1310_SATA0_CFG_TX_CLK_EN           BIT(19)
+       #define SPEAR1310_SATA0_CFG_RX_CLK_EN           BIT(18)
+       #define SPEAR1310_SATA0_CFG_POWERUP_RESET       BIT(17)
+       #define SPEAR1310_SATA0_CFG_PM_CLK_EN           BIT(16)
+       #define SPEAR1310_PCIE2_CFG_DEVICE_PRESENT      BIT(11)
+       #define SPEAR1310_PCIE2_CFG_POWERUP_RESET       BIT(10)
+       #define SPEAR1310_PCIE2_CFG_CORE_CLK_EN         BIT(9)
+       #define SPEAR1310_PCIE2_CFG_AUX_CLK_EN          BIT(8)
+       #define SPEAR1310_PCIE1_CFG_DEVICE_PRESENT      BIT(7)
+       #define SPEAR1310_PCIE1_CFG_POWERUP_RESET       BIT(6)
+       #define SPEAR1310_PCIE1_CFG_CORE_CLK_EN         BIT(5)
+       #define SPEAR1310_PCIE1_CFG_AUX_CLK_EN          BIT(4)
+       #define SPEAR1310_PCIE0_CFG_DEVICE_PRESENT      BIT(3)
+       #define SPEAR1310_PCIE0_CFG_POWERUP_RESET       BIT(2)
+       #define SPEAR1310_PCIE0_CFG_CORE_CLK_EN         BIT(1)
+       #define SPEAR1310_PCIE0_CFG_AUX_CLK_EN          BIT(0)
+
+       #define SPEAR1310_PCIE_CFG_MASK(x) ((0xF << (x * 4)) | BIT((x + 29)))
+       #define SPEAR1310_SATA_CFG_MASK(x) ((0xF << (x * 4 + 16)) | \
+                       BIT((x + 29)))
+       #define SPEAR1310_PCIE_CFG_VAL(x) \
+                       (SPEAR1310_PCIE_SATA##x##_SEL_PCIE | \
+                       SPEAR1310_PCIE##x##_CFG_AUX_CLK_EN | \
+                       SPEAR1310_PCIE##x##_CFG_CORE_CLK_EN | \
+                       SPEAR1310_PCIE##x##_CFG_POWERUP_RESET | \
+                       SPEAR1310_PCIE##x##_CFG_DEVICE_PRESENT)
+       #define SPEAR1310_SATA_CFG_VAL(x) \
+                       (SPEAR1310_PCIE_SATA##x##_SEL_SATA | \
+                       SPEAR1310_SATA##x##_CFG_PM_CLK_EN | \
+                       SPEAR1310_SATA##x##_CFG_POWERUP_RESET | \
+                       SPEAR1310_SATA##x##_CFG_RX_CLK_EN | \
+                       SPEAR1310_SATA##x##_CFG_TX_CLK_EN)
+
+#define SPEAR1310_PCIE_MIPHY_CFG_1             0x3A8
+       #define SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT     BIT(31)
+       #define SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2       BIT(28)
+       #define SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(x)   (x << 16)
+       #define SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT   BIT(15)
+       #define SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2     BIT(12)
+       #define SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(x) (x << 0)
+       #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_MASK (0xFFFF)
+       #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK (0xFFFF << 16)
+       #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA \
+                       (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \
+                       SPEAR1310_MIPHY_DUAL_CLK_REF_DIV2 | \
+                       SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(60) | \
+                       SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \
+                       SPEAR1310_MIPHY_SINGLE_CLK_REF_DIV2 | \
+                       SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(60))
+       #define SPEAR1310_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
+                       (SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(120))
+       #define SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE \
+                       (SPEAR1310_MIPHY_DUAL_OSC_BYPASS_EXT | \
+                       SPEAR1310_MIPHY_DUAL_PLL_RATIO_TOP(25) | \
+                       SPEAR1310_MIPHY_SINGLE_OSC_BYPASS_EXT | \
+                       SPEAR1310_MIPHY_SINGLE_PLL_RATIO_TOP(25))
+
+#define SPEAR1310_PCIE_MIPHY_CFG_2             0x3AC
+
+enum spear1310_miphy_mode {
+       SATA,
+       PCIE,
+};
+
+struct spear1310_miphy_priv {
+       /* instance id of this phy */
+       u32                             id;
+       /* phy mode: 0 for SATA 1 for PCIe */
+       enum spear1310_miphy_mode       mode;
+       /* regmap for any soc specific misc registers */
+       struct regmap                   *misc;
+       /* phy struct pointer */
+       struct phy                      *phy;
+};
+
+static int spear1310_miphy_pcie_init(struct spear1310_miphy_priv *priv)
+{
+       u32 val;
+
+       regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1,
+                          SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK,
+                          SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE);
+
+       switch (priv->id) {
+       case 0:
+               val = SPEAR1310_PCIE_CFG_VAL(0);
+               break;
+       case 1:
+               val = SPEAR1310_PCIE_CFG_VAL(1);
+               break;
+       case 2:
+               val = SPEAR1310_PCIE_CFG_VAL(2);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG,
+                          SPEAR1310_PCIE_CFG_MASK(priv->id), val);
+
+       return 0;
+}
+
+static int spear1310_miphy_pcie_exit(struct spear1310_miphy_priv *priv)
+{
+       regmap_update_bits(priv->misc, SPEAR1310_PCIE_SATA_CFG,
+                          SPEAR1310_PCIE_CFG_MASK(priv->id), 0);
+
+       regmap_update_bits(priv->misc, SPEAR1310_PCIE_MIPHY_CFG_1,
+                          SPEAR1310_PCIE_SATA_MIPHY_CFG_PCIE_MASK, 0);
+
+       return 0;
+}
+
+static int spear1310_miphy_init(struct phy *phy)
+{
+       struct spear1310_miphy_priv *priv = phy_get_drvdata(phy);
+       int ret = 0;
+
+       if (priv->mode == PCIE)
+               ret = spear1310_miphy_pcie_init(priv);
+
+       return ret;
+}
+
+static int spear1310_miphy_exit(struct phy *phy)
+{
+       struct spear1310_miphy_priv *priv = phy_get_drvdata(phy);
+       int ret = 0;
+
+       if (priv->mode == PCIE)
+               ret = spear1310_miphy_pcie_exit(priv);
+
+       return ret;
+}
+
+static const struct of_device_id spear1310_miphy_of_match[] = {
+       { .compatible = "st,spear1310-miphy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, spear1310_miphy_of_match);
+
+static struct phy_ops spear1310_miphy_ops = {
+       .init = spear1310_miphy_init,
+       .exit = spear1310_miphy_exit,
+       .owner = THIS_MODULE,
+};
+
+static struct phy *spear1310_miphy_xlate(struct device *dev,
+                                        struct of_phandle_args *args)
+{
+       struct spear1310_miphy_priv *priv = dev_get_drvdata(dev);
+
+       if (args->args_count < 1) {
+               dev_err(dev, "DT did not pass correct no of args\n");
+               return NULL;
+       }
+
+       priv->mode = args->args[0];
+
+       if (priv->mode != SATA && priv->mode != PCIE) {
+               dev_err(dev, "DT did not pass correct phy mode\n");
+               return NULL;
+       }
+
+       return priv->phy;
+}
+
+static int spear1310_miphy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct spear1310_miphy_priv *priv;
+       struct phy_provider *phy_provider;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->misc =
+               syscon_regmap_lookup_by_phandle(dev->of_node, "misc");
+       if (IS_ERR(priv->misc)) {
+               dev_err(dev, "failed to find misc regmap\n");
+               return PTR_ERR(priv->misc);
+       }
+
+       if (of_property_read_u32(dev->of_node, "phy-id", &priv->id)) {
+               dev_err(dev, "failed to find phy id\n");
+               return -EINVAL;
+       }
+
+       priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops, NULL);
+       if (IS_ERR(priv->phy)) {
+               dev_err(dev, "failed to create SATA PCIe PHY\n");
+               return PTR_ERR(priv->phy);
+       }
+
+       dev_set_drvdata(dev, priv);
+       phy_set_drvdata(priv->phy, priv);
+
+       phy_provider =
+               devm_of_phy_provider_register(dev, spear1310_miphy_xlate);
+       if (IS_ERR(phy_provider)) {
+               dev_err(dev, "failed to register phy provider\n");
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static struct platform_driver spear1310_miphy_driver = {
+       .probe          = spear1310_miphy_probe,
+       .driver = {
+               .name = "spear1310-miphy",
+               .of_match_table = of_match_ptr(spear1310_miphy_of_match),
+       },
+};
+
+module_platform_driver(spear1310_miphy_driver);
+
+MODULE_DESCRIPTION("ST SPEAR1310-MIPHY driver");
+MODULE_AUTHOR("Pratyush Anand <pratyush.anand@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-spear1340-miphy.c b/drivers/phy/phy-spear1340-miphy.c
new file mode 100644 (file)
index 0000000..1ecd094
--- /dev/null
@@ -0,0 +1,294 @@
+/*
+ * ST spear1340-miphy driver
+ *
+ * Copyright (C) 2014 ST Microelectronics
+ * Pratyush Anand <pratyush.anand@st.com>
+ * Mohit Kumar <mohit.kumar@st.com>
+ *
+ * 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/bitops.h>
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+
+/* SPEAr1340 Registers */
+/* Power Management Registers */
+#define SPEAR1340_PCM_CFG                      0x100
+       #define SPEAR1340_PCM_CFG_SATA_POWER_EN         BIT(11)
+#define SPEAR1340_PCM_WKUP_CFG                 0x104
+#define SPEAR1340_SWITCH_CTR                   0x108
+
+#define SPEAR1340_PERIP1_SW_RST                        0x318
+       #define SPEAR1340_PERIP1_SW_RSATA               BIT(12)
+#define SPEAR1340_PERIP2_SW_RST                        0x31C
+#define SPEAR1340_PERIP3_SW_RST                        0x320
+
+/* PCIE - SATA configuration registers */
+#define SPEAR1340_PCIE_SATA_CFG                        0x424
+       /* PCIE CFG MASks */
+       #define SPEAR1340_PCIE_CFG_DEVICE_PRESENT       BIT(11)
+       #define SPEAR1340_PCIE_CFG_POWERUP_RESET        BIT(10)
+       #define SPEAR1340_PCIE_CFG_CORE_CLK_EN          BIT(9)
+       #define SPEAR1340_PCIE_CFG_AUX_CLK_EN           BIT(8)
+       #define SPEAR1340_SATA_CFG_TX_CLK_EN            BIT(4)
+       #define SPEAR1340_SATA_CFG_RX_CLK_EN            BIT(3)
+       #define SPEAR1340_SATA_CFG_POWERUP_RESET        BIT(2)
+       #define SPEAR1340_SATA_CFG_PM_CLK_EN            BIT(1)
+       #define SPEAR1340_PCIE_SATA_SEL_PCIE            (0)
+       #define SPEAR1340_PCIE_SATA_SEL_SATA            (1)
+       #define SPEAR1340_PCIE_SATA_CFG_MASK            0xF1F
+       #define SPEAR1340_PCIE_CFG_VAL  (SPEAR1340_PCIE_SATA_SEL_PCIE | \
+                       SPEAR1340_PCIE_CFG_AUX_CLK_EN | \
+                       SPEAR1340_PCIE_CFG_CORE_CLK_EN | \
+                       SPEAR1340_PCIE_CFG_POWERUP_RESET | \
+                       SPEAR1340_PCIE_CFG_DEVICE_PRESENT)
+       #define SPEAR1340_SATA_CFG_VAL  (SPEAR1340_PCIE_SATA_SEL_SATA | \
+                       SPEAR1340_SATA_CFG_PM_CLK_EN | \
+                       SPEAR1340_SATA_CFG_POWERUP_RESET | \
+                       SPEAR1340_SATA_CFG_RX_CLK_EN | \
+                       SPEAR1340_SATA_CFG_TX_CLK_EN)
+
+#define SPEAR1340_PCIE_MIPHY_CFG               0x428
+       #define SPEAR1340_MIPHY_OSC_BYPASS_EXT          BIT(31)
+       #define SPEAR1340_MIPHY_CLK_REF_DIV2            BIT(27)
+       #define SPEAR1340_MIPHY_CLK_REF_DIV4            (2 << 27)
+       #define SPEAR1340_MIPHY_CLK_REF_DIV8            (3 << 27)
+       #define SPEAR1340_MIPHY_PLL_RATIO_TOP(x)        (x << 0)
+       #define SPEAR1340_PCIE_MIPHY_CFG_MASK           0xF80000FF
+       #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA \
+                       (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
+                       SPEAR1340_MIPHY_CLK_REF_DIV2 | \
+                       SPEAR1340_MIPHY_PLL_RATIO_TOP(60))
+       #define SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK \
+                       (SPEAR1340_MIPHY_PLL_RATIO_TOP(120))
+       #define SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE \
+                       (SPEAR1340_MIPHY_OSC_BYPASS_EXT | \
+                       SPEAR1340_MIPHY_PLL_RATIO_TOP(25))
+
+enum spear1340_miphy_mode {
+       SATA,
+       PCIE,
+};
+
+struct spear1340_miphy_priv {
+       /* phy mode: 0 for SATA 1 for PCIe */
+       enum spear1340_miphy_mode       mode;
+       /* regmap for any soc specific misc registers */
+       struct regmap                   *misc;
+       /* phy struct pointer */
+       struct phy                      *phy;
+};
+
+static int spear1340_miphy_sata_init(struct spear1340_miphy_priv *priv)
+{
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
+                          SPEAR1340_PCIE_SATA_CFG_MASK,
+                          SPEAR1340_SATA_CFG_VAL);
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
+                          SPEAR1340_PCIE_MIPHY_CFG_MASK,
+                          SPEAR1340_PCIE_SATA_MIPHY_CFG_SATA_25M_CRYSTAL_CLK);
+       /* Switch on sata power domain */
+       regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG,
+                          SPEAR1340_PCM_CFG_SATA_POWER_EN,
+                          SPEAR1340_PCM_CFG_SATA_POWER_EN);
+       /* Wait for SATA power domain on */
+       msleep(20);
+
+       /* Disable PCIE SATA Controller reset */
+       regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST,
+                          SPEAR1340_PERIP1_SW_RSATA, 0);
+       /* Wait for SATA reset de-assert completion */
+       msleep(20);
+
+       return 0;
+}
+
+static int spear1340_miphy_sata_exit(struct spear1340_miphy_priv *priv)
+{
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
+                          SPEAR1340_PCIE_SATA_CFG_MASK, 0);
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
+                          SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
+
+       /* Enable PCIE SATA Controller reset */
+       regmap_update_bits(priv->misc, SPEAR1340_PERIP1_SW_RST,
+                          SPEAR1340_PERIP1_SW_RSATA,
+                          SPEAR1340_PERIP1_SW_RSATA);
+       /* Wait for SATA power domain off */
+       msleep(20);
+       /* Switch off sata power domain */
+       regmap_update_bits(priv->misc, SPEAR1340_PCM_CFG,
+                          SPEAR1340_PCM_CFG_SATA_POWER_EN, 0);
+       /* Wait for SATA reset assert completion */
+       msleep(20);
+
+       return 0;
+}
+
+static int spear1340_miphy_pcie_init(struct spear1340_miphy_priv *priv)
+{
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
+                          SPEAR1340_PCIE_MIPHY_CFG_MASK,
+                          SPEAR1340_PCIE_SATA_MIPHY_CFG_PCIE);
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
+                          SPEAR1340_PCIE_SATA_CFG_MASK,
+                          SPEAR1340_PCIE_CFG_VAL);
+
+       return 0;
+}
+
+static int spear1340_miphy_pcie_exit(struct spear1340_miphy_priv *priv)
+{
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_MIPHY_CFG,
+                          SPEAR1340_PCIE_MIPHY_CFG_MASK, 0);
+       regmap_update_bits(priv->misc, SPEAR1340_PCIE_SATA_CFG,
+                          SPEAR1340_PCIE_SATA_CFG_MASK, 0);
+
+       return 0;
+}
+
+static int spear1340_miphy_init(struct phy *phy)
+{
+       struct spear1340_miphy_priv *priv = phy_get_drvdata(phy);
+       int ret = 0;
+
+       if (priv->mode == SATA)
+               ret = spear1340_miphy_sata_init(priv);
+       else if (priv->mode == PCIE)
+               ret = spear1340_miphy_pcie_init(priv);
+
+       return ret;
+}
+
+static int spear1340_miphy_exit(struct phy *phy)
+{
+       struct spear1340_miphy_priv *priv = phy_get_drvdata(phy);
+       int ret = 0;
+
+       if (priv->mode == SATA)
+               ret = spear1340_miphy_sata_exit(priv);
+       else if (priv->mode == PCIE)
+               ret = spear1340_miphy_pcie_exit(priv);
+
+       return ret;
+}
+
+static const struct of_device_id spear1340_miphy_of_match[] = {
+       { .compatible = "st,spear1340-miphy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, spear1340_miphy_of_match);
+
+static struct phy_ops spear1340_miphy_ops = {
+       .init = spear1340_miphy_init,
+       .exit = spear1340_miphy_exit,
+       .owner = THIS_MODULE,
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int spear1340_miphy_suspend(struct device *dev)
+{
+       struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (priv->mode == SATA)
+               ret = spear1340_miphy_sata_exit(priv);
+
+       return ret;
+}
+
+static int spear1340_miphy_resume(struct device *dev)
+{
+       struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (priv->mode == SATA)
+               ret = spear1340_miphy_sata_init(priv);
+
+       return ret;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(spear1340_miphy_pm_ops, spear1340_miphy_suspend,
+                        spear1340_miphy_resume);
+
+static struct phy *spear1340_miphy_xlate(struct device *dev,
+                                        struct of_phandle_args *args)
+{
+       struct spear1340_miphy_priv *priv = dev_get_drvdata(dev);
+
+       if (args->args_count < 1) {
+               dev_err(dev, "DT did not pass correct no of args\n");
+               return NULL;
+       }
+
+       priv->mode = args->args[0];
+
+       if (priv->mode != SATA && priv->mode != PCIE) {
+               dev_err(dev, "DT did not pass correct phy mode\n");
+               return NULL;
+       }
+
+       return priv->phy;
+}
+
+static int spear1340_miphy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct spear1340_miphy_priv *priv;
+       struct phy_provider *phy_provider;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->misc =
+               syscon_regmap_lookup_by_phandle(dev->of_node, "misc");
+       if (IS_ERR(priv->misc)) {
+               dev_err(dev, "failed to find misc regmap\n");
+               return PTR_ERR(priv->misc);
+       }
+
+       priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops, NULL);
+       if (IS_ERR(priv->phy)) {
+               dev_err(dev, "failed to create SATA PCIe PHY\n");
+               return PTR_ERR(priv->phy);
+       }
+
+       dev_set_drvdata(dev, priv);
+       phy_set_drvdata(priv->phy, priv);
+
+       phy_provider =
+               devm_of_phy_provider_register(dev, spear1340_miphy_xlate);
+       if (IS_ERR(phy_provider)) {
+               dev_err(dev, "failed to register phy provider\n");
+               return PTR_ERR(phy_provider);
+       }
+
+       return 0;
+}
+
+static struct platform_driver spear1340_miphy_driver = {
+       .probe          = spear1340_miphy_probe,
+       .driver = {
+               .name = "spear1340-miphy",
+               .pm = &spear1340_miphy_pm_ops,
+               .of_match_table = of_match_ptr(spear1340_miphy_of_match),
+       },
+};
+
+module_platform_driver(spear1340_miphy_driver);
+
+MODULE_DESCRIPTION("ST SPEAR1340-MIPHY driver");
+MODULE_AUTHOR("Pratyush Anand <pratyush.anand@st.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-stih407-usb.c b/drivers/phy/phy-stih407-usb.c
new file mode 100644 (file)
index 0000000..42428d4
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics
+ *
+ * STMicroelectronics Generic PHY driver for STiH407 USB2.
+ *
+ * Author: Giuseppe Cavallaro <peppe.cavallaro@st.com>
+ *
+ * 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/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/phy/phy.h>
+
+/* Default PHY_SEL and REFCLKSEL configuration */
+#define STIH407_USB_PICOPHY_CTRL_PORT_CONF     0x6
+#define STIH407_USB_PICOPHY_CTRL_PORT_MASK     0x1f
+
+/* ports parameters overriding */
+#define STIH407_USB_PICOPHY_PARAM_DEF          0x39a4dc
+#define STIH407_USB_PICOPHY_PARAM_MASK         0xffffffff
+
+struct stih407_usb2_picophy {
+       struct phy *phy;
+       struct regmap *regmap;
+       struct device *dev;
+       struct reset_control *rstc;
+       struct reset_control *rstport;
+       int ctrl;
+       int param;
+};
+
+static int stih407_usb2_pico_ctrl(struct stih407_usb2_picophy *phy_dev)
+{
+       reset_control_deassert(phy_dev->rstc);
+
+       return regmap_update_bits(phy_dev->regmap, phy_dev->ctrl,
+                                 STIH407_USB_PICOPHY_CTRL_PORT_MASK,
+                                 STIH407_USB_PICOPHY_CTRL_PORT_CONF);
+}
+
+static int stih407_usb2_init_port(struct phy *phy)
+{
+       int ret;
+       struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy);
+
+       stih407_usb2_pico_ctrl(phy_dev);
+
+       ret = regmap_update_bits(phy_dev->regmap,
+                                phy_dev->param,
+                                STIH407_USB_PICOPHY_PARAM_MASK,
+                                STIH407_USB_PICOPHY_PARAM_DEF);
+       if (ret)
+               return ret;
+
+       return reset_control_deassert(phy_dev->rstport);
+}
+
+static int stih407_usb2_exit_port(struct phy *phy)
+{
+       struct stih407_usb2_picophy *phy_dev = phy_get_drvdata(phy);
+
+       /*
+        * Only port reset is asserted, phy global reset is kept untouched
+        * as other ports may still be active. When all ports are in reset
+        * state, assumption is made that power will be cut off on the phy, in
+        * case of suspend for instance. Theoretically, asserting individual
+        * reset (like here) or global reset should be equivalent.
+        */
+       return reset_control_assert(phy_dev->rstport);
+}
+
+static const struct phy_ops stih407_usb2_picophy_data = {
+       .init = stih407_usb2_init_port,
+       .exit = stih407_usb2_exit_port,
+       .owner = THIS_MODULE,
+};
+
+static int stih407_usb2_picophy_probe(struct platform_device *pdev)
+{
+       struct stih407_usb2_picophy *phy_dev;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct phy_provider *phy_provider;
+       struct phy *phy;
+       struct resource *res;
+
+       phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
+       if (!phy_dev)
+               return -ENOMEM;
+
+       phy_dev->dev = dev;
+       dev_set_drvdata(dev, phy_dev);
+
+       phy_dev->rstc = devm_reset_control_get(dev, "global");
+       if (IS_ERR(phy_dev->rstc)) {
+               dev_err(dev, "failed to ctrl picoPHY reset\n");
+               return PTR_ERR(phy_dev->rstc);
+       }
+
+       phy_dev->rstport = devm_reset_control_get(dev, "port");
+       if (IS_ERR(phy_dev->rstport)) {
+               dev_err(dev, "failed to ctrl picoPHY reset\n");
+               return PTR_ERR(phy_dev->rstport);
+       }
+
+       /* Reset port by default: only deassert it in phy init */
+       reset_control_assert(phy_dev->rstport);
+
+       phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+       if (IS_ERR(phy_dev->regmap)) {
+               dev_err(dev, "No syscfg phandle specified\n");
+               return PTR_ERR(phy_dev->regmap);
+       }
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
+       if (!res) {
+               dev_err(dev, "No ctrl reg found\n");
+               return -ENXIO;
+       }
+       phy_dev->ctrl = res->start;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "param");
+       if (!res) {
+               dev_err(dev, "No param reg found\n");
+               return -ENXIO;
+       }
+       phy_dev->param = res->start;
+
+       phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data, NULL);
+       if (IS_ERR(phy)) {
+               dev_err(dev, "failed to create Display Port PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       phy_dev->phy = phy;
+       phy_set_drvdata(phy, phy_dev);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       dev_info(dev, "STiH407 USB Generic picoPHY driver probed!");
+
+       return 0;
+}
+
+static const struct of_device_id stih407_usb2_picophy_of_match[] = {
+       { .compatible = "st,stih407-usb2-phy" },
+       { /*sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, stih407_usb2_picophy_of_match);
+
+static struct platform_driver stih407_usb2_picophy_driver = {
+       .probe = stih407_usb2_picophy_probe,
+       .driver = {
+                  .name = "stih407-usb-genphy",
+                  .of_match_table = stih407_usb2_picophy_of_match,
+                  }
+};
+
+module_platform_driver(stih407_usb2_picophy_driver);
+
+MODULE_AUTHOR("Giuseppe Cavallaro <peppe.cavallaro@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics Generic picoPHY driver for STiH407");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-stih41x-usb.c b/drivers/phy/phy-stih41x-usb.c
new file mode 100644 (file)
index 0000000..9f16cb8
--- /dev/null
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics
+ *
+ * STMicroelectronics PHY driver for STiH41x USB.
+ *
+ * Author: Maxime Coquelin <maxime.coquelin@st.com>
+ *
+ * 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/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+#define SYSCFG332  0x80
+#define SYSCFG2520 0x820
+
+/**
+ * struct stih41x_usb_cfg - SoC specific PHY register mapping
+ * @syscfg: Offset in syscfg registers bank
+ * @cfg_mask: Bits mask for PHY configuration
+ * @cfg: Static configuration value for PHY
+ * @oscok: Notify the PHY oscillator clock is ready
+ *        Setting this bit enable the PHY
+ */
+struct stih41x_usb_cfg {
+       u32 syscfg;
+       u32 cfg_mask;
+       u32 cfg;
+       u32 oscok;
+};
+
+/**
+ * struct stih41x_usb_phy - Private data for the PHY
+ * @dev: device for this controller
+ * @regmap: Syscfg registers bank in which PHY is configured
+ * @cfg: SoC specific PHY register mapping
+ * @clk: Oscillator used by the PHY
+ */
+struct stih41x_usb_phy {
+       struct device *dev;
+       struct regmap *regmap;
+       const struct stih41x_usb_cfg *cfg;
+       struct clk *clk;
+};
+
+static struct stih41x_usb_cfg stih415_usb_phy_cfg = {
+       .syscfg = SYSCFG332,
+       .cfg_mask = 0x3f,
+       .cfg = 0x38,
+       .oscok = BIT(6),
+};
+
+static struct stih41x_usb_cfg stih416_usb_phy_cfg = {
+       .syscfg = SYSCFG2520,
+       .cfg_mask = 0x33f,
+       .cfg = 0x238,
+       .oscok = BIT(6),
+};
+
+static int stih41x_usb_phy_init(struct phy *phy)
+{
+       struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy);
+
+       return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
+                          phy_dev->cfg->cfg_mask, phy_dev->cfg->cfg);
+}
+
+static int stih41x_usb_phy_power_on(struct phy *phy)
+{
+       struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy);
+       int ret;
+
+       ret = clk_prepare_enable(phy_dev->clk);
+       if (ret) {
+               dev_err(phy_dev->dev, "Failed to enable osc_phy clock\n");
+               return ret;
+       }
+
+       return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
+                       phy_dev->cfg->oscok, phy_dev->cfg->oscok);
+}
+
+static int stih41x_usb_phy_power_off(struct phy *phy)
+{
+       struct stih41x_usb_phy *phy_dev = phy_get_drvdata(phy);
+       int ret;
+
+       ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
+                       phy_dev->cfg->oscok, 0);
+       if (ret) {
+               dev_err(phy_dev->dev, "Failed to clear oscok bit\n");
+               return ret;
+       }
+
+       clk_disable_unprepare(phy_dev->clk);
+
+       return 0;
+}
+
+static struct phy_ops stih41x_usb_phy_ops = {
+       .init           = stih41x_usb_phy_init,
+       .power_on       = stih41x_usb_phy_power_on,
+       .power_off      = stih41x_usb_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static const struct of_device_id stih41x_usb_phy_of_match[];
+
+static int stih41x_usb_phy_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       const struct of_device_id *match;
+       struct stih41x_usb_phy *phy_dev;
+       struct device *dev = &pdev->dev;
+       struct phy_provider *phy_provider;
+       struct phy *phy;
+
+       phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
+       if (!phy_dev)
+               return -ENOMEM;
+
+       match = of_match_device(stih41x_usb_phy_of_match, &pdev->dev);
+       if (!match)
+               return -ENODEV;
+
+       phy_dev->cfg = match->data;
+
+       phy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+       if (IS_ERR(phy_dev->regmap)) {
+               dev_err(dev, "No syscfg phandle specified\n");
+               return PTR_ERR(phy_dev->regmap);
+       }
+
+       phy_dev->clk = devm_clk_get(dev, "osc_phy");
+       if (IS_ERR(phy_dev->clk)) {
+               dev_err(dev, "osc_phy clk not found\n");
+               return PTR_ERR(phy_dev->clk);
+       }
+
+       phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops, NULL);
+
+       if (IS_ERR(phy)) {
+               dev_err(dev, "failed to create phy\n");
+               return PTR_ERR(phy);
+       }
+
+       phy_dev->dev = dev;
+
+       phy_set_drvdata(phy, phy_dev);
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       return 0;
+}
+
+static const struct of_device_id stih41x_usb_phy_of_match[] = {
+       { .compatible = "st,stih415-usb-phy", .data = &stih415_usb_phy_cfg },
+       { .compatible = "st,stih416-usb-phy", .data = &stih416_usb_phy_cfg },
+       { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(of, stih41x_usb_phy_of_match);
+
+static struct platform_driver stih41x_usb_phy_driver = {
+       .probe  = stih41x_usb_phy_probe,
+       .driver = {
+               .name   = "stih41x-usb-phy",
+               .of_match_table = stih41x_usb_phy_of_match,
+       }
+};
+module_platform_driver(stih41x_usb_phy_driver);
+
+MODULE_AUTHOR("Maxime Coquelin <maxime.coquelin@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics USB PHY driver for STiH41x series");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
new file mode 100644 (file)
index 0000000..0baf5ef
--- /dev/null
@@ -0,0 +1,334 @@
+/*
+ * Allwinner sun4i USB phy driver
+ *
+ * Copyright (C) 2014 Hans de Goede <hdegoede@redhat.com>
+ *
+ * Based on code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * Modelled after: Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY driver
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ * Author: Sylwester Nawrocki <s.nawrocki@samsung.com>
+ *
+ * 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/err.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+
+#define REG_ISCR                       0x00
+#define REG_PHYCTL                     0x04
+#define REG_PHYBIST                    0x08
+#define REG_PHYTUNE                    0x0c
+
+#define PHYCTL_DATA                    BIT(7)
+
+#define SUNXI_AHB_ICHR8_EN             BIT(10)
+#define SUNXI_AHB_INCR4_BURST_EN       BIT(9)
+#define SUNXI_AHB_INCRX_ALIGN_EN       BIT(8)
+#define SUNXI_ULPI_BYPASS_EN           BIT(0)
+
+/* Common Control Bits for Both PHYs */
+#define PHY_PLL_BW                     0x03
+#define PHY_RES45_CAL_EN               0x0c
+
+/* Private Control Bits for Each PHY */
+#define PHY_TX_AMPLITUDE_TUNE          0x20
+#define PHY_TX_SLEWRATE_TUNE           0x22
+#define PHY_VBUSVALID_TH_SEL           0x25
+#define PHY_PULLUP_RES_SEL             0x27
+#define PHY_OTG_FUNC_EN                        0x28
+#define PHY_VBUS_DET_EN                        0x29
+#define PHY_DISCON_TH_SEL              0x2a
+
+#define MAX_PHYS                       3
+
+struct sun4i_usb_phy_data {
+       void __iomem *base;
+       struct mutex mutex;
+       int num_phys;
+       u32 disc_thresh;
+       struct sun4i_usb_phy {
+               struct phy *phy;
+               void __iomem *pmu;
+               struct regulator *vbus;
+               struct reset_control *reset;
+               struct clk *clk;
+               int index;
+       } phys[MAX_PHYS];
+};
+
+#define to_sun4i_usb_phy_data(phy) \
+       container_of((phy), struct sun4i_usb_phy_data, phys[(phy)->index])
+
+static void sun4i_usb_phy_write(struct sun4i_usb_phy *phy, u32 addr, u32 data,
+                               int len)
+{
+       struct sun4i_usb_phy_data *phy_data = to_sun4i_usb_phy_data(phy);
+       u32 temp, usbc_bit = BIT(phy->index * 2);
+       int i;
+
+       mutex_lock(&phy_data->mutex);
+
+       for (i = 0; i < len; i++) {
+               temp = readl(phy_data->base + REG_PHYCTL);
+
+               /* clear the address portion */
+               temp &= ~(0xff << 8);
+
+               /* set the address */
+               temp |= ((addr + i) << 8);
+               writel(temp, phy_data->base + REG_PHYCTL);
+
+               /* set the data bit and clear usbc bit*/
+               temp = readb(phy_data->base + REG_PHYCTL);
+               if (data & 0x1)
+                       temp |= PHYCTL_DATA;
+               else
+                       temp &= ~PHYCTL_DATA;
+               temp &= ~usbc_bit;
+               writeb(temp, phy_data->base + REG_PHYCTL);
+
+               /* pulse usbc_bit */
+               temp = readb(phy_data->base + REG_PHYCTL);
+               temp |= usbc_bit;
+               writeb(temp, phy_data->base + REG_PHYCTL);
+
+               temp = readb(phy_data->base + REG_PHYCTL);
+               temp &= ~usbc_bit;
+               writeb(temp, phy_data->base + REG_PHYCTL);
+
+               data >>= 1;
+       }
+       mutex_unlock(&phy_data->mutex);
+}
+
+static void sun4i_usb_phy_passby(struct sun4i_usb_phy *phy, int enable)
+{
+       u32 bits, reg_value;
+
+       if (!phy->pmu)
+               return;
+
+       bits = SUNXI_AHB_ICHR8_EN | SUNXI_AHB_INCR4_BURST_EN |
+               SUNXI_AHB_INCRX_ALIGN_EN | SUNXI_ULPI_BYPASS_EN;
+
+       reg_value = readl(phy->pmu);
+
+       if (enable)
+               reg_value |= bits;
+       else
+               reg_value &= ~bits;
+
+       writel(reg_value, phy->pmu);
+}
+
+static int sun4i_usb_phy_init(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+       int ret;
+
+       ret = clk_prepare_enable(phy->clk);
+       if (ret)
+               return ret;
+
+       ret = reset_control_deassert(phy->reset);
+       if (ret) {
+               clk_disable_unprepare(phy->clk);
+               return ret;
+       }
+
+       /* Adjust PHY's magnitude and rate */
+       sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
+
+       /* Disconnect threshold adjustment */
+       sun4i_usb_phy_write(phy, PHY_DISCON_TH_SEL, data->disc_thresh, 2);
+
+       sun4i_usb_phy_passby(phy, 1);
+
+       return 0;
+}
+
+static int sun4i_usb_phy_exit(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+
+       sun4i_usb_phy_passby(phy, 0);
+       reset_control_assert(phy->reset);
+       clk_disable_unprepare(phy->clk);
+
+       return 0;
+}
+
+static int sun4i_usb_phy_power_on(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+       int ret = 0;
+
+       if (phy->vbus)
+               ret = regulator_enable(phy->vbus);
+
+       return ret;
+}
+
+static int sun4i_usb_phy_power_off(struct phy *_phy)
+{
+       struct sun4i_usb_phy *phy = phy_get_drvdata(_phy);
+
+       if (phy->vbus)
+               regulator_disable(phy->vbus);
+
+       return 0;
+}
+
+static struct phy_ops sun4i_usb_phy_ops = {
+       .init           = sun4i_usb_phy_init,
+       .exit           = sun4i_usb_phy_exit,
+       .power_on       = sun4i_usb_phy_power_on,
+       .power_off      = sun4i_usb_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *sun4i_usb_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
+
+       if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
+               return ERR_PTR(-ENODEV);
+
+       return data->phys[args->args[0]].phy;
+}
+
+static int sun4i_usb_phy_probe(struct platform_device *pdev)
+{
+       struct sun4i_usb_phy_data *data;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct phy_provider *phy_provider;
+       bool dedicated_clocks;
+       struct resource *res;
+       int i;
+
+       data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       mutex_init(&data->mutex);
+
+       if (of_device_is_compatible(np, "allwinner,sun5i-a13-usb-phy"))
+               data->num_phys = 2;
+       else
+               data->num_phys = 3;
+
+       if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
+               data->disc_thresh = 3;
+       else
+               data->disc_thresh = 2;
+
+       if (of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy"))
+               dedicated_clocks = true;
+       else
+               dedicated_clocks = false;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_ctrl");
+       data->base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(data->base))
+               return PTR_ERR(data->base);
+
+       /* Skip 0, 0 is the phy for otg which is not yet supported. */
+       for (i = 1; i < data->num_phys; i++) {
+               struct sun4i_usb_phy *phy = data->phys + i;
+               char name[16];
+
+               snprintf(name, sizeof(name), "usb%d_vbus", i);
+               phy->vbus = devm_regulator_get_optional(dev, name);
+               if (IS_ERR(phy->vbus)) {
+                       if (PTR_ERR(phy->vbus) == -EPROBE_DEFER)
+                               return -EPROBE_DEFER;
+                       phy->vbus = NULL;
+               }
+
+               if (dedicated_clocks)
+                       snprintf(name, sizeof(name), "usb%d_phy", i);
+               else
+                       strlcpy(name, "usb_phy", sizeof(name));
+
+               phy->clk = devm_clk_get(dev, name);
+               if (IS_ERR(phy->clk)) {
+                       dev_err(dev, "failed to get clock %s\n", name);
+                       return PTR_ERR(phy->clk);
+               }
+
+               snprintf(name, sizeof(name), "usb%d_reset", i);
+               phy->reset = devm_reset_control_get(dev, name);
+               if (IS_ERR(phy->reset)) {
+                       dev_err(dev, "failed to get reset %s\n", name);
+                       return PTR_ERR(phy->reset);
+               }
+
+               if (i) { /* No pmu for usbc0 */
+                       snprintf(name, sizeof(name), "pmu%d", i);
+                       res = platform_get_resource_byname(pdev,
+                                                       IORESOURCE_MEM, name);
+                       phy->pmu = devm_ioremap_resource(dev, res);
+                       if (IS_ERR(phy->pmu))
+                               return PTR_ERR(phy->pmu);
+               }
+
+               phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL);
+               if (IS_ERR(phy->phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", i);
+                       return PTR_ERR(phy->phy);
+               }
+
+               phy->index = i;
+               phy_set_drvdata(phy->phy, &data->phys[i]);
+       }
+
+       dev_set_drvdata(dev, data);
+       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id sun4i_usb_phy_of_match[] = {
+       { .compatible = "allwinner,sun4i-a10-usb-phy" },
+       { .compatible = "allwinner,sun5i-a13-usb-phy" },
+       { .compatible = "allwinner,sun6i-a31-usb-phy" },
+       { .compatible = "allwinner,sun7i-a20-usb-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sun4i_usb_phy_of_match);
+
+static struct platform_driver sun4i_usb_phy_driver = {
+       .probe  = sun4i_usb_phy_probe,
+       .driver = {
+               .of_match_table = sun4i_usb_phy_of_match,
+               .name  = "sun4i-usb-phy",
+       }
+};
+module_platform_driver(sun4i_usb_phy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun4i USB phy driver");
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
new file mode 100644 (file)
index 0000000..ab1e22d
--- /dev/null
@@ -0,0 +1,531 @@
+/*
+ * phy-ti-pipe3 - PIPE3 PHY driver.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/phy/phy.h>
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/phy/omap_control_phy.h>
+#include <linux/of_platform.h>
+
+#define        PLL_STATUS              0x00000004
+#define        PLL_GO                  0x00000008
+#define        PLL_CONFIGURATION1      0x0000000C
+#define        PLL_CONFIGURATION2      0x00000010
+#define        PLL_CONFIGURATION3      0x00000014
+#define        PLL_CONFIGURATION4      0x00000020
+
+#define        PLL_REGM_MASK           0x001FFE00
+#define        PLL_REGM_SHIFT          0x9
+#define        PLL_REGM_F_MASK         0x0003FFFF
+#define        PLL_REGM_F_SHIFT        0x0
+#define        PLL_REGN_MASK           0x000001FE
+#define        PLL_REGN_SHIFT          0x1
+#define        PLL_SELFREQDCO_MASK     0x0000000E
+#define        PLL_SELFREQDCO_SHIFT    0x1
+#define        PLL_SD_MASK             0x0003FC00
+#define        PLL_SD_SHIFT            10
+#define        SET_PLL_GO              0x1
+#define PLL_LDOPWDN            BIT(15)
+#define PLL_TICOPWDN           BIT(16)
+#define        PLL_LOCK                0x2
+#define        PLL_IDLE                0x1
+
+/*
+ * This is an Empirical value that works, need to confirm the actual
+ * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
+ * to be correctly reflected in the PIPE3PHY_PLL_STATUS register.
+ */
+#define PLL_IDLE_TIME  100     /* in milliseconds */
+#define PLL_LOCK_TIME  100     /* in milliseconds */
+
+struct pipe3_dpll_params {
+       u16     m;
+       u8      n;
+       u8      freq:3;
+       u8      sd;
+       u32     mf;
+};
+
+struct pipe3_dpll_map {
+       unsigned long rate;
+       struct pipe3_dpll_params params;
+};
+
+struct ti_pipe3 {
+       void __iomem            *pll_ctrl_base;
+       struct device           *dev;
+       struct device           *control_dev;
+       struct clk              *wkupclk;
+       struct clk              *sys_clk;
+       struct clk              *refclk;
+       struct clk              *div_clk;
+       struct pipe3_dpll_map   *dpll_map;
+       u8                      id;
+};
+
+static struct pipe3_dpll_map dpll_map_usb[] = {
+       {12000000, {1250, 5, 4, 20, 0} },       /* 12 MHz */
+       {16800000, {3125, 20, 4, 20, 0} },      /* 16.8 MHz */
+       {19200000, {1172, 8, 4, 20, 65537} },   /* 19.2 MHz */
+       {20000000, {1000, 7, 4, 10, 0} },       /* 20 MHz */
+       {26000000, {1250, 12, 4, 20, 0} },      /* 26 MHz */
+       {38400000, {3125, 47, 4, 20, 92843} },  /* 38.4 MHz */
+       { },                                    /* Terminator */
+};
+
+static struct pipe3_dpll_map dpll_map_sata[] = {
+       {12000000, {1000, 7, 4, 6, 0} },        /* 12 MHz */
+       {16800000, {714, 7, 4, 6, 0} },         /* 16.8 MHz */
+       {19200000, {625, 7, 4, 6, 0} },         /* 19.2 MHz */
+       {20000000, {600, 7, 4, 6, 0} },         /* 20 MHz */
+       {26000000, {461, 7, 4, 6, 0} },         /* 26 MHz */
+       {38400000, {312, 7, 4, 6, 0} },         /* 38.4 MHz */
+       { },                                    /* Terminator */
+};
+
+static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset)
+{
+       return __raw_readl(addr + offset);
+}
+
+static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset,
+       u32 data)
+{
+       __raw_writel(data, addr + offset);
+}
+
+static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy)
+{
+       unsigned long rate;
+       struct pipe3_dpll_map *dpll_map = phy->dpll_map;
+
+       rate = clk_get_rate(phy->sys_clk);
+
+       for (; dpll_map->rate; dpll_map++) {
+               if (rate == dpll_map->rate)
+                       return &dpll_map->params;
+       }
+
+       dev_err(phy->dev, "No DPLL configuration for %lu Hz SYS CLK\n", rate);
+
+       return NULL;
+}
+
+static int ti_pipe3_power_off(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+
+       omap_control_phy_power(phy->control_dev, 0);
+
+       return 0;
+}
+
+static int ti_pipe3_power_on(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+
+       omap_control_phy_power(phy->control_dev, 1);
+
+       return 0;
+}
+
+static int ti_pipe3_dpll_wait_lock(struct ti_pipe3 *phy)
+{
+       u32             val;
+       unsigned long   timeout;
+
+       timeout = jiffies + msecs_to_jiffies(PLL_LOCK_TIME);
+       do {
+               cpu_relax();
+               val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
+               if (val & PLL_LOCK)
+                       break;
+       } while (!time_after(jiffies, timeout));
+
+       if (!(val & PLL_LOCK)) {
+               dev_err(phy->dev, "DPLL failed to lock\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
+static int ti_pipe3_dpll_program(struct ti_pipe3 *phy)
+{
+       u32                     val;
+       struct pipe3_dpll_params *dpll_params;
+
+       dpll_params = ti_pipe3_get_dpll_params(phy);
+       if (!dpll_params)
+               return -EINVAL;
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
+       val &= ~PLL_REGN_MASK;
+       val |= dpll_params->n << PLL_REGN_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+       val &= ~PLL_SELFREQDCO_MASK;
+       val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
+       val &= ~PLL_REGM_MASK;
+       val |= dpll_params->m << PLL_REGM_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
+       val &= ~PLL_REGM_F_MASK;
+       val |= dpll_params->mf << PLL_REGM_F_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
+
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
+       val &= ~PLL_SD_MASK;
+       val |= dpll_params->sd << PLL_SD_SHIFT;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
+
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
+
+       return ti_pipe3_dpll_wait_lock(phy);
+}
+
+static int ti_pipe3_init(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+       u32 val;
+       int ret = 0;
+
+       if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
+               omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1);
+               return 0;
+       }
+
+       /* Bring it out of IDLE if it is IDLE */
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+       if (val & PLL_IDLE) {
+               val &= ~PLL_IDLE;
+               ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+               ret = ti_pipe3_dpll_wait_lock(phy);
+       }
+
+       /* Program the DPLL only if not locked */
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
+       if (!(val & PLL_LOCK))
+               if (ti_pipe3_dpll_program(phy))
+                       return -EINVAL;
+
+       return ret;
+}
+
+static int ti_pipe3_exit(struct phy *x)
+{
+       struct ti_pipe3 *phy = phy_get_drvdata(x);
+       u32 val;
+       unsigned long timeout;
+
+       /* SATA DPLL can't be powered down due to Errata i783 and PCIe
+        * does not have internal DPLL
+        */
+       if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") ||
+           of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie"))
+               return 0;
+
+       /* Put DPLL in IDLE mode */
+       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+       val |= PLL_IDLE;
+       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+
+       /* wait for LDO and Oscillator to power down */
+       timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME);
+       do {
+               cpu_relax();
+               val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
+               if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
+                       break;
+       } while (!time_after(jiffies, timeout));
+
+       if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
+               dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n",
+                       val);
+               return -EBUSY;
+       }
+
+       return 0;
+}
+static struct phy_ops ops = {
+       .init           = ti_pipe3_init,
+       .exit           = ti_pipe3_exit,
+       .power_on       = ti_pipe3_power_on,
+       .power_off      = ti_pipe3_power_off,
+       .owner          = THIS_MODULE,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id ti_pipe3_id_table[];
+#endif
+
+static int ti_pipe3_probe(struct platform_device *pdev)
+{
+       struct ti_pipe3 *phy;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       struct resource *res;
+       struct device_node *node = pdev->dev.of_node;
+       struct device_node *control_node;
+       struct platform_device *control_pdev;
+       const struct of_device_id *match;
+       struct clk *clk;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       phy->dev                = &pdev->dev;
+
+       if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
+               match = of_match_device(of_match_ptr(ti_pipe3_id_table),
+                                       &pdev->dev);
+               if (!match)
+                       return -EINVAL;
+
+               phy->dpll_map = (struct pipe3_dpll_map *)match->data;
+               if (!phy->dpll_map) {
+                       dev_err(&pdev->dev, "no DPLL data\n");
+                       return -EINVAL;
+               }
+
+               res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                                  "pll_ctrl");
+               phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
+               if (IS_ERR(phy->pll_ctrl_base))
+                       return PTR_ERR(phy->pll_ctrl_base);
+
+               phy->sys_clk = devm_clk_get(phy->dev, "sysclk");
+               if (IS_ERR(phy->sys_clk)) {
+                       dev_err(&pdev->dev, "unable to get sysclk\n");
+                       return -EINVAL;
+               }
+       }
+
+       if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
+               phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
+               if (IS_ERR(phy->wkupclk)) {
+                       dev_err(&pdev->dev, "unable to get wkupclk\n");
+                       return PTR_ERR(phy->wkupclk);
+               }
+
+               phy->refclk = devm_clk_get(phy->dev, "refclk");
+               if (IS_ERR(phy->refclk)) {
+                       dev_err(&pdev->dev, "unable to get refclk\n");
+                       return PTR_ERR(phy->refclk);
+               }
+       } else {
+               phy->wkupclk = ERR_PTR(-ENODEV);
+               phy->refclk = ERR_PTR(-ENODEV);
+       }
+
+       if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
+               if (of_property_read_u8(node, "id", &phy->id) < 0)
+                       phy->id = 1;
+
+               clk = devm_clk_get(phy->dev, "dpll_ref");
+               if (IS_ERR(clk)) {
+                       dev_err(&pdev->dev, "unable to get dpll ref clk\n");
+                       return PTR_ERR(clk);
+               }
+               clk_set_rate(clk, 1500000000);
+
+               clk = devm_clk_get(phy->dev, "dpll_ref_m2");
+               if (IS_ERR(clk)) {
+                       dev_err(&pdev->dev, "unable to get dpll ref m2 clk\n");
+                       return PTR_ERR(clk);
+               }
+               clk_set_rate(clk, 100000000);
+
+               clk = devm_clk_get(phy->dev, "phy-div");
+               if (IS_ERR(clk)) {
+                       dev_err(&pdev->dev, "unable to get phy-div clk\n");
+                       return PTR_ERR(clk);
+               }
+               clk_set_rate(clk, 100000000);
+
+               phy->div_clk = devm_clk_get(phy->dev, "div-clk");
+               if (IS_ERR(phy->div_clk)) {
+                       dev_err(&pdev->dev, "unable to get div-clk\n");
+                       return PTR_ERR(phy->div_clk);
+               }
+       } else {
+               phy->div_clk = ERR_PTR(-ENODEV);
+       }
+
+       control_node = of_parse_phandle(node, "ctrl-module", 0);
+       if (!control_node) {
+               dev_err(&pdev->dev, "Failed to get control device phandle\n");
+               return -EINVAL;
+       }
+
+       control_pdev = of_find_device_by_node(control_node);
+       if (!control_pdev) {
+               dev_err(&pdev->dev, "Failed to get control device\n");
+               return -EINVAL;
+       }
+
+       phy->control_dev = &control_pdev->dev;
+
+       omap_control_phy_power(phy->control_dev, 0);
+
+       platform_set_drvdata(pdev, phy);
+       pm_runtime_enable(phy->dev);
+
+       generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
+       if (IS_ERR(generic_phy))
+               return PTR_ERR(generic_phy);
+
+       phy_set_drvdata(generic_phy, phy);
+       phy_provider = devm_of_phy_provider_register(phy->dev,
+                       of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       pm_runtime_get(&pdev->dev);
+
+       return 0;
+}
+
+static int ti_pipe3_remove(struct platform_device *pdev)
+{
+       if (!pm_runtime_suspended(&pdev->dev))
+               pm_runtime_put(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_RUNTIME
+
+static int ti_pipe3_runtime_suspend(struct device *dev)
+{
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+       if (!IS_ERR(phy->wkupclk))
+               clk_disable_unprepare(phy->wkupclk);
+       if (!IS_ERR(phy->refclk))
+               clk_disable_unprepare(phy->refclk);
+       if (!IS_ERR(phy->div_clk))
+               clk_disable_unprepare(phy->div_clk);
+
+       return 0;
+}
+
+static int ti_pipe3_runtime_resume(struct device *dev)
+{
+       u32 ret = 0;
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+       if (!IS_ERR(phy->refclk)) {
+               ret = clk_prepare_enable(phy->refclk);
+               if (ret) {
+                       dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
+                       goto err1;
+               }
+       }
+
+       if (!IS_ERR(phy->wkupclk)) {
+               ret = clk_prepare_enable(phy->wkupclk);
+               if (ret) {
+                       dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
+                       goto err2;
+               }
+       }
+
+       if (!IS_ERR(phy->div_clk)) {
+               ret = clk_prepare_enable(phy->div_clk);
+               if (ret) {
+                       dev_err(phy->dev, "Failed to enable div_clk %d\n", ret);
+                       goto err3;
+               }
+       }
+       return 0;
+
+err3:
+       if (!IS_ERR(phy->wkupclk))
+               clk_disable_unprepare(phy->wkupclk);
+
+err2:
+       if (!IS_ERR(phy->refclk))
+               clk_disable_unprepare(phy->refclk);
+
+err1:
+       return ret;
+}
+
+static const struct dev_pm_ops ti_pipe3_pm_ops = {
+       SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
+                          ti_pipe3_runtime_resume, NULL)
+};
+
+#define DEV_PM_OPS     (&ti_pipe3_pm_ops)
+#else
+#define DEV_PM_OPS     NULL
+#endif
+
+#ifdef CONFIG_OF
+static const struct of_device_id ti_pipe3_id_table[] = {
+       {
+               .compatible = "ti,phy-usb3",
+               .data = dpll_map_usb,
+       },
+       {
+               .compatible = "ti,omap-usb3",
+               .data = dpll_map_usb,
+       },
+       {
+               .compatible = "ti,phy-pipe3-sata",
+               .data = dpll_map_sata,
+       },
+       {
+               .compatible = "ti,phy-pipe3-pcie",
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
+#endif
+
+static struct platform_driver ti_pipe3_driver = {
+       .probe          = ti_pipe3_probe,
+       .remove         = ti_pipe3_remove,
+       .driver         = {
+               .name   = "ti-pipe3",
+               .pm     = DEV_PM_OPS,
+               .of_match_table = of_match_ptr(ti_pipe3_id_table),
+       },
+};
+
+module_platform_driver(ti_pipe3_driver);
+
+MODULE_ALIAS("platform: ti_pipe3");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("TI PIPE3 phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-twl4030-usb.c b/drivers/phy/phy-twl4030-usb.c
new file mode 100644 (file)
index 0000000..e2698d2
--- /dev/null
@@ -0,0 +1,807 @@
+/*
+ * twl4030_usb - TWL4030 USB transceiver, talking to OMAP OTG controller
+ *
+ * Copyright (C) 2004-2007 Texas Instruments
+ * Copyright (C) 2008 Nokia Corporation
+ * Contact: Felipe Balbi <felipe.balbi@nokia.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Current status:
+ *     - HS USB ULPI mode works.
+ *     - 3-pin mode support may be added in future.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/workqueue.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/musb-omap.h>
+#include <linux/usb/ulpi.h>
+#include <linux/i2c/twl.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+
+/* Register defines */
+
+#define MCPC_CTRL                      0x30
+#define MCPC_CTRL_RTSOL                        (1 << 7)
+#define MCPC_CTRL_EXTSWR               (1 << 6)
+#define MCPC_CTRL_EXTSWC               (1 << 5)
+#define MCPC_CTRL_VOICESW              (1 << 4)
+#define MCPC_CTRL_OUT64K               (1 << 3)
+#define MCPC_CTRL_RTSCTSSW             (1 << 2)
+#define MCPC_CTRL_HS_UART              (1 << 0)
+
+#define MCPC_IO_CTRL                   0x33
+#define MCPC_IO_CTRL_MICBIASEN         (1 << 5)
+#define MCPC_IO_CTRL_CTS_NPU           (1 << 4)
+#define MCPC_IO_CTRL_RXD_PU            (1 << 3)
+#define MCPC_IO_CTRL_TXDTYP            (1 << 2)
+#define MCPC_IO_CTRL_CTSTYP            (1 << 1)
+#define MCPC_IO_CTRL_RTSTYP            (1 << 0)
+
+#define MCPC_CTRL2                     0x36
+#define MCPC_CTRL2_MCPC_CK_EN          (1 << 0)
+
+#define OTHER_FUNC_CTRL                        0x80
+#define OTHER_FUNC_CTRL_BDIS_ACON_EN   (1 << 4)
+#define OTHER_FUNC_CTRL_FIVEWIRE_MODE  (1 << 2)
+
+#define OTHER_IFC_CTRL                 0x83
+#define OTHER_IFC_CTRL_OE_INT_EN       (1 << 6)
+#define OTHER_IFC_CTRL_CEA2011_MODE    (1 << 5)
+#define OTHER_IFC_CTRL_FSLSSERIALMODE_4PIN     (1 << 4)
+#define OTHER_IFC_CTRL_HIZ_ULPI_60MHZ_OUT      (1 << 3)
+#define OTHER_IFC_CTRL_HIZ_ULPI                (1 << 2)
+#define OTHER_IFC_CTRL_ALT_INT_REROUTE (1 << 0)
+
+#define OTHER_INT_EN_RISE              0x86
+#define OTHER_INT_EN_FALL              0x89
+#define OTHER_INT_STS                  0x8C
+#define OTHER_INT_LATCH                        0x8D
+#define OTHER_INT_VB_SESS_VLD          (1 << 7)
+#define OTHER_INT_DM_HI                        (1 << 6) /* not valid for "latch" reg */
+#define OTHER_INT_DP_HI                        (1 << 5) /* not valid for "latch" reg */
+#define OTHER_INT_BDIS_ACON            (1 << 3) /* not valid for "fall" regs */
+#define OTHER_INT_MANU                 (1 << 1)
+#define OTHER_INT_ABNORMAL_STRESS      (1 << 0)
+
+#define ID_STATUS                      0x96
+#define ID_RES_FLOAT                   (1 << 4)
+#define ID_RES_440K                    (1 << 3)
+#define ID_RES_200K                    (1 << 2)
+#define ID_RES_102K                    (1 << 1)
+#define ID_RES_GND                     (1 << 0)
+
+#define POWER_CTRL                     0xAC
+#define POWER_CTRL_OTG_ENAB            (1 << 5)
+
+#define OTHER_IFC_CTRL2                        0xAF
+#define OTHER_IFC_CTRL2_ULPI_STP_LOW   (1 << 4)
+#define OTHER_IFC_CTRL2_ULPI_TXEN_POL  (1 << 3)
+#define OTHER_IFC_CTRL2_ULPI_4PIN_2430 (1 << 2)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_MASK    (3 << 0) /* bits 0 and 1 */
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT1N   (0 << 0)
+#define OTHER_IFC_CTRL2_USB_INT_OUTSEL_INT2N   (1 << 0)
+
+#define REG_CTRL_EN                    0xB2
+#define REG_CTRL_ERROR                 0xB5
+#define ULPI_I2C_CONFLICT_INTEN                (1 << 0)
+
+#define OTHER_FUNC_CTRL2               0xB8
+#define OTHER_FUNC_CTRL2_VBAT_TIMER_EN (1 << 0)
+
+/* following registers do not have separate _clr and _set registers */
+#define VBUS_DEBOUNCE                  0xC0
+#define ID_DEBOUNCE                    0xC1
+#define VBAT_TIMER                     0xD3
+#define PHY_PWR_CTRL                   0xFD
+#define PHY_PWR_PHYPWD                 (1 << 0)
+#define PHY_CLK_CTRL                   0xFE
+#define PHY_CLK_CTRL_CLOCKGATING_EN    (1 << 2)
+#define PHY_CLK_CTRL_CLK32K_EN         (1 << 1)
+#define REQ_PHY_DPLL_CLK               (1 << 0)
+#define PHY_CLK_CTRL_STS               0xFF
+#define PHY_DPLL_CLK                   (1 << 0)
+
+/* In module TWL_MODULE_PM_MASTER */
+#define STS_HW_CONDITIONS              0x0F
+
+/* In module TWL_MODULE_PM_RECEIVER */
+#define VUSB_DEDICATED1                        0x7D
+#define VUSB_DEDICATED2                        0x7E
+#define VUSB1V5_DEV_GRP                        0x71
+#define VUSB1V5_TYPE                   0x72
+#define VUSB1V5_REMAP                  0x73
+#define VUSB1V8_DEV_GRP                        0x74
+#define VUSB1V8_TYPE                   0x75
+#define VUSB1V8_REMAP                  0x76
+#define VUSB3V1_DEV_GRP                        0x77
+#define VUSB3V1_TYPE                   0x78
+#define VUSB3V1_REMAP                  0x79
+
+/* In module TWL4030_MODULE_INTBR */
+#define PMBR1                          0x0D
+#define GPIO_USB_4PIN_ULPI_2430C       (3 << 0)
+
+struct twl4030_usb {
+       struct usb_phy          phy;
+       struct device           *dev;
+
+       /* TWL4030 internal USB regulator supplies */
+       struct regulator        *usb1v5;
+       struct regulator        *usb1v8;
+       struct regulator        *usb3v1;
+
+       /* for vbus reporting with irqs disabled */
+       struct mutex            lock;
+
+       /* pin configuration */
+       enum twl4030_usb_mode   usb_mode;
+
+       int                     irq;
+       enum omap_musb_vbus_id_status linkstat;
+       bool                    vbus_supplied;
+
+       struct delayed_work     id_workaround_work;
+};
+
+/* internal define on top of container_of */
+#define phy_to_twl(x)          container_of((x), struct twl4030_usb, phy)
+
+/*-------------------------------------------------------------------------*/
+
+static int twl4030_i2c_write_u8_verify(struct twl4030_usb *twl,
+               u8 module, u8 data, u8 address)
+{
+       u8 check;
+
+       if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+           (twl_i2c_read_u8(module, &check, address) >= 0) &&
+                                               (check == data))
+               return 0;
+       dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
+                       1, module, address, check, data);
+
+       /* Failed once: Try again */
+       if ((twl_i2c_write_u8(module, data, address) >= 0) &&
+           (twl_i2c_read_u8(module, &check, address) >= 0) &&
+                                               (check == data))
+               return 0;
+       dev_dbg(twl->dev, "Write%d[%d,0x%x] wrote %02x but read %02x\n",
+                       2, module, address, check, data);
+
+       /* Failed again: Return error */
+       return -EBUSY;
+}
+
+#define twl4030_usb_write_verify(twl, address, data)   \
+       twl4030_i2c_write_u8_verify(twl, TWL_MODULE_USB, (data), (address))
+
+static inline int twl4030_usb_write(struct twl4030_usb *twl,
+               u8 address, u8 data)
+{
+       int ret = 0;
+
+       ret = twl_i2c_write_u8(TWL_MODULE_USB, data, address);
+       if (ret < 0)
+               dev_dbg(twl->dev,
+                       "TWL4030:USB:Write[0x%x] Error %d\n", address, ret);
+       return ret;
+}
+
+static inline int twl4030_readb(struct twl4030_usb *twl, u8 module, u8 address)
+{
+       u8 data;
+       int ret = 0;
+
+       ret = twl_i2c_read_u8(module, &data, address);
+       if (ret >= 0)
+               ret = data;
+       else
+               dev_dbg(twl->dev,
+                       "TWL4030:readb[0x%x,0x%x] Error %d\n",
+                                       module, address, ret);
+
+       return ret;
+}
+
+static inline int twl4030_usb_read(struct twl4030_usb *twl, u8 address)
+{
+       return twl4030_readb(twl, TWL_MODULE_USB, address);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static inline int
+twl4030_usb_set_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+       return twl4030_usb_write(twl, ULPI_SET(reg), bits);
+}
+
+static inline int
+twl4030_usb_clear_bits(struct twl4030_usb *twl, u8 reg, u8 bits)
+{
+       return twl4030_usb_write(twl, ULPI_CLR(reg), bits);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static bool twl4030_is_driving_vbus(struct twl4030_usb *twl)
+{
+       int ret;
+
+       ret = twl4030_usb_read(twl, PHY_CLK_CTRL_STS);
+       if (ret < 0 || !(ret & PHY_DPLL_CLK))
+               /*
+                * if clocks are off, registers are not updated,
+                * but we can assume we don't drive VBUS in this case
+                */
+               return false;
+
+       ret = twl4030_usb_read(twl, ULPI_OTG_CTRL);
+       if (ret < 0)
+               return false;
+
+       return (ret & (ULPI_OTG_DRVVBUS | ULPI_OTG_CHRGVBUS)) ? true : false;
+}
+
+static enum omap_musb_vbus_id_status
+       twl4030_usb_linkstat(struct twl4030_usb *twl)
+{
+       int     status;
+       enum omap_musb_vbus_id_status linkstat = OMAP_MUSB_UNKNOWN;
+
+       twl->vbus_supplied = false;
+
+       /*
+        * For ID/VBUS sensing, see manual section 15.4.8 ...
+        * except when using only battery backup power, two
+        * comparators produce VBUS_PRES and ID_PRES signals,
+        * which don't match docs elsewhere.  But ... BIT(7)
+        * and BIT(2) of STS_HW_CONDITIONS, respectively, do
+        * seem to match up.  If either is true the USB_PRES
+        * signal is active, the OTG module is activated, and
+        * its interrupt may be raised (may wake the system).
+        */
+       status = twl4030_readb(twl, TWL_MODULE_PM_MASTER, STS_HW_CONDITIONS);
+       if (status < 0)
+               dev_err(twl->dev, "USB link status err %d\n", status);
+       else if (status & (BIT(7) | BIT(2))) {
+               if (status & BIT(7)) {
+                       if (twl4030_is_driving_vbus(twl))
+                               status &= ~BIT(7);
+                       else
+                               twl->vbus_supplied = true;
+               }
+
+               if (status & BIT(2))
+                       linkstat = OMAP_MUSB_ID_GROUND;
+               else if (status & BIT(7))
+                       linkstat = OMAP_MUSB_VBUS_VALID;
+               else
+                       linkstat = OMAP_MUSB_VBUS_OFF;
+       } else {
+               if (twl->linkstat != OMAP_MUSB_UNKNOWN)
+                       linkstat = OMAP_MUSB_VBUS_OFF;
+       }
+
+       dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
+                       status, status, linkstat);
+
+       /* REVISIT this assumes host and peripheral controllers
+        * are registered, and that both are active...
+        */
+
+       return linkstat;
+}
+
+static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
+{
+       twl->usb_mode = mode;
+
+       switch (mode) {
+       case T2_USB_MODE_ULPI:
+               twl4030_usb_clear_bits(twl, ULPI_IFC_CTRL,
+                                       ULPI_IFC_CTRL_CARKITMODE);
+               twl4030_usb_set_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+               twl4030_usb_clear_bits(twl, ULPI_FUNC_CTRL,
+                                       ULPI_FUNC_CTRL_XCVRSEL_MASK |
+                                       ULPI_FUNC_CTRL_OPMODE_MASK);
+               break;
+       case -1:
+               /* FIXME: power on defaults */
+               break;
+       default:
+               dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
+                               mode);
+               break;
+       }
+}
+
+static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
+{
+       unsigned long timeout;
+       int val = twl4030_usb_read(twl, PHY_CLK_CTRL);
+
+       if (val >= 0) {
+               if (on) {
+                       /* enable DPLL to access PHY registers over I2C */
+                       val |= REQ_PHY_DPLL_CLK;
+                       WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
+                                               (u8)val) < 0);
+
+                       timeout = jiffies + HZ;
+                       while (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
+                                                       PHY_DPLL_CLK)
+                               && time_before(jiffies, timeout))
+                                       udelay(10);
+                       if (!(twl4030_usb_read(twl, PHY_CLK_CTRL_STS) &
+                                                       PHY_DPLL_CLK))
+                               dev_err(twl->dev, "Timeout setting T2 HSUSB "
+                                               "PHY DPLL clock\n");
+               } else {
+                       /* let ULPI control the DPLL clock */
+                       val &= ~REQ_PHY_DPLL_CLK;
+                       WARN_ON(twl4030_usb_write_verify(twl, PHY_CLK_CTRL,
+                                               (u8)val) < 0);
+               }
+       }
+}
+
+static void __twl4030_phy_power(struct twl4030_usb *twl, int on)
+{
+       u8 pwr = twl4030_usb_read(twl, PHY_PWR_CTRL);
+
+       if (on)
+               pwr &= ~PHY_PWR_PHYPWD;
+       else
+               pwr |= PHY_PWR_PHYPWD;
+
+       WARN_ON(twl4030_usb_write_verify(twl, PHY_PWR_CTRL, pwr) < 0);
+}
+
+static int twl4030_usb_runtime_suspend(struct device *dev)
+{
+       struct twl4030_usb *twl = dev_get_drvdata(dev);
+
+       dev_dbg(twl->dev, "%s\n", __func__);
+       if (pm_runtime_suspended(dev))
+               return 0;
+
+       __twl4030_phy_power(twl, 0);
+       regulator_disable(twl->usb1v5);
+       regulator_disable(twl->usb1v8);
+       regulator_disable(twl->usb3v1);
+
+       return 0;
+}
+
+static int twl4030_usb_runtime_resume(struct device *dev)
+{
+       struct twl4030_usb *twl = dev_get_drvdata(dev);
+       int res;
+
+       dev_dbg(twl->dev, "%s\n", __func__);
+       if (pm_runtime_active(dev))
+               return 0;
+
+       res = regulator_enable(twl->usb3v1);
+       if (res)
+               dev_err(twl->dev, "Failed to enable usb3v1\n");
+
+       res = regulator_enable(twl->usb1v8);
+       if (res)
+               dev_err(twl->dev, "Failed to enable usb1v8\n");
+
+       /*
+        * Disabling usb3v1 regulator (= writing 0 to VUSB3V1_DEV_GRP
+        * in twl4030) resets the VUSB_DEDICATED2 register. This reset
+        * enables VUSB3V1_SLEEP bit that remaps usb3v1 ACTIVE state to
+        * SLEEP. We work around this by clearing the bit after usv3v1
+        * is re-activated. This ensures that VUSB3V1 is really active.
+        */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);
+
+       res = regulator_enable(twl->usb1v5);
+       if (res)
+               dev_err(twl->dev, "Failed to enable usb1v5\n");
+
+       __twl4030_phy_power(twl, 1);
+       twl4030_usb_write(twl, PHY_CLK_CTRL,
+                         twl4030_usb_read(twl, PHY_CLK_CTRL) |
+                         (PHY_CLK_CTRL_CLOCKGATING_EN |
+                          PHY_CLK_CTRL_CLK32K_EN));
+
+       return 0;
+}
+
+static int twl4030_phy_power_off(struct phy *phy)
+{
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
+
+       dev_dbg(twl->dev, "%s\n", __func__);
+       pm_runtime_mark_last_busy(twl->dev);
+       pm_runtime_put_autosuspend(twl->dev);
+
+       return 0;
+}
+
+static int twl4030_phy_power_on(struct phy *phy)
+{
+       struct twl4030_usb *twl = phy_get_drvdata(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);
+       schedule_delayed_work(&twl->id_workaround_work, 0);
+
+       return 0;
+}
+
+static int twl4030_usb_ldo_init(struct twl4030_usb *twl)
+{
+       /* Enable writing to power configuration registers */
+       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG1,
+                        TWL4030_PM_MASTER_PROTECT_KEY);
+
+       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, TWL4030_PM_MASTER_KEY_CFG2,
+                        TWL4030_PM_MASTER_PROTECT_KEY);
+
+       /* Keep VUSB3V1 LDO in sleep state until VBUS/ID change detected*/
+       /*twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB_DEDICATED2);*/
+
+       /* input to VUSB3V1 LDO is from VBAT, not VBUS */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0x14, VUSB_DEDICATED1);
+
+       /* Initialize 3.1V regulator */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_DEV_GRP);
+
+       twl->usb3v1 = devm_regulator_get(twl->dev, "usb3v1");
+       if (IS_ERR(twl->usb3v1))
+               return -ENODEV;
+
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB3V1_TYPE);
+
+       /* Initialize 1.5V regulator */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_DEV_GRP);
+
+       twl->usb1v5 = devm_regulator_get(twl->dev, "usb1v5");
+       if (IS_ERR(twl->usb1v5))
+               return -ENODEV;
+
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V5_TYPE);
+
+       /* Initialize 1.8V regulator */
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_DEV_GRP);
+
+       twl->usb1v8 = devm_regulator_get(twl->dev, "usb1v8");
+       if (IS_ERR(twl->usb1v8))
+               return -ENODEV;
+
+       twl_i2c_write_u8(TWL_MODULE_PM_RECEIVER, 0, VUSB1V8_TYPE);
+
+       /* disable access to power configuration registers */
+       twl_i2c_write_u8(TWL_MODULE_PM_MASTER, 0,
+                        TWL4030_PM_MASTER_PROTECT_KEY);
+
+       return 0;
+}
+
+static ssize_t twl4030_usb_vbus_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct twl4030_usb *twl = dev_get_drvdata(dev);
+       int ret = -EINVAL;
+
+       mutex_lock(&twl->lock);
+       ret = sprintf(buf, "%s\n",
+                       twl->vbus_supplied ? "on" : "off");
+       mutex_unlock(&twl->lock);
+
+       return ret;
+}
+static DEVICE_ATTR(vbus, 0444, twl4030_usb_vbus_show, NULL);
+
+static irqreturn_t twl4030_usb_irq(int irq, void *_twl)
+{
+       struct twl4030_usb *twl = _twl;
+       enum omap_musb_vbus_id_status status;
+       bool status_changed = false;
+
+       status = twl4030_usb_linkstat(twl);
+
+       mutex_lock(&twl->lock);
+       if (status >= 0 && status != twl->linkstat) {
+               twl->linkstat = status;
+               status_changed = true;
+       }
+       mutex_unlock(&twl->lock);
+
+       if (status_changed) {
+               /* FIXME add a set_power() method so that B-devices can
+                * configure the charger appropriately.  It's not always
+                * correct to consume VBUS power, and how much current to
+                * consume is a function of the USB configuration chosen
+                * by the host.
+                *
+                * REVISIT usb_gadget_vbus_connect(...) as needed, ditto
+                * its disconnect() sibling, when changing to/from the
+                * USB_LINK_VBUS state.  musb_hdrc won't care until it
+                * starts to handle softconnect right.
+                */
+               if ((status == OMAP_MUSB_VBUS_VALID) ||
+                   (status == OMAP_MUSB_ID_GROUND)) {
+                       if (pm_runtime_suspended(twl->dev))
+                               pm_runtime_get_sync(twl->dev);
+               } else {
+                       if (pm_runtime_active(twl->dev)) {
+                               pm_runtime_mark_last_busy(twl->dev);
+                               pm_runtime_put_autosuspend(twl->dev);
+                       }
+               }
+               omap_musb_mailbox(status);
+       }
+
+       /* don't schedule during sleep - irq works right then */
+       if (status == OMAP_MUSB_ID_GROUND && pm_runtime_active(twl->dev)) {
+               cancel_delayed_work(&twl->id_workaround_work);
+               schedule_delayed_work(&twl->id_workaround_work, HZ);
+       }
+
+       if (irq)
+               sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+
+       return IRQ_HANDLED;
+}
+
+static void twl4030_id_workaround_work(struct work_struct *work)
+{
+       struct twl4030_usb *twl = container_of(work, struct twl4030_usb,
+               id_workaround_work.work);
+
+       twl4030_usb_irq(0, twl);
+}
+
+static int twl4030_phy_init(struct phy *phy)
+{
+       struct twl4030_usb *twl = phy_get_drvdata(phy);
+
+       pm_runtime_get_sync(twl->dev);
+       schedule_delayed_work(&twl->id_workaround_work, 0);
+       pm_runtime_mark_last_busy(twl->dev);
+       pm_runtime_put_autosuspend(twl->dev);
+
+       return 0;
+}
+
+static int twl4030_set_peripheral(struct usb_otg *otg,
+                                       struct usb_gadget *gadget)
+{
+       if (!otg)
+               return -ENODEV;
+
+       otg->gadget = gadget;
+       if (!gadget)
+               otg->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int twl4030_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+       if (!otg)
+               return -ENODEV;
+
+       otg->host = host;
+       if (!host)
+               otg->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static const struct phy_ops ops = {
+       .init           = twl4030_phy_init,
+       .power_on       = twl4030_phy_power_on,
+       .power_off      = twl4030_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static const struct dev_pm_ops twl4030_usb_pm_ops = {
+       SET_RUNTIME_PM_OPS(twl4030_usb_runtime_suspend,
+                          twl4030_usb_runtime_resume, NULL)
+};
+
+static int twl4030_usb_probe(struct platform_device *pdev)
+{
+       struct twl4030_usb_data *pdata = dev_get_platdata(&pdev->dev);
+       struct twl4030_usb      *twl;
+       struct phy              *phy;
+       int                     status, err;
+       struct usb_otg          *otg;
+       struct device_node      *np = pdev->dev.of_node;
+       struct phy_provider     *phy_provider;
+       struct phy_init_data    *init_data = NULL;
+
+       twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
+       if (!twl)
+               return -ENOMEM;
+
+       if (np)
+               of_property_read_u32(np, "usb_mode",
+                               (enum twl4030_usb_mode *)&twl->usb_mode);
+       else if (pdata) {
+               twl->usb_mode = pdata->usb_mode;
+               init_data = pdata->init_data;
+       } else {
+               dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
+               return -EINVAL;
+       }
+
+       otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+       if (!otg)
+               return -ENOMEM;
+
+       twl->dev                = &pdev->dev;
+       twl->irq                = platform_get_irq(pdev, 0);
+       twl->vbus_supplied      = false;
+       twl->linkstat           = -EINVAL;
+       twl->linkstat           = OMAP_MUSB_UNKNOWN;
+
+       twl->phy.dev            = twl->dev;
+       twl->phy.label          = "twl4030";
+       twl->phy.otg            = otg;
+       twl->phy.type           = USB_PHY_TYPE_USB2;
+
+       otg->usb_phy            = &twl->phy;
+       otg->set_host           = twl4030_set_host;
+       otg->set_peripheral     = twl4030_set_peripheral;
+
+       phy = devm_phy_create(twl->dev, NULL, &ops, init_data);
+       if (IS_ERR(phy)) {
+               dev_dbg(&pdev->dev, "Failed to create PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       phy_set_drvdata(phy, twl);
+
+       phy_provider = devm_of_phy_provider_register(twl->dev,
+               of_phy_simple_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       /* init mutex for workqueue */
+       mutex_init(&twl->lock);
+
+       INIT_DELAYED_WORK(&twl->id_workaround_work, twl4030_id_workaround_work);
+
+       err = twl4030_usb_ldo_init(twl);
+       if (err) {
+               dev_err(&pdev->dev, "ldo init failed\n");
+               return err;
+       }
+       usb_add_phy_dev(&twl->phy);
+
+       platform_set_drvdata(pdev, twl);
+       if (device_create_file(&pdev->dev, &dev_attr_vbus))
+               dev_warn(&pdev->dev, "could not create sysfs file\n");
+
+       ATOMIC_INIT_NOTIFIER_HEAD(&twl->phy.notifier);
+
+       pm_runtime_use_autosuspend(&pdev->dev);
+       pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_get_sync(&pdev->dev);
+
+       /* Our job is to use irqs and status from the power module
+        * to keep the transceiver disabled when nothing's connected.
+        *
+        * FIXME we actually shouldn't start enabling it until the
+        * USB controller drivers have said they're ready, by calling
+        * set_host() and/or set_peripheral() ... OTG_capable boards
+        * need both handles, otherwise just one suffices.
+        */
+       status = devm_request_threaded_irq(twl->dev, twl->irq, NULL,
+                       twl4030_usb_irq, IRQF_TRIGGER_FALLING |
+                       IRQF_TRIGGER_RISING | IRQF_ONESHOT, "twl4030_usb", twl);
+       if (status < 0) {
+               dev_dbg(&pdev->dev, "can't get IRQ %d, err %d\n",
+                       twl->irq, status);
+               return status;
+       }
+
+       pm_runtime_mark_last_busy(&pdev->dev);
+       pm_runtime_put_autosuspend(twl->dev);
+
+       dev_info(&pdev->dev, "Initialized TWL4030 USB module\n");
+       return 0;
+}
+
+static int twl4030_usb_remove(struct platform_device *pdev)
+{
+       struct twl4030_usb *twl = platform_get_drvdata(pdev);
+       int val;
+
+       pm_runtime_get_sync(twl->dev);
+       cancel_delayed_work(&twl->id_workaround_work);
+       device_remove_file(twl->dev, &dev_attr_vbus);
+
+       /* set transceiver mode to power on defaults */
+       twl4030_usb_set_mode(twl, -1);
+
+       /* autogate 60MHz ULPI clock,
+        * clear dpll clock request for i2c access,
+        * disable 32KHz
+        */
+       val = twl4030_usb_read(twl, PHY_CLK_CTRL);
+       if (val >= 0) {
+               val |= PHY_CLK_CTRL_CLOCKGATING_EN;
+               val &= ~(PHY_CLK_CTRL_CLK32K_EN | REQ_PHY_DPLL_CLK);
+               twl4030_usb_write(twl, PHY_CLK_CTRL, (u8)val);
+       }
+
+       /* disable complete OTG block */
+       twl4030_usb_clear_bits(twl, POWER_CTRL, POWER_CTRL_OTG_ENAB);
+       pm_runtime_mark_last_busy(twl->dev);
+       pm_runtime_put(twl->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id twl4030_usb_id_table[] = {
+       { .compatible = "ti,twl4030-usb" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, twl4030_usb_id_table);
+#endif
+
+static struct platform_driver twl4030_usb_driver = {
+       .probe          = twl4030_usb_probe,
+       .remove         = twl4030_usb_remove,
+       .driver         = {
+               .name   = "twl4030_usb",
+               .pm     = &twl4030_usb_pm_ops,
+               .of_match_table = of_match_ptr(twl4030_usb_id_table),
+       },
+};
+
+static int __init twl4030_usb_init(void)
+{
+       return platform_driver_register(&twl4030_usb_driver);
+}
+subsys_initcall(twl4030_usb_init);
+
+static void __exit twl4030_usb_exit(void)
+{
+       platform_driver_unregister(&twl4030_usb_driver);
+}
+module_exit(twl4030_usb_exit);
+
+MODULE_ALIAS("platform:twl4030_usb");
+MODULE_AUTHOR("Texas Instruments, Inc, Nokia Corporation");
+MODULE_DESCRIPTION("TWL4030 USB transceiver driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
new file mode 100644 (file)
index 0000000..f8a51b1
--- /dev/null
@@ -0,0 +1,1749 @@
+/*
+ * AppliedMicro X-Gene Multi-purpose PHY driver
+ *
+ * Copyright (c) 2014, Applied Micro Circuits Corporation
+ * Author: Loc Ho <lho@apm.com>
+ *         Tuan Phan <tphan@apm.com>
+ *         Suman Tripathi <stripathi@apm.com>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * The APM X-Gene PHY consists of two PLL clock macro's (CMU) and lanes.
+ * The first PLL clock macro is used for internal reference clock. The second
+ * PLL clock macro is used to generate the clock for the PHY. This driver
+ * configures the first PLL CMU, the second PLL CMU, and programs the PHY to
+ * operate according to the mode of operation. The first PLL CMU is only
+ * required if internal clock is enabled.
+ *
+ * Logical Layer Out Of HW module units:
+ *
+ * -----------------
+ * | Internal      |    |------|
+ * | Ref PLL CMU   |----|      |     -------------    ---------
+ * ------------ ----    | MUX  |-----|PHY PLL CMU|----| Serdes|
+ *                      |      |     |           |    ---------
+ * External Clock ------|      |     -------------
+ *                      |------|
+ *
+ * The Ref PLL CMU CSR (Configuration System Registers) is accessed
+ * indirectly from the SDS offset at 0x2000. It is only required for
+ * internal reference clock.
+ * The PHY PLL CMU CSR is accessed indirectly from the SDS offset at 0x0000.
+ * The Serdes CSR is accessed indirectly from the SDS offset at 0x0400.
+ *
+ * The Ref PLL CMU can be located within the same PHY IP or outside the PHY IP
+ * due to shared Ref PLL CMU. For PHY with Ref PLL CMU shared with another IP,
+ * it is located outside the PHY IP. This is the case for the PHY located
+ * at 0x1f23a000 (SATA Port 4/5). For such PHY, another resource is required
+ * to located the SDS/Ref PLL CMU module and its clock for that IP enabled.
+ *
+ * Currently, this driver only supports Gen3 SATA mode with external clock.
+ */
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/phy/phy.h>
+#include <linux/clk.h>
+
+/* Max 2 lanes per a PHY unit */
+#define MAX_LANE                       2
+
+/* Register offset inside the PHY */
+#define SERDES_PLL_INDIRECT_OFFSET     0x0000
+#define SERDES_PLL_REF_INDIRECT_OFFSET 0x2000
+#define SERDES_INDIRECT_OFFSET         0x0400
+#define SERDES_LANE_STRIDE             0x0200
+
+/* Some default Serdes parameters */
+#define DEFAULT_SATA_TXBOOST_GAIN      { 0x1e, 0x1e, 0x1e }
+#define DEFAULT_SATA_TXEYEDIRECTION    { 0x0, 0x0, 0x0 }
+#define DEFAULT_SATA_TXEYETUNING       { 0xa, 0xa, 0xa }
+#define DEFAULT_SATA_SPD_SEL           { 0x1, 0x3, 0x7 }
+#define DEFAULT_SATA_TXAMP             { 0x8, 0x8, 0x8 }
+#define DEFAULT_SATA_TXCN1             { 0x2, 0x2, 0x2 }
+#define DEFAULT_SATA_TXCN2             { 0x0, 0x0, 0x0 }
+#define DEFAULT_SATA_TXCP1             { 0xa, 0xa, 0xa }
+
+#define SATA_SPD_SEL_GEN3              0x7
+#define SATA_SPD_SEL_GEN2              0x3
+#define SATA_SPD_SEL_GEN1              0x1
+
+#define SSC_DISABLE                    0
+#define SSC_ENABLE                     1
+
+#define FBDIV_VAL_50M                  0x77
+#define REFDIV_VAL_50M                 0x1
+#define FBDIV_VAL_100M                 0x3B
+#define REFDIV_VAL_100M                        0x0
+
+/* SATA Clock/Reset CSR */
+#define SATACLKENREG                   0x00000000
+#define  SATA0_CORE_CLKEN              0x00000002
+#define  SATA1_CORE_CLKEN              0x00000004
+#define SATASRESETREG                  0x00000004
+#define  SATA_MEM_RESET_MASK           0x00000020
+#define  SATA_MEM_RESET_RD(src)                (((src) & 0x00000020) >> 5)
+#define  SATA_SDS_RESET_MASK           0x00000004
+#define  SATA_CSR_RESET_MASK           0x00000001
+#define  SATA_CORE_RESET_MASK          0x00000002
+#define  SATA_PMCLK_RESET_MASK         0x00000010
+#define  SATA_PCLK_RESET_MASK          0x00000008
+
+/* SDS CSR used for PHY Indirect access */
+#define SATA_ENET_SDS_PCS_CTL0         0x00000000
+#define  REGSPEC_CFG_I_TX_WORDMODE0_SET(dst, src) \
+               (((dst) & ~0x00070000) | (((u32) (src) << 16) & 0x00070000))
+#define  REGSPEC_CFG_I_RX_WORDMODE0_SET(dst, src) \
+               (((dst) & ~0x00e00000) | (((u32) (src) << 21) & 0x00e00000))
+#define SATA_ENET_SDS_CTL0             0x0000000c
+#define  REGSPEC_CFG_I_CUSTOMER_PIN_MODE0_SET(dst, src) \
+               (((dst) & ~0x00007fff) | (((u32) (src)) & 0x00007fff))
+#define SATA_ENET_SDS_CTL1             0x00000010
+#define  CFG_I_SPD_SEL_CDR_OVR1_SET(dst, src) \
+               (((dst) & ~0x0000000f) | (((u32) (src)) & 0x0000000f))
+#define SATA_ENET_SDS_RST_CTL          0x00000024
+#define SATA_ENET_SDS_IND_CMD_REG      0x0000003c
+#define  CFG_IND_WR_CMD_MASK           0x00000001
+#define  CFG_IND_RD_CMD_MASK           0x00000002
+#define  CFG_IND_CMD_DONE_MASK         0x00000004
+#define  CFG_IND_ADDR_SET(dst, src) \
+               (((dst) & ~0x003ffff0) | (((u32) (src) << 4) & 0x003ffff0))
+#define SATA_ENET_SDS_IND_RDATA_REG    0x00000040
+#define SATA_ENET_SDS_IND_WDATA_REG    0x00000044
+#define SATA_ENET_CLK_MACRO_REG                0x0000004c
+#define  I_RESET_B_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src)) & 0x00000001))
+#define  I_PLL_FBDIV_SET(dst, src) \
+               (((dst) & ~0x001ff000) | (((u32) (src) << 12) & 0x001ff000))
+#define  I_CUSTOMEROV_SET(dst, src) \
+               (((dst) & ~0x00000f80) | (((u32) (src) << 7) & 0x00000f80))
+#define  O_PLL_LOCK_RD(src)            (((src) & 0x40000000) >> 30)
+#define  O_PLL_READY_RD(src)           (((src) & 0x80000000) >> 31)
+
+/* PLL Clock Macro Unit (CMU) CSR accessing from SDS indirectly */
+#define CMU_REG0                       0x00000
+#define  CMU_REG0_PLL_REF_SEL_MASK     0x00002000
+#define  CMU_REG0_PLL_REF_SEL_SET(dst, src)    \
+               (((dst) & ~0x00002000) | (((u32) (src) << 13) & 0x00002000))
+#define  CMU_REG0_PDOWN_MASK           0x00004000
+#define  CMU_REG0_CAL_COUNT_RESOL_SET(dst, src) \
+               (((dst) & ~0x000000e0) | (((u32) (src) << 5) & 0x000000e0))
+#define CMU_REG1                       0x00002
+#define  CMU_REG1_PLL_CP_SET(dst, src) \
+               (((dst) & ~0x00003c00) | (((u32) (src) << 10) & 0x00003c00))
+#define  CMU_REG1_PLL_MANUALCAL_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  CMU_REG1_PLL_CP_SEL_SET(dst, src) \
+               (((dst) & ~0x000003e0) | (((u32) (src) << 5) & 0x000003e0))
+#define  CMU_REG1_REFCLK_CMOS_SEL_MASK 0x00000001
+#define  CMU_REG1_REFCLK_CMOS_SEL_SET(dst, src)        \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG2                       0x00004
+#define  CMU_REG2_PLL_REFDIV_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  CMU_REG2_PLL_LFRES_SET(dst, src) \
+               (((dst) & ~0x0000001e) | (((u32) (src) << 1) & 0x0000001e))
+#define  CMU_REG2_PLL_FBDIV_SET(dst, src) \
+               (((dst) & ~0x00003fe0) | (((u32) (src) << 5) & 0x00003fe0))
+#define CMU_REG3                       0x00006
+#define  CMU_REG3_VCOVARSEL_SET(dst, src) \
+               (((dst) & ~0x0000000f) | (((u32) (src) << 0) & 0x0000000f))
+#define  CMU_REG3_VCO_MOMSEL_INIT_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define  CMU_REG3_VCO_MANMOMSEL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define CMU_REG4                       0x00008
+#define CMU_REG5                       0x0000a
+#define  CMU_REG5_PLL_LFSMCAP_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  CMU_REG5_PLL_LOCK_RESOLUTION_SET(dst, src) \
+               (((dst) & ~0x0000000e) | (((u32) (src) << 1) & 0x0000000e))
+#define  CMU_REG5_PLL_LFCAP_SET(dst, src) \
+               (((dst) & ~0x00003000) | (((u32) (src) << 12) & 0x00003000))
+#define  CMU_REG5_PLL_RESETB_MASK      0x00000001
+#define CMU_REG6                       0x0000c
+#define  CMU_REG6_PLL_VREGTRIM_SET(dst, src) \
+               (((dst) & ~0x00000600) | (((u32) (src) << 9) & 0x00000600))
+#define  CMU_REG6_MAN_PVT_CAL_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define CMU_REG7                       0x0000e
+#define  CMU_REG7_PLL_CALIB_DONE_RD(src) ((0x00004000 & (u32) (src)) >> 14)
+#define  CMU_REG7_VCO_CAL_FAIL_RD(src) ((0x00000c00 & (u32) (src)) >> 10)
+#define CMU_REG8                       0x00010
+#define CMU_REG9                       0x00012
+#define  CMU_REG9_WORD_LEN_8BIT                0x000
+#define  CMU_REG9_WORD_LEN_10BIT       0x001
+#define  CMU_REG9_WORD_LEN_16BIT       0x002
+#define  CMU_REG9_WORD_LEN_20BIT       0x003
+#define  CMU_REG9_WORD_LEN_32BIT       0x004
+#define  CMU_REG9_WORD_LEN_40BIT       0x005
+#define  CMU_REG9_WORD_LEN_64BIT       0x006
+#define  CMU_REG9_WORD_LEN_66BIT       0x007
+#define  CMU_REG9_TX_WORD_MODE_CH1_SET(dst, src) \
+               (((dst) & ~0x00000380) | (((u32) (src) << 7) & 0x00000380))
+#define  CMU_REG9_TX_WORD_MODE_CH0_SET(dst, src) \
+               (((dst) & ~0x00000070) | (((u32) (src) << 4) & 0x00000070))
+#define  CMU_REG9_PLL_POST_DIVBY2_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  CMU_REG9_VBG_BYPASSB_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define  CMU_REG9_IGEN_BYPASS_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define CMU_REG10                      0x00014
+#define  CMU_REG10_VREG_REFSEL_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG11                      0x00016
+#define CMU_REG12                      0x00018
+#define  CMU_REG12_STATE_DELAY9_SET(dst, src) \
+               (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define CMU_REG13                      0x0001a
+#define CMU_REG14                      0x0001c
+#define CMU_REG15                      0x0001e
+#define CMU_REG16                      0x00020
+#define  CMU_REG16_PVT_DN_MAN_ENA_MASK 0x00000001
+#define  CMU_REG16_PVT_UP_MAN_ENA_MASK 0x00000002
+#define  CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(dst, src) \
+               (((dst) & ~0x0000001c) | (((u32) (src) << 2) & 0x0000001c))
+#define  CMU_REG16_CALIBRATION_DONE_OVERRIDE_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define  CMU_REG16_BYPASS_PLL_LOCK_SET(dst, src) \
+               (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define CMU_REG17                      0x00022
+#define  CMU_REG17_PVT_CODE_R2A_SET(dst, src) \
+               (((dst) & ~0x00007f00) | (((u32) (src) << 8) & 0x00007f00))
+#define  CMU_REG17_RESERVED_7_SET(dst, src) \
+               (((dst) & ~0x000000e0) | (((u32) (src) << 5) & 0x000000e0))
+#define  CMU_REG17_PVT_TERM_MAN_ENA_MASK       0x00008000
+#define CMU_REG18                      0x00024
+#define CMU_REG19                      0x00026
+#define CMU_REG20                      0x00028
+#define CMU_REG21                      0x0002a
+#define CMU_REG22                      0x0002c
+#define CMU_REG23                      0x0002e
+#define CMU_REG24                      0x00030
+#define CMU_REG25                      0x00032
+#define CMU_REG26                      0x00034
+#define  CMU_REG26_FORCE_PLL_LOCK_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define CMU_REG27                      0x00036
+#define CMU_REG28                      0x00038
+#define CMU_REG29                      0x0003a
+#define CMU_REG30                      0x0003c
+#define  CMU_REG30_LOCK_COUNT_SET(dst, src) \
+               (((dst) & ~0x00000006) | (((u32) (src) << 1) & 0x00000006))
+#define  CMU_REG30_PCIE_MODE_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define CMU_REG31                      0x0003e
+#define CMU_REG32                      0x00040
+#define  CMU_REG32_FORCE_VCOCAL_START_MASK     0x00004000
+#define  CMU_REG32_PVT_CAL_WAIT_SEL_SET(dst, src) \
+               (((dst) & ~0x00000006) | (((u32) (src) << 1) & 0x00000006))
+#define  CMU_REG32_IREF_ADJ_SET(dst, src) \
+               (((dst) & ~0x00000180) | (((u32) (src) << 7) & 0x00000180))
+#define CMU_REG33                      0x00042
+#define CMU_REG34                      0x00044
+#define  CMU_REG34_VCO_CAL_VTH_LO_MAX_SET(dst, src) \
+               (((dst) & ~0x0000000f) | (((u32) (src) << 0) & 0x0000000f))
+#define  CMU_REG34_VCO_CAL_VTH_HI_MAX_SET(dst, src) \
+               (((dst) & ~0x00000f00) | (((u32) (src) << 8) & 0x00000f00))
+#define  CMU_REG34_VCO_CAL_VTH_LO_MIN_SET(dst, src) \
+               (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define  CMU_REG34_VCO_CAL_VTH_HI_MIN_SET(dst, src) \
+               (((dst) & ~0x0000f000) | (((u32) (src) << 12) & 0x0000f000))
+#define CMU_REG35                      0x00046
+#define  CMU_REG35_PLL_SSC_MOD_SET(dst, src) \
+               (((dst) & ~0x0000fe00) | (((u32) (src) << 9) & 0x0000fe00))
+#define CMU_REG36                              0x00048
+#define  CMU_REG36_PLL_SSC_EN_SET(dst, src) \
+               (((dst) & ~0x00000010) | (((u32) (src) << 4) & 0x00000010))
+#define  CMU_REG36_PLL_SSC_VSTEP_SET(dst, src) \
+               (((dst) & ~0x0000ffc0) | (((u32) (src) << 6) & 0x0000ffc0))
+#define  CMU_REG36_PLL_SSC_DSMSEL_SET(dst, src) \
+               (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define CMU_REG37                      0x0004a
+#define CMU_REG38                      0x0004c
+#define CMU_REG39                      0x0004e
+
+/* PHY lane CSR accessing from SDS indirectly */
+#define RXTX_REG0                      0x000
+#define  RXTX_REG0_CTLE_EQ_HR_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG0_CTLE_EQ_QR_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG0_CTLE_EQ_FR_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG1                      0x002
+#define  RXTX_REG1_RXACVCM_SET(dst, src) \
+               (((dst) & ~0x0000f000) | (((u32) (src) << 12) & 0x0000f000))
+#define  RXTX_REG1_CTLE_EQ_SET(dst, src) \
+               (((dst) & ~0x00000f80) | (((u32) (src) << 7) & 0x00000f80))
+#define  RXTX_REG1_RXVREG1_SET(dst, src) \
+               (((dst) & ~0x00000060) | (((u32) (src) << 5) & 0x00000060))
+#define  RXTX_REG1_RXIREF_ADJ_SET(dst, src) \
+               (((dst) & ~0x00000006) | (((u32) (src) << 1) &  0x00000006))
+#define RXTX_REG2                      0x004
+#define  RXTX_REG2_VTT_ENA_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define  RXTX_REG2_TX_FIFO_ENA_SET(dst, src) \
+               (((dst) & ~0x00000020) | (((u32) (src) << 5) & 0x00000020))
+#define  RXTX_REG2_VTT_SEL_SET(dst, src) \
+               (((dst) & ~0x000000c0) | (((u32) (src) << 6) & 0x000000c0))
+#define RXTX_REG4                      0x008
+#define  RXTX_REG4_TX_LOOPBACK_BUF_EN_MASK     0x00000040
+#define  RXTX_REG4_TX_DATA_RATE_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  RXTX_REG4_TX_WORD_MODE_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG5                      0x00a
+#define  RXTX_REG5_TX_CN1_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG5_TX_CP1_SET(dst, src) \
+               (((dst) & ~0x000007e0) | (((u32) (src) << 5) & 0x000007e0))
+#define  RXTX_REG5_TX_CN2_SET(dst, src) \
+               (((dst) & ~0x0000001f) | (((u32) (src) << 0) & 0x0000001f))
+#define RXTX_REG6                      0x00c
+#define  RXTX_REG6_TXAMP_CNTL_SET(dst, src) \
+               (((dst) & ~0x00000780) | (((u32) (src) << 7) & 0x00000780))
+#define  RXTX_REG6_TXAMP_ENA_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define  RXTX_REG6_RX_BIST_ERRCNT_RD_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define  RXTX_REG6_TX_IDLE_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  RXTX_REG6_RX_BIST_RESYNC_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG7                      0x00e
+#define  RXTX_REG7_RESETB_RXD_MASK     0x00000100
+#define  RXTX_REG7_RESETB_RXA_MASK     0x00000080
+#define  RXTX_REG7_BIST_ENA_RX_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define  RXTX_REG7_RX_WORD_MODE_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG8                      0x010
+#define  RXTX_REG8_CDR_LOOP_ENA_SET(dst, src) \
+               (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
+#define  RXTX_REG8_CDR_BYPASS_RXLOS_SET(dst, src) \
+               (((dst) & ~0x00000800) | (((u32) (src) << 11) & 0x00000800))
+#define  RXTX_REG8_SSC_ENABLE_SET(dst, src) \
+               (((dst) & ~0x00000200) | (((u32) (src) << 9) & 0x00000200))
+#define  RXTX_REG8_SD_VREF_SET(dst, src) \
+               (((dst) & ~0x000000f0) | (((u32) (src) << 4) & 0x000000f0))
+#define  RXTX_REG8_SD_DISABLE_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define RXTX_REG7                      0x00e
+#define  RXTX_REG7_RESETB_RXD_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define  RXTX_REG7_RESETB_RXA_SET(dst, src) \
+               (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
+#define  RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK     0x00004000
+#define  RXTX_REG7_LOOP_BACK_ENA_CTLE_SET(dst, src) \
+               (((dst) & ~0x00004000) | (((u32) (src) << 14) & 0x00004000))
+#define RXTX_REG11                     0x016
+#define  RXTX_REG11_PHASE_ADJUST_LIMIT_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define RXTX_REG12                     0x018
+#define  RXTX_REG12_LATCH_OFF_ENA_SET(dst, src) \
+               (((dst) & ~0x00002000) | (((u32) (src) << 13) & 0x00002000))
+#define  RXTX_REG12_SUMOS_ENABLE_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define  RXTX_REG12_RX_DET_TERM_ENABLE_MASK    0x00000002
+#define  RXTX_REG12_RX_DET_TERM_ENABLE_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG13                     0x01a
+#define RXTX_REG14                     0x01c
+#define  RXTX_REG14_CLTE_LATCAL_MAN_PROG_SET(dst, src) \
+               (((dst) & ~0x0000003f) | (((u32) (src) << 0) & 0x0000003f))
+#define  RXTX_REG14_CTLE_LATCAL_MAN_ENA_SET(dst, src) \
+               (((dst) & ~0x00000040) | (((u32) (src) << 6) & 0x00000040))
+#define RXTX_REG26                     0x034
+#define  RXTX_REG26_PERIOD_ERROR_LATCH_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define  RXTX_REG26_BLWC_ENA_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define RXTX_REG21                     0x02a
+#define  RXTX_REG21_DO_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG21_XO_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define  RXTX_REG21_LATCH_CAL_FAIL_ODD_RD(src) ((0x0000000f & (u32)(src)))
+#define RXTX_REG22                     0x02c
+#define  RXTX_REG22_SO_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define  RXTX_REG22_EO_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG22_LATCH_CAL_FAIL_EVEN_RD(src)        ((0x0000000f & (u32)(src)))
+#define RXTX_REG23                     0x02e
+#define  RXTX_REG23_DE_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG23_XE_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG24                     0x030
+#define  RXTX_REG24_EE_LATCH_CALOUT_RD(src) ((0x0000fc00 & (u32) (src)) >> 10)
+#define  RXTX_REG24_SE_LATCH_CALOUT_RD(src) ((0x000003f0 & (u32) (src)) >> 4)
+#define RXTX_REG27                     0x036
+#define RXTX_REG28                     0x038
+#define RXTX_REG31                     0x03e
+#define RXTX_REG38                     0x04c
+#define  RXTX_REG38_CUSTOMER_PINMODE_INV_SET(dst, src) \
+               (((dst) & 0x0000fffe) | (((u32) (src) << 1) & 0x0000fffe))
+#define RXTX_REG39                     0x04e
+#define RXTX_REG40                     0x050
+#define RXTX_REG41                     0x052
+#define RXTX_REG42                     0x054
+#define RXTX_REG43                     0x056
+#define RXTX_REG44                     0x058
+#define RXTX_REG45                     0x05a
+#define RXTX_REG46                     0x05c
+#define RXTX_REG47                     0x05e
+#define RXTX_REG48                     0x060
+#define RXTX_REG49                     0x062
+#define RXTX_REG50                     0x064
+#define RXTX_REG51                     0x066
+#define RXTX_REG52                     0x068
+#define RXTX_REG53                     0x06a
+#define RXTX_REG54                     0x06c
+#define RXTX_REG55                     0x06e
+#define RXTX_REG61                     0x07a
+#define  RXTX_REG61_ISCAN_INBERT_SET(dst, src) \
+               (((dst) & ~0x00000010) | (((u32) (src) << 4) & 0x00000010))
+#define  RXTX_REG61_LOADFREQ_SHIFT_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  RXTX_REG61_EYE_COUNT_WIDTH_SEL_SET(dst, src) \
+               (((dst) & ~0x000000c0) | (((u32) (src) << 6) & 0x000000c0))
+#define  RXTX_REG61_SPD_SEL_CDR_SET(dst, src) \
+               (((dst) & ~0x00003c00) | (((u32) (src) << 10) & 0x00003c00))
+#define RXTX_REG62                     0x07c
+#define  RXTX_REG62_PERIOD_H1_QLATCH_SET(dst, src) \
+               (((dst) & ~0x00003800) | (((u32) (src) << 11) & 0x00003800))
+#define RXTX_REG81                     0x0a2
+#define  RXTX_REG89_MU_TH7_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG89_MU_TH8_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG89_MU_TH9_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG96                     0x0c0
+#define  RXTX_REG96_MU_FREQ1_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG96_MU_FREQ2_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG96_MU_FREQ3_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG99                     0x0c6
+#define  RXTX_REG99_MU_PHASE1_SET(dst, src) \
+               (((dst) & ~0x0000f800) | (((u32) (src) << 11) & 0x0000f800))
+#define  RXTX_REG99_MU_PHASE2_SET(dst, src) \
+               (((dst) & ~0x000007c0) | (((u32) (src) << 6) & 0x000007c0))
+#define  RXTX_REG99_MU_PHASE3_SET(dst, src) \
+               (((dst) & ~0x0000003e) | (((u32) (src) << 1) & 0x0000003e))
+#define RXTX_REG102                    0x0cc
+#define  RXTX_REG102_FREQLOOP_LIMIT_SET(dst, src) \
+               (((dst) & ~0x00000060) | (((u32) (src) << 5) & 0x00000060))
+#define RXTX_REG114                    0x0e4
+#define RXTX_REG121                    0x0f2
+#define  RXTX_REG121_SUMOS_CAL_CODE_RD(src) ((0x0000003e & (u32)(src)) >> 0x1)
+#define RXTX_REG125                    0x0fa
+#define  RXTX_REG125_PQ_REG_SET(dst, src) \
+               (((dst) & ~0x0000fe00) | (((u32) (src) << 9) & 0x0000fe00))
+#define  RXTX_REG125_SIGN_PQ_SET(dst, src) \
+               (((dst) & ~0x00000100) | (((u32) (src) << 8) & 0x00000100))
+#define  RXTX_REG125_SIGN_PQ_2C_SET(dst, src) \
+               (((dst) & ~0x00000080) | (((u32) (src) << 7) & 0x00000080))
+#define  RXTX_REG125_PHZ_MANUALCODE_SET(dst, src) \
+               (((dst) & ~0x0000007c) | (((u32) (src) << 2) & 0x0000007c))
+#define  RXTX_REG125_PHZ_MANUAL_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define RXTX_REG127                    0x0fe
+#define  RXTX_REG127_FORCE_SUM_CAL_START_MASK  0x00000002
+#define  RXTX_REG127_FORCE_LAT_CAL_START_MASK  0x00000004
+#define  RXTX_REG127_FORCE_SUM_CAL_START_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define  RXTX_REG127_FORCE_LAT_CAL_START_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define  RXTX_REG127_LATCH_MAN_CAL_ENA_SET(dst, src) \
+               (((dst) & ~0x00000008) | (((u32) (src) << 3) & 0x00000008))
+#define  RXTX_REG127_DO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG127_XO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG128                    0x100
+#define  RXTX_REG128_LATCH_CAL_WAIT_SEL_SET(dst, src) \
+               (((dst) & ~0x0000000c) | (((u32) (src) << 2) & 0x0000000c))
+#define  RXTX_REG128_EO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG128_SO_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG129                    0x102
+#define  RXTX_REG129_DE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG129_XE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG130                    0x104
+#define  RXTX_REG130_EE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x0000fc00) | (((u32) (src) << 10) & 0x0000fc00))
+#define  RXTX_REG130_SE_LATCH_MANCAL_SET(dst, src) \
+               (((dst) & ~0x000003f0) | (((u32) (src) << 4) & 0x000003f0))
+#define RXTX_REG145                    0x122
+#define  RXTX_REG145_TX_IDLE_SATA_SET(dst, src) \
+               (((dst) & ~0x00000001) | (((u32) (src) << 0) & 0x00000001))
+#define  RXTX_REG145_RXES_ENA_SET(dst, src) \
+               (((dst) & ~0x00000002) | (((u32) (src) << 1) & 0x00000002))
+#define  RXTX_REG145_RXDFE_CONFIG_SET(dst, src) \
+               (((dst) & ~0x0000c000) | (((u32) (src) << 14) & 0x0000c000))
+#define  RXTX_REG145_RXVWES_LATENA_SET(dst, src) \
+               (((dst) & ~0x00000004) | (((u32) (src) << 2) & 0x00000004))
+#define RXTX_REG147                    0x126
+#define RXTX_REG148                    0x128
+
+/* Clock macro type */
+enum cmu_type_t {
+       REF_CMU = 0,    /* Clock macro is the internal reference clock */
+       PHY_CMU = 1,    /* Clock macro is the PLL for the Serdes */
+};
+
+enum mux_type_t {
+       MUX_SELECT_ATA = 0,     /* Switch the MUX to ATA */
+       MUX_SELECT_SGMMII = 0,  /* Switch the MUX to SGMII */
+};
+
+enum clk_type_t {
+       CLK_EXT_DIFF = 0,       /* External differential */
+       CLK_INT_DIFF = 1,       /* Internal differential */
+       CLK_INT_SING = 2,       /* Internal single ended */
+};
+
+enum phy_mode {
+       MODE_SATA       = 0,    /* List them for simple reference */
+       MODE_SGMII      = 1,
+       MODE_PCIE       = 2,
+       MODE_USB        = 3,
+       MODE_XFI        = 4,
+       MODE_MAX
+};
+
+struct xgene_sata_override_param {
+       u32 speed[MAX_LANE]; /* Index for override parameter per lane */
+       u32 txspeed[3];                 /* Tx speed */
+       u32 txboostgain[MAX_LANE*3];    /* Tx freq boost and gain control */
+       u32 txeyetuning[MAX_LANE*3];    /* Tx eye tuning */
+       u32 txeyedirection[MAX_LANE*3]; /* Tx eye tuning direction */
+       u32 txamplitude[MAX_LANE*3];    /* Tx amplitude control */
+       u32 txprecursor_cn1[MAX_LANE*3]; /* Tx emphasis taps 1st pre-cursor */
+       u32 txprecursor_cn2[MAX_LANE*3]; /* Tx emphasis taps 2nd pre-cursor */
+       u32 txpostcursor_cp1[MAX_LANE*3]; /* Tx emphasis taps post-cursor */
+};
+
+struct xgene_phy_ctx {
+       struct device *dev;
+       struct phy *phy;
+       enum phy_mode mode;             /* Mode of operation */
+       enum clk_type_t clk_type;       /* Input clock selection */
+       void __iomem *sds_base;         /* PHY CSR base addr */
+       struct clk *clk;                /* Optional clock */
+
+       /* Override Serdes parameters */
+       struct xgene_sata_override_param sata_param;
+};
+
+/*
+ * For chip earlier than A3 version, enable this flag.
+ * To enable, pass boot argument phy_xgene.preA3Chip=1
+ */
+static int preA3Chip;
+MODULE_PARM_DESC(preA3Chip, "Enable pre-A3 chip support (1=enable 0=disable)");
+module_param_named(preA3Chip, preA3Chip, int, 0444);
+
+static void sds_wr(void __iomem *csr_base, u32 indirect_cmd_reg,
+                  u32 indirect_data_reg, u32 addr, u32 data)
+{
+       unsigned long deadline = jiffies + HZ;
+       u32 val;
+       u32 cmd;
+
+       cmd = CFG_IND_WR_CMD_MASK | CFG_IND_CMD_DONE_MASK;
+       cmd = CFG_IND_ADDR_SET(cmd, addr);
+       writel(data, csr_base + indirect_data_reg);
+       readl(csr_base + indirect_data_reg); /* Force a barrier */
+       writel(cmd, csr_base + indirect_cmd_reg);
+       readl(csr_base + indirect_cmd_reg); /* Force a barrier */
+       do {
+               val = readl(csr_base + indirect_cmd_reg);
+       } while (!(val & CFG_IND_CMD_DONE_MASK) &&
+                time_before(jiffies, deadline));
+       if (!(val & CFG_IND_CMD_DONE_MASK))
+               pr_err("SDS WR timeout at 0x%p offset 0x%08X value 0x%08X\n",
+                      csr_base + indirect_cmd_reg, addr, data);
+}
+
+static void sds_rd(void __iomem *csr_base, u32 indirect_cmd_reg,
+                  u32 indirect_data_reg, u32 addr, u32 *data)
+{
+       unsigned long deadline = jiffies + HZ;
+       u32 val;
+       u32 cmd;
+
+       cmd = CFG_IND_RD_CMD_MASK | CFG_IND_CMD_DONE_MASK;
+       cmd = CFG_IND_ADDR_SET(cmd, addr);
+       writel(cmd, csr_base + indirect_cmd_reg);
+       readl(csr_base + indirect_cmd_reg); /* Force a barrier */
+       do {
+               val = readl(csr_base + indirect_cmd_reg);
+       } while (!(val & CFG_IND_CMD_DONE_MASK) &&
+                time_before(jiffies, deadline));
+       *data = readl(csr_base + indirect_data_reg);
+       if (!(val & CFG_IND_CMD_DONE_MASK))
+               pr_err("SDS WR timeout at 0x%p offset 0x%08X value 0x%08X\n",
+                      csr_base + indirect_cmd_reg, addr, *data);
+}
+
+static void cmu_wr(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                  u32 reg, u32 data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+       u32 val;
+
+       if (cmu_type == REF_CMU)
+               reg += SERDES_PLL_REF_INDIRECT_OFFSET;
+       else
+               reg += SERDES_PLL_INDIRECT_OFFSET;
+       sds_wr(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+               SATA_ENET_SDS_IND_WDATA_REG, reg, data);
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+               SATA_ENET_SDS_IND_RDATA_REG, reg, &val);
+       pr_debug("CMU WR addr 0x%X value 0x%08X <-> 0x%08X\n", reg, data, val);
+}
+
+static void cmu_rd(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                  u32 reg, u32 *data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+
+       if (cmu_type == REF_CMU)
+               reg += SERDES_PLL_REF_INDIRECT_OFFSET;
+       else
+               reg += SERDES_PLL_INDIRECT_OFFSET;
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+               SATA_ENET_SDS_IND_RDATA_REG, reg, data);
+       pr_debug("CMU RD addr 0x%X value 0x%08X\n", reg, *data);
+}
+
+static void cmu_toggle1to0(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                          u32 reg, u32 bits)
+{
+       u32 val;
+
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val |= bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val &= ~bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void cmu_clrbits(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                       u32 reg, u32 bits)
+{
+       u32 val;
+
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val &= ~bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void cmu_setbits(struct xgene_phy_ctx *ctx, enum cmu_type_t cmu_type,
+                       u32 reg, u32 bits)
+{
+       u32 val;
+
+       cmu_rd(ctx, cmu_type, reg, &val);
+       val |= bits;
+       cmu_wr(ctx, cmu_type, reg, val);
+}
+
+static void serdes_wr(struct xgene_phy_ctx *ctx, int lane, u32 reg, u32 data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+       u32 val;
+
+       reg += SERDES_INDIRECT_OFFSET;
+       reg += lane * SERDES_LANE_STRIDE;
+       sds_wr(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+              SATA_ENET_SDS_IND_WDATA_REG, reg, data);
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+              SATA_ENET_SDS_IND_RDATA_REG, reg, &val);
+       pr_debug("SERDES WR addr 0x%X value 0x%08X <-> 0x%08X\n", reg, data,
+                val);
+}
+
+static void serdes_rd(struct xgene_phy_ctx *ctx, int lane, u32 reg, u32 *data)
+{
+       void __iomem *sds_base = ctx->sds_base;
+
+       reg += SERDES_INDIRECT_OFFSET;
+       reg += lane * SERDES_LANE_STRIDE;
+       sds_rd(sds_base, SATA_ENET_SDS_IND_CMD_REG,
+              SATA_ENET_SDS_IND_RDATA_REG, reg, data);
+       pr_debug("SERDES RD addr 0x%X value 0x%08X\n", reg, *data);
+}
+
+static void serdes_clrbits(struct xgene_phy_ctx *ctx, int lane, u32 reg,
+                          u32 bits)
+{
+       u32 val;
+
+       serdes_rd(ctx, lane, reg, &val);
+       val &= ~bits;
+       serdes_wr(ctx, lane, reg, val);
+}
+
+static void serdes_setbits(struct xgene_phy_ctx *ctx, int lane, u32 reg,
+                          u32 bits)
+{
+       u32 val;
+
+       serdes_rd(ctx, lane, reg, &val);
+       val |= bits;
+       serdes_wr(ctx, lane, reg, val);
+}
+
+static void xgene_phy_cfg_cmu_clk_type(struct xgene_phy_ctx *ctx,
+                                      enum cmu_type_t cmu_type,
+                                      enum clk_type_t clk_type)
+{
+       u32 val;
+
+       /* Set the reset sequence delay for TX ready assertion */
+       cmu_rd(ctx, cmu_type, CMU_REG12, &val);
+       val = CMU_REG12_STATE_DELAY9_SET(val, 0x1);
+       cmu_wr(ctx, cmu_type, CMU_REG12, val);
+       /* Set the programmable stage delays between various enable stages */
+       cmu_wr(ctx, cmu_type, CMU_REG13, 0x0222);
+       cmu_wr(ctx, cmu_type, CMU_REG14, 0x2225);
+
+       /* Configure clock type */
+       if (clk_type == CLK_EXT_DIFF) {
+               /* Select external clock mux */
+               cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+               val = CMU_REG0_PLL_REF_SEL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG0, val);
+               /* Select CMOS as reference clock  */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               dev_dbg(ctx->dev, "Set external reference clock\n");
+       } else if (clk_type == CLK_INT_DIFF) {
+               /* Select internal clock mux */
+               cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+               val = CMU_REG0_PLL_REF_SEL_SET(val, 0x1);
+               cmu_wr(ctx, cmu_type, CMU_REG0, val);
+               /* Select CMOS as reference clock  */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x1);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               dev_dbg(ctx->dev, "Set internal reference clock\n");
+       } else if (clk_type == CLK_INT_SING) {
+               /*
+                * NOTE: This clock type is NOT support for controller
+                *       whose internal clock shared in the PCIe controller
+                *
+                * Select internal clock mux
+                */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x1);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               /* Select CML as reference clock */
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_REFCLK_CMOS_SEL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               dev_dbg(ctx->dev,
+                       "Set internal single ended reference clock\n");
+       }
+}
+
+static void xgene_phy_sata_cfg_cmu_core(struct xgene_phy_ctx *ctx,
+                                       enum cmu_type_t cmu_type,
+                                       enum clk_type_t clk_type)
+{
+       u32 val;
+       int ref_100MHz;
+
+       if (cmu_type == REF_CMU) {
+               /* Set VCO calibration voltage threshold */
+               cmu_rd(ctx, cmu_type, CMU_REG34, &val);
+               val = CMU_REG34_VCO_CAL_VTH_LO_MAX_SET(val, 0x7);
+               val = CMU_REG34_VCO_CAL_VTH_HI_MAX_SET(val, 0xc);
+               val = CMU_REG34_VCO_CAL_VTH_LO_MIN_SET(val, 0x3);
+               val = CMU_REG34_VCO_CAL_VTH_HI_MIN_SET(val, 0x8);
+               cmu_wr(ctx, cmu_type, CMU_REG34, val);
+       }
+
+       /* Set the VCO calibration counter */
+       cmu_rd(ctx, cmu_type, CMU_REG0, &val);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG0_CAL_COUNT_RESOL_SET(val, 0x4);
+       else
+               val = CMU_REG0_CAL_COUNT_RESOL_SET(val, 0x7);
+       cmu_wr(ctx, cmu_type, CMU_REG0, val);
+
+       /* Configure PLL for calibration */
+       cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+       val = CMU_REG1_PLL_CP_SET(val, 0x1);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG1_PLL_CP_SEL_SET(val, 0x5);
+       else
+               val = CMU_REG1_PLL_CP_SEL_SET(val, 0x3);
+       if (cmu_type == REF_CMU)
+               val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x0);
+       else
+               val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x1);
+       cmu_wr(ctx, cmu_type, CMU_REG1, val);
+
+       if (cmu_type != REF_CMU)
+               cmu_clrbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+
+       /* Configure the PLL for either 100MHz or 50MHz */
+       cmu_rd(ctx, cmu_type, CMU_REG2, &val);
+       if (cmu_type == REF_CMU) {
+               val = CMU_REG2_PLL_LFRES_SET(val, 0xa);
+               ref_100MHz = 1;
+       } else {
+               val = CMU_REG2_PLL_LFRES_SET(val, 0x3);
+               if (clk_type == CLK_EXT_DIFF)
+                       ref_100MHz = 0;
+               else
+                       ref_100MHz = 1;
+       }
+       if (ref_100MHz) {
+               val = CMU_REG2_PLL_FBDIV_SET(val, FBDIV_VAL_100M);
+               val = CMU_REG2_PLL_REFDIV_SET(val, REFDIV_VAL_100M);
+       } else {
+               val = CMU_REG2_PLL_FBDIV_SET(val, FBDIV_VAL_50M);
+               val = CMU_REG2_PLL_REFDIV_SET(val, REFDIV_VAL_50M);
+       }
+       cmu_wr(ctx, cmu_type, CMU_REG2, val);
+
+       /* Configure the VCO */
+       cmu_rd(ctx, cmu_type, CMU_REG3, &val);
+       if (cmu_type == REF_CMU) {
+               val = CMU_REG3_VCOVARSEL_SET(val, 0x3);
+               val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x10);
+       } else {
+               val = CMU_REG3_VCOVARSEL_SET(val, 0xF);
+               if (preA3Chip)
+                       val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x15);
+               else
+                       val = CMU_REG3_VCO_MOMSEL_INIT_SET(val, 0x1a);
+               val = CMU_REG3_VCO_MANMOMSEL_SET(val, 0x15);
+       }
+       cmu_wr(ctx, cmu_type, CMU_REG3, val);
+
+       /* Disable force PLL lock */
+       cmu_rd(ctx, cmu_type, CMU_REG26, &val);
+       val = CMU_REG26_FORCE_PLL_LOCK_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG26, val);
+
+       /* Setup PLL loop filter */
+       cmu_rd(ctx, cmu_type, CMU_REG5, &val);
+       val = CMU_REG5_PLL_LFSMCAP_SET(val, 0x3);
+       val = CMU_REG5_PLL_LFCAP_SET(val, 0x3);
+       if (cmu_type == REF_CMU || !preA3Chip)
+               val = CMU_REG5_PLL_LOCK_RESOLUTION_SET(val, 0x7);
+       else
+               val = CMU_REG5_PLL_LOCK_RESOLUTION_SET(val, 0x4);
+       cmu_wr(ctx, cmu_type, CMU_REG5, val);
+
+       /* Enable or disable manual calibration */
+       cmu_rd(ctx, cmu_type, CMU_REG6, &val);
+       val = CMU_REG6_PLL_VREGTRIM_SET(val, preA3Chip ? 0x0 : 0x2);
+       val = CMU_REG6_MAN_PVT_CAL_SET(val, preA3Chip ? 0x1 : 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG6, val);
+
+       /* Configure lane for 20-bits */
+       if (cmu_type == PHY_CMU) {
+               cmu_rd(ctx, cmu_type, CMU_REG9, &val);
+               val = CMU_REG9_TX_WORD_MODE_CH1_SET(val,
+                                                   CMU_REG9_WORD_LEN_20BIT);
+               val = CMU_REG9_TX_WORD_MODE_CH0_SET(val,
+                                                   CMU_REG9_WORD_LEN_20BIT);
+               val = CMU_REG9_PLL_POST_DIVBY2_SET(val, 0x1);
+               if (!preA3Chip) {
+                       val = CMU_REG9_VBG_BYPASSB_SET(val, 0x0);
+                       val = CMU_REG9_IGEN_BYPASS_SET(val , 0x0);
+               }
+               cmu_wr(ctx, cmu_type, CMU_REG9, val);
+
+               if (!preA3Chip) {
+                       cmu_rd(ctx, cmu_type, CMU_REG10, &val);
+                       val = CMU_REG10_VREG_REFSEL_SET(val, 0x1);
+                       cmu_wr(ctx, cmu_type, CMU_REG10, val);
+               }
+       }
+
+       cmu_rd(ctx, cmu_type, CMU_REG16, &val);
+       val = CMU_REG16_CALIBRATION_DONE_OVERRIDE_SET(val, 0x1);
+       val = CMU_REG16_BYPASS_PLL_LOCK_SET(val, 0x1);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x4);
+       else
+               val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x7);
+       cmu_wr(ctx, cmu_type, CMU_REG16, val);
+
+       /* Configure for SATA */
+       cmu_rd(ctx, cmu_type, CMU_REG30, &val);
+       val = CMU_REG30_PCIE_MODE_SET(val, 0x0);
+       val = CMU_REG30_LOCK_COUNT_SET(val, 0x3);
+       cmu_wr(ctx, cmu_type, CMU_REG30, val);
+
+       /* Disable state machine bypass */
+       cmu_wr(ctx, cmu_type, CMU_REG31, 0xF);
+
+       cmu_rd(ctx, cmu_type, CMU_REG32, &val);
+       val = CMU_REG32_PVT_CAL_WAIT_SEL_SET(val, 0x3);
+       if (cmu_type == REF_CMU || preA3Chip)
+               val = CMU_REG32_IREF_ADJ_SET(val, 0x3);
+       else
+               val = CMU_REG32_IREF_ADJ_SET(val, 0x1);
+       cmu_wr(ctx, cmu_type, CMU_REG32, val);
+
+       /* Set VCO calibration threshold */
+       if (cmu_type != REF_CMU && preA3Chip)
+               cmu_wr(ctx, cmu_type, CMU_REG34, 0x8d27);
+       else
+               cmu_wr(ctx, cmu_type, CMU_REG34, 0x873c);
+
+       /* Set CTLE Override and override waiting from state machine */
+       cmu_wr(ctx, cmu_type, CMU_REG37, 0xF00F);
+}
+
+static void xgene_phy_ssc_enable(struct xgene_phy_ctx *ctx,
+                                enum cmu_type_t cmu_type)
+{
+       u32 val;
+
+       /* Set SSC modulation value */
+       cmu_rd(ctx, cmu_type, CMU_REG35, &val);
+       val = CMU_REG35_PLL_SSC_MOD_SET(val, 98);
+       cmu_wr(ctx, cmu_type, CMU_REG35, val);
+
+       /* Enable SSC, set vertical step and DSM value */
+       cmu_rd(ctx, cmu_type, CMU_REG36, &val);
+       val = CMU_REG36_PLL_SSC_VSTEP_SET(val, 30);
+       val = CMU_REG36_PLL_SSC_EN_SET(val, 1);
+       val = CMU_REG36_PLL_SSC_DSMSEL_SET(val, 1);
+       cmu_wr(ctx, cmu_type, CMU_REG36, val);
+
+       /* Reset the PLL */
+       cmu_clrbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+       cmu_setbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+
+       /* Force VCO calibration to restart */
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+                      CMU_REG32_FORCE_VCOCAL_START_MASK);
+}
+
+static void xgene_phy_sata_cfg_lanes(struct xgene_phy_ctx *ctx)
+{
+       u32 val;
+       u32 reg;
+       int i;
+       int lane;
+
+       for (lane = 0; lane < MAX_LANE; lane++) {
+               serdes_wr(ctx, lane, RXTX_REG147, 0x6);
+
+               /* Set boost control for quarter, half, and full rate */
+               serdes_rd(ctx, lane, RXTX_REG0, &val);
+               val = RXTX_REG0_CTLE_EQ_HR_SET(val, 0x10);
+               val = RXTX_REG0_CTLE_EQ_QR_SET(val, 0x10);
+               val = RXTX_REG0_CTLE_EQ_FR_SET(val, 0x10);
+               serdes_wr(ctx, lane, RXTX_REG0, val);
+
+               /* Set boost control value */
+               serdes_rd(ctx, lane, RXTX_REG1, &val);
+               val = RXTX_REG1_RXACVCM_SET(val, 0x7);
+               val = RXTX_REG1_CTLE_EQ_SET(val,
+                       ctx->sata_param.txboostgain[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               serdes_wr(ctx, lane, RXTX_REG1, val);
+
+               /* Latch VTT value based on the termination to ground and
+                  enable TX FIFO */
+               serdes_rd(ctx, lane, RXTX_REG2, &val);
+               val = RXTX_REG2_VTT_ENA_SET(val, 0x1);
+               val = RXTX_REG2_VTT_SEL_SET(val, 0x1);
+               val = RXTX_REG2_TX_FIFO_ENA_SET(val, 0x1);
+               serdes_wr(ctx, lane, RXTX_REG2, val);
+
+               /* Configure Tx for 20-bits */
+               serdes_rd(ctx, lane, RXTX_REG4, &val);
+               val = RXTX_REG4_TX_WORD_MODE_SET(val, CMU_REG9_WORD_LEN_20BIT);
+               serdes_wr(ctx, lane, RXTX_REG4, val);
+
+               if (!preA3Chip) {
+                       serdes_rd(ctx, lane, RXTX_REG1, &val);
+                       val = RXTX_REG1_RXVREG1_SET(val, 0x2);
+                       val = RXTX_REG1_RXIREF_ADJ_SET(val, 0x2);
+                       serdes_wr(ctx, lane, RXTX_REG1, val);
+               }
+
+               /* Set pre-emphasis first 1 and 2, and post-emphasis values */
+               serdes_rd(ctx, lane, RXTX_REG5, &val);
+               val = RXTX_REG5_TX_CN1_SET(val,
+                       ctx->sata_param.txprecursor_cn1[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG5_TX_CP1_SET(val,
+                       ctx->sata_param.txpostcursor_cp1[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG5_TX_CN2_SET(val,
+                       ctx->sata_param.txprecursor_cn2[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               serdes_wr(ctx, lane, RXTX_REG5, val);
+
+               /* Set TX amplitude value */
+               serdes_rd(ctx, lane, RXTX_REG6, &val);
+               val = RXTX_REG6_TXAMP_CNTL_SET(val,
+                       ctx->sata_param.txamplitude[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG6_TXAMP_ENA_SET(val, 0x1);
+               val = RXTX_REG6_TX_IDLE_SET(val, 0x0);
+               val = RXTX_REG6_RX_BIST_RESYNC_SET(val, 0x0);
+               val = RXTX_REG6_RX_BIST_ERRCNT_RD_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG6, val);
+
+               /* Configure Rx for 20-bits */
+               serdes_rd(ctx, lane, RXTX_REG7, &val);
+               val = RXTX_REG7_BIST_ENA_RX_SET(val, 0x0);
+               val = RXTX_REG7_RX_WORD_MODE_SET(val, CMU_REG9_WORD_LEN_20BIT);
+               serdes_wr(ctx, lane, RXTX_REG7, val);
+
+               /* Set CDR and LOS values and enable Rx SSC */
+               serdes_rd(ctx, lane, RXTX_REG8, &val);
+               val = RXTX_REG8_CDR_LOOP_ENA_SET(val, 0x1);
+               val = RXTX_REG8_CDR_BYPASS_RXLOS_SET(val, 0x0);
+               val = RXTX_REG8_SSC_ENABLE_SET(val, 0x1);
+               val = RXTX_REG8_SD_DISABLE_SET(val, 0x0);
+               val = RXTX_REG8_SD_VREF_SET(val, 0x4);
+               serdes_wr(ctx, lane, RXTX_REG8, val);
+
+               /* Set phase adjust upper/lower limits */
+               serdes_rd(ctx, lane, RXTX_REG11, &val);
+               val = RXTX_REG11_PHASE_ADJUST_LIMIT_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG11, val);
+
+               /* Enable Latch Off; disable SUMOS and Tx termination */
+               serdes_rd(ctx, lane, RXTX_REG12, &val);
+               val = RXTX_REG12_LATCH_OFF_ENA_SET(val, 0x1);
+               val = RXTX_REG12_SUMOS_ENABLE_SET(val, 0x0);
+               val = RXTX_REG12_RX_DET_TERM_ENABLE_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG12, val);
+
+               /* Set period error latch to 512T and enable BWL */
+               serdes_rd(ctx, lane, RXTX_REG26, &val);
+               val = RXTX_REG26_PERIOD_ERROR_LATCH_SET(val, 0x0);
+               val = RXTX_REG26_BLWC_ENA_SET(val, 0x1);
+               serdes_wr(ctx, lane, RXTX_REG26, val);
+
+               serdes_wr(ctx, lane, RXTX_REG28, 0x0);
+
+               /* Set DFE loop preset value */
+               serdes_wr(ctx, lane, RXTX_REG31, 0x0);
+
+               /* Set Eye Monitor counter width to 12-bit */
+               serdes_rd(ctx, lane, RXTX_REG61, &val);
+               val = RXTX_REG61_ISCAN_INBERT_SET(val, 0x1);
+               val = RXTX_REG61_LOADFREQ_SHIFT_SET(val, 0x0);
+               val = RXTX_REG61_EYE_COUNT_WIDTH_SEL_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG61, val);
+
+               serdes_rd(ctx, lane, RXTX_REG62, &val);
+               val = RXTX_REG62_PERIOD_H1_QLATCH_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG62, val);
+
+               /* Set BW select tap X for DFE loop */
+               for (i = 0; i < 9; i++) {
+                       reg = RXTX_REG81 + i * 2;
+                       serdes_rd(ctx, lane, reg, &val);
+                       val = RXTX_REG89_MU_TH7_SET(val, 0xe);
+                       val = RXTX_REG89_MU_TH8_SET(val, 0xe);
+                       val = RXTX_REG89_MU_TH9_SET(val, 0xe);
+                       serdes_wr(ctx, lane, reg, val);
+               }
+
+               /* Set BW select tap X for frequency adjust loop */
+               for (i = 0; i < 3; i++) {
+                       reg = RXTX_REG96 + i * 2;
+                       serdes_rd(ctx, lane, reg, &val);
+                       val = RXTX_REG96_MU_FREQ1_SET(val, 0x10);
+                       val = RXTX_REG96_MU_FREQ2_SET(val, 0x10);
+                       val = RXTX_REG96_MU_FREQ3_SET(val, 0x10);
+                       serdes_wr(ctx, lane, reg, val);
+               }
+
+               /* Set BW select tap X for phase adjust loop */
+               for (i = 0; i < 3; i++) {
+                       reg = RXTX_REG99 + i * 2;
+                       serdes_rd(ctx, lane, reg, &val);
+                       val = RXTX_REG99_MU_PHASE1_SET(val, 0x7);
+                       val = RXTX_REG99_MU_PHASE2_SET(val, 0x7);
+                       val = RXTX_REG99_MU_PHASE3_SET(val, 0x7);
+                       serdes_wr(ctx, lane, reg, val);
+               }
+
+               serdes_rd(ctx, lane, RXTX_REG102, &val);
+               val = RXTX_REG102_FREQLOOP_LIMIT_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG102, val);
+
+               serdes_wr(ctx, lane, RXTX_REG114, 0xffe0);
+
+               serdes_rd(ctx, lane, RXTX_REG125, &val);
+               val = RXTX_REG125_SIGN_PQ_SET(val,
+                       ctx->sata_param.txeyedirection[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG125_PQ_REG_SET(val,
+                       ctx->sata_param.txeyetuning[lane * 3 +
+                       ctx->sata_param.speed[lane]]);
+               val = RXTX_REG125_PHZ_MANUAL_SET(val, 0x1);
+               serdes_wr(ctx, lane, RXTX_REG125, val);
+
+               serdes_rd(ctx, lane, RXTX_REG127, &val);
+               val = RXTX_REG127_LATCH_MAN_CAL_ENA_SET(val, 0x0);
+               serdes_wr(ctx, lane, RXTX_REG127, val);
+
+               serdes_rd(ctx, lane, RXTX_REG128, &val);
+               val = RXTX_REG128_LATCH_CAL_WAIT_SEL_SET(val, 0x3);
+               serdes_wr(ctx, lane, RXTX_REG128, val);
+
+               serdes_rd(ctx, lane, RXTX_REG145, &val);
+               val = RXTX_REG145_RXDFE_CONFIG_SET(val, 0x3);
+               val = RXTX_REG145_TX_IDLE_SATA_SET(val, 0x0);
+               if (preA3Chip) {
+                       val = RXTX_REG145_RXES_ENA_SET(val, 0x1);
+                       val = RXTX_REG145_RXVWES_LATENA_SET(val, 0x1);
+               } else {
+                       val = RXTX_REG145_RXES_ENA_SET(val, 0x0);
+                       val = RXTX_REG145_RXVWES_LATENA_SET(val, 0x0);
+               }
+               serdes_wr(ctx, lane, RXTX_REG145, val);
+
+               /*
+                * Set Rx LOS filter clock rate, sample rate, and threshold
+                * windows
+                */
+               for (i = 0; i < 4; i++) {
+                       reg = RXTX_REG148 + i * 2;
+                       serdes_wr(ctx, lane, reg, 0xFFFF);
+               }
+       }
+}
+
+static int xgene_phy_cal_rdy_chk(struct xgene_phy_ctx *ctx,
+                                enum cmu_type_t cmu_type,
+                                enum clk_type_t clk_type)
+{
+       void __iomem *csr_serdes = ctx->sds_base;
+       int loop;
+       u32 val;
+
+       /* Release PHY main reset */
+       writel(0xdf, csr_serdes + SATA_ENET_SDS_RST_CTL);
+       readl(csr_serdes + SATA_ENET_SDS_RST_CTL); /* Force a barrier */
+
+       if (cmu_type != REF_CMU) {
+               cmu_setbits(ctx, cmu_type, CMU_REG5, CMU_REG5_PLL_RESETB_MASK);
+               /*
+                * As per PHY design spec, the PLL reset requires a minimum
+                * of 800us.
+                */
+               usleep_range(800, 1000);
+
+               cmu_rd(ctx, cmu_type, CMU_REG1, &val);
+               val = CMU_REG1_PLL_MANUALCAL_SET(val, 0x0);
+               cmu_wr(ctx, cmu_type, CMU_REG1, val);
+               /*
+                * As per PHY design spec, the PLL auto calibration requires
+                * a minimum of 800us.
+                */
+               usleep_range(800, 1000);
+
+               cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+                              CMU_REG32_FORCE_VCOCAL_START_MASK);
+               /*
+                * As per PHY design spec, the PLL requires a minimum of
+                * 800us to settle.
+                */
+               usleep_range(800, 1000);
+       }
+
+       if (!preA3Chip)
+               goto skip_manual_cal;
+
+       /*
+        * Configure the termination resister calibration
+        * The serial receive pins, RXP/RXN, have TERMination resistor
+        * that is required to be calibrated.
+        */
+       cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+       val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x12);
+       val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG17, val);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG17,
+                      CMU_REG17_PVT_TERM_MAN_ENA_MASK);
+       /*
+        * The serial transmit pins, TXP/TXN, have Pull-UP and Pull-DOWN
+        * resistors that are required to the calibrated.
+        * Configure the pull DOWN calibration
+        */
+       cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+       val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x29);
+       val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG17, val);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG16,
+                      CMU_REG16_PVT_DN_MAN_ENA_MASK);
+       /* Configure the pull UP calibration */
+       cmu_rd(ctx, cmu_type, CMU_REG17, &val);
+       val = CMU_REG17_PVT_CODE_R2A_SET(val, 0x28);
+       val = CMU_REG17_RESERVED_7_SET(val, 0x0);
+       cmu_wr(ctx, cmu_type, CMU_REG17, val);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG16,
+                      CMU_REG16_PVT_UP_MAN_ENA_MASK);
+
+skip_manual_cal:
+       /* Poll the PLL calibration completion status for at least 1 ms */
+       loop = 100;
+       do {
+               cmu_rd(ctx, cmu_type, CMU_REG7, &val);
+               if (CMU_REG7_PLL_CALIB_DONE_RD(val))
+                       break;
+               /*
+                * As per PHY design spec, PLL calibration status requires
+                * a minimum of 10us to be updated.
+                */
+               usleep_range(10, 100);
+       } while (--loop > 0);
+
+       cmu_rd(ctx, cmu_type, CMU_REG7, &val);
+       dev_dbg(ctx->dev, "PLL calibration %s\n",
+               CMU_REG7_PLL_CALIB_DONE_RD(val) ? "done" : "failed");
+       if (CMU_REG7_VCO_CAL_FAIL_RD(val)) {
+               dev_err(ctx->dev,
+                       "PLL calibration failed due to VCO failure\n");
+               return -1;
+       }
+       dev_dbg(ctx->dev, "PLL calibration successful\n");
+
+       cmu_rd(ctx, cmu_type, CMU_REG15, &val);
+       dev_dbg(ctx->dev, "PHY Tx is %sready\n", val & 0x300 ? "" : "not ");
+       return 0;
+}
+
+static void xgene_phy_pdwn_force_vco(struct xgene_phy_ctx *ctx,
+                                    enum cmu_type_t cmu_type,
+                                    enum clk_type_t clk_type)
+{
+       u32 val;
+
+       dev_dbg(ctx->dev, "Reset VCO and re-start again\n");
+       if (cmu_type == PHY_CMU) {
+               cmu_rd(ctx, cmu_type, CMU_REG16, &val);
+               val = CMU_REG16_VCOCAL_WAIT_BTW_CODE_SET(val, 0x7);
+               cmu_wr(ctx, cmu_type, CMU_REG16, val);
+       }
+
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG0, CMU_REG0_PDOWN_MASK);
+       cmu_toggle1to0(ctx, cmu_type, CMU_REG32,
+                      CMU_REG32_FORCE_VCOCAL_START_MASK);
+}
+
+static int xgene_phy_hw_init_sata(struct xgene_phy_ctx *ctx,
+                                 enum clk_type_t clk_type, int ssc_enable)
+{
+       void __iomem *sds_base = ctx->sds_base;
+       u32 val;
+       int i;
+
+       /* Configure the PHY for operation */
+       dev_dbg(ctx->dev, "Reset PHY\n");
+       /* Place PHY into reset */
+       writel(0x0, sds_base + SATA_ENET_SDS_RST_CTL);
+       val = readl(sds_base + SATA_ENET_SDS_RST_CTL);  /* Force a barrier */
+       /* Release PHY lane from reset (active high) */
+       writel(0x20, sds_base + SATA_ENET_SDS_RST_CTL);
+       readl(sds_base + SATA_ENET_SDS_RST_CTL);        /* Force a barrier */
+       /* Release all PHY module out of reset except PHY main reset */
+       writel(0xde, sds_base + SATA_ENET_SDS_RST_CTL);
+       readl(sds_base + SATA_ENET_SDS_RST_CTL);        /* Force a barrier */
+
+       /* Set the operation speed */
+       val = readl(sds_base + SATA_ENET_SDS_CTL1);
+       val = CFG_I_SPD_SEL_CDR_OVR1_SET(val,
+               ctx->sata_param.txspeed[ctx->sata_param.speed[0]]);
+       writel(val, sds_base + SATA_ENET_SDS_CTL1);
+
+       dev_dbg(ctx->dev, "Set the customer pin mode to SATA\n");
+       val = readl(sds_base + SATA_ENET_SDS_CTL0);
+       val = REGSPEC_CFG_I_CUSTOMER_PIN_MODE0_SET(val, 0x4421);
+       writel(val, sds_base + SATA_ENET_SDS_CTL0);
+
+       /* Configure the clock macro unit (CMU) clock type */
+       xgene_phy_cfg_cmu_clk_type(ctx, PHY_CMU, clk_type);
+
+       /* Configure the clock macro */
+       xgene_phy_sata_cfg_cmu_core(ctx, PHY_CMU, clk_type);
+
+       /* Enable SSC if enabled */
+       if (ssc_enable)
+               xgene_phy_ssc_enable(ctx, PHY_CMU);
+
+       /* Configure PHY lanes */
+       xgene_phy_sata_cfg_lanes(ctx);
+
+       /* Set Rx/Tx 20-bit */
+       val = readl(sds_base + SATA_ENET_SDS_PCS_CTL0);
+       val = REGSPEC_CFG_I_RX_WORDMODE0_SET(val, 0x3);
+       val = REGSPEC_CFG_I_TX_WORDMODE0_SET(val, 0x3);
+       writel(val, sds_base + SATA_ENET_SDS_PCS_CTL0);
+
+       /* Start PLL calibration and try for three times */
+       i = 10;
+       do {
+               if (!xgene_phy_cal_rdy_chk(ctx, PHY_CMU, clk_type))
+                       break;
+               /* If failed, toggle the VCO power signal and start again */
+               xgene_phy_pdwn_force_vco(ctx, PHY_CMU, clk_type);
+       } while (--i > 0);
+       /* Even on failure, allow to continue any way */
+       if (i <= 0)
+               dev_err(ctx->dev, "PLL calibration failed\n");
+
+       return 0;
+}
+
+static int xgene_phy_hw_initialize(struct xgene_phy_ctx *ctx,
+                                  enum clk_type_t clk_type,
+                                  int ssc_enable)
+{
+       int rc;
+
+       dev_dbg(ctx->dev, "PHY init clk type %d\n", clk_type);
+
+       if (ctx->mode == MODE_SATA) {
+               rc = xgene_phy_hw_init_sata(ctx, clk_type, ssc_enable);
+               if (rc)
+                       return rc;
+       } else {
+               dev_err(ctx->dev, "Un-supported customer pin mode %d\n",
+                       ctx->mode);
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/*
+ * Receiver Offset Calibration:
+ *
+ * Calibrate the receiver signal path offset in two steps - summar and
+ * latch calibrations
+ */
+static void xgene_phy_force_lat_summer_cal(struct xgene_phy_ctx *ctx, int lane)
+{
+       int i;
+       struct {
+               u32 reg;
+               u32 val;
+       } serdes_reg[] = {
+               {RXTX_REG38, 0x0},
+               {RXTX_REG39, 0xff00},
+               {RXTX_REG40, 0xffff},
+               {RXTX_REG41, 0xffff},
+               {RXTX_REG42, 0xffff},
+               {RXTX_REG43, 0xffff},
+               {RXTX_REG44, 0xffff},
+               {RXTX_REG45, 0xffff},
+               {RXTX_REG46, 0xffff},
+               {RXTX_REG47, 0xfffc},
+               {RXTX_REG48, 0x0},
+               {RXTX_REG49, 0x0},
+               {RXTX_REG50, 0x0},
+               {RXTX_REG51, 0x0},
+               {RXTX_REG52, 0x0},
+               {RXTX_REG53, 0x0},
+               {RXTX_REG54, 0x0},
+               {RXTX_REG55, 0x0},
+       };
+
+       /* Start SUMMER calibration */
+       serdes_setbits(ctx, lane, RXTX_REG127,
+                      RXTX_REG127_FORCE_SUM_CAL_START_MASK);
+       /*
+        * As per PHY design spec, the Summer calibration requires a minimum
+        * of 100us to complete.
+        */
+       usleep_range(100, 500);
+       serdes_clrbits(ctx, lane, RXTX_REG127,
+                       RXTX_REG127_FORCE_SUM_CAL_START_MASK);
+       /*
+        * As per PHY design spec, the auto calibration requires a minimum
+        * of 100us to complete.
+        */
+       usleep_range(100, 500);
+
+       /* Start latch calibration */
+       serdes_setbits(ctx, lane, RXTX_REG127,
+                      RXTX_REG127_FORCE_LAT_CAL_START_MASK);
+       /*
+        * As per PHY design spec, the latch calibration requires a minimum
+        * of 100us to complete.
+        */
+       usleep_range(100, 500);
+       serdes_clrbits(ctx, lane, RXTX_REG127,
+                      RXTX_REG127_FORCE_LAT_CAL_START_MASK);
+
+       /* Configure the PHY lane for calibration */
+       serdes_wr(ctx, lane, RXTX_REG28, 0x7);
+       serdes_wr(ctx, lane, RXTX_REG31, 0x7e00);
+       serdes_clrbits(ctx, lane, RXTX_REG4,
+                      RXTX_REG4_TX_LOOPBACK_BUF_EN_MASK);
+       serdes_clrbits(ctx, lane, RXTX_REG7,
+                      RXTX_REG7_LOOP_BACK_ENA_CTLE_MASK);
+       for (i = 0; i < ARRAY_SIZE(serdes_reg); i++)
+               serdes_wr(ctx, lane, serdes_reg[i].reg,
+                         serdes_reg[i].val);
+}
+
+static void xgene_phy_reset_rxd(struct xgene_phy_ctx *ctx, int lane)
+{
+       /* Reset digital Rx */
+       serdes_clrbits(ctx, lane, RXTX_REG7, RXTX_REG7_RESETB_RXD_MASK);
+       /* As per PHY design spec, the reset requires a minimum of 100us. */
+       usleep_range(100, 150);
+       serdes_setbits(ctx, lane, RXTX_REG7, RXTX_REG7_RESETB_RXD_MASK);
+}
+
+static int xgene_phy_get_avg(int accum, int samples)
+{
+       return (accum + (samples / 2)) / samples;
+}
+
+static void xgene_phy_gen_avg_val(struct xgene_phy_ctx *ctx, int lane)
+{
+       int max_loop = 10;
+       int avg_loop = 0;
+       int lat_do = 0, lat_xo = 0, lat_eo = 0, lat_so = 0;
+       int lat_de = 0, lat_xe = 0, lat_ee = 0, lat_se = 0;
+       int sum_cal = 0;
+       int lat_do_itr, lat_xo_itr, lat_eo_itr, lat_so_itr;
+       int lat_de_itr, lat_xe_itr, lat_ee_itr, lat_se_itr;
+       int sum_cal_itr;
+       int fail_even;
+       int fail_odd;
+       u32 val;
+
+       dev_dbg(ctx->dev, "Generating avg calibration value for lane %d\n",
+               lane);
+
+       /* Enable RX Hi-Z termination */
+       serdes_setbits(ctx, lane, RXTX_REG12,
+                       RXTX_REG12_RX_DET_TERM_ENABLE_MASK);
+       /* Turn off DFE */
+       serdes_wr(ctx, lane, RXTX_REG28, 0x0000);
+       /* DFE Presets to zero */
+       serdes_wr(ctx, lane, RXTX_REG31, 0x0000);
+
+       /*
+        * Receiver Offset Calibration:
+        * Calibrate the receiver signal path offset in two steps - summar
+        * and latch calibration.
+        * Runs the "Receiver Offset Calibration multiple times to determine
+        * the average value to use.
+        */
+       while (avg_loop < max_loop) {
+               /* Start the calibration */
+               xgene_phy_force_lat_summer_cal(ctx, lane);
+
+               serdes_rd(ctx, lane, RXTX_REG21, &val);
+               lat_do_itr = RXTX_REG21_DO_LATCH_CALOUT_RD(val);
+               lat_xo_itr = RXTX_REG21_XO_LATCH_CALOUT_RD(val);
+               fail_odd = RXTX_REG21_LATCH_CAL_FAIL_ODD_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG22, &val);
+               lat_eo_itr = RXTX_REG22_EO_LATCH_CALOUT_RD(val);
+               lat_so_itr = RXTX_REG22_SO_LATCH_CALOUT_RD(val);
+               fail_even = RXTX_REG22_LATCH_CAL_FAIL_EVEN_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG23, &val);
+               lat_de_itr = RXTX_REG23_DE_LATCH_CALOUT_RD(val);
+               lat_xe_itr = RXTX_REG23_XE_LATCH_CALOUT_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG24, &val);
+               lat_ee_itr = RXTX_REG24_EE_LATCH_CALOUT_RD(val);
+               lat_se_itr = RXTX_REG24_SE_LATCH_CALOUT_RD(val);
+
+               serdes_rd(ctx, lane, RXTX_REG121, &val);
+               sum_cal_itr = RXTX_REG121_SUMOS_CAL_CODE_RD(val);
+
+               /* Check for failure. If passed, sum them for averaging */
+               if ((fail_even == 0 || fail_even == 1) &&
+                   (fail_odd == 0 || fail_odd == 1)) {
+                       lat_do += lat_do_itr;
+                       lat_xo += lat_xo_itr;
+                       lat_eo += lat_eo_itr;
+                       lat_so += lat_so_itr;
+                       lat_de += lat_de_itr;
+                       lat_xe += lat_xe_itr;
+                       lat_ee += lat_ee_itr;
+                       lat_se += lat_se_itr;
+                       sum_cal += sum_cal_itr;
+
+                       dev_dbg(ctx->dev, "Iteration %d:\n", avg_loop);
+                       dev_dbg(ctx->dev, "DO 0x%x XO 0x%x EO 0x%x SO 0x%x\n",
+                               lat_do_itr, lat_xo_itr, lat_eo_itr,
+                               lat_so_itr);
+                       dev_dbg(ctx->dev, "DE 0x%x XE 0x%x EE 0x%x SE 0x%x\n",
+                               lat_de_itr, lat_xe_itr, lat_ee_itr,
+                               lat_se_itr);
+                       dev_dbg(ctx->dev, "SUM 0x%x\n", sum_cal_itr);
+                       ++avg_loop;
+               } else {
+                       dev_err(ctx->dev,
+                               "Receiver calibration failed at %d loop\n",
+                               avg_loop);
+               }
+               xgene_phy_reset_rxd(ctx, lane);
+       }
+
+       /* Update latch manual calibration with average value */
+       serdes_rd(ctx, lane, RXTX_REG127, &val);
+       val = RXTX_REG127_DO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_do, max_loop));
+       val = RXTX_REG127_XO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_xo, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG127, val);
+
+       serdes_rd(ctx, lane, RXTX_REG128, &val);
+       val = RXTX_REG128_EO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_eo, max_loop));
+       val = RXTX_REG128_SO_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_so, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG128, val);
+
+       serdes_rd(ctx, lane, RXTX_REG129, &val);
+       val = RXTX_REG129_DE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_de, max_loop));
+       val = RXTX_REG129_XE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_xe, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG129, val);
+
+       serdes_rd(ctx, lane, RXTX_REG130, &val);
+       val = RXTX_REG130_EE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_ee, max_loop));
+       val = RXTX_REG130_SE_LATCH_MANCAL_SET(val,
+               xgene_phy_get_avg(lat_se, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG130, val);
+
+       /* Update SUMMER calibration with average value */
+       serdes_rd(ctx, lane, RXTX_REG14, &val);
+       val = RXTX_REG14_CLTE_LATCAL_MAN_PROG_SET(val,
+               xgene_phy_get_avg(sum_cal, max_loop));
+       serdes_wr(ctx, lane, RXTX_REG14, val);
+
+       dev_dbg(ctx->dev, "Average Value:\n");
+       dev_dbg(ctx->dev, "DO 0x%x XO 0x%x EO 0x%x SO 0x%x\n",
+                xgene_phy_get_avg(lat_do, max_loop),
+                xgene_phy_get_avg(lat_xo, max_loop),
+                xgene_phy_get_avg(lat_eo, max_loop),
+                xgene_phy_get_avg(lat_so, max_loop));
+       dev_dbg(ctx->dev, "DE 0x%x XE 0x%x EE 0x%x SE 0x%x\n",
+                xgene_phy_get_avg(lat_de, max_loop),
+                xgene_phy_get_avg(lat_xe, max_loop),
+                xgene_phy_get_avg(lat_ee, max_loop),
+                xgene_phy_get_avg(lat_se, max_loop));
+       dev_dbg(ctx->dev, "SUM 0x%x\n",
+               xgene_phy_get_avg(sum_cal, max_loop));
+
+       serdes_rd(ctx, lane, RXTX_REG14, &val);
+       val = RXTX_REG14_CTLE_LATCAL_MAN_ENA_SET(val, 0x1);
+       serdes_wr(ctx, lane, RXTX_REG14, val);
+       dev_dbg(ctx->dev, "Enable Manual Summer calibration\n");
+
+       serdes_rd(ctx, lane, RXTX_REG127, &val);
+       val = RXTX_REG127_LATCH_MAN_CAL_ENA_SET(val, 0x1);
+       dev_dbg(ctx->dev, "Enable Manual Latch calibration\n");
+       serdes_wr(ctx, lane, RXTX_REG127, val);
+
+       /* Disable RX Hi-Z termination */
+       serdes_rd(ctx, lane, RXTX_REG12, &val);
+       val = RXTX_REG12_RX_DET_TERM_ENABLE_SET(val, 0);
+       serdes_wr(ctx, lane, RXTX_REG12, val);
+       /* Turn on DFE */
+       serdes_wr(ctx, lane, RXTX_REG28, 0x0007);
+       /* Set DFE preset */
+       serdes_wr(ctx, lane, RXTX_REG31, 0x7e00);
+}
+
+static int xgene_phy_hw_init(struct phy *phy)
+{
+       struct xgene_phy_ctx *ctx = phy_get_drvdata(phy);
+       int rc;
+       int i;
+
+       rc = xgene_phy_hw_initialize(ctx, CLK_EXT_DIFF, SSC_DISABLE);
+       if (rc) {
+               dev_err(ctx->dev, "PHY initialize failed %d\n", rc);
+               return rc;
+       }
+
+       /* Setup clock properly after PHY configuration */
+       if (!IS_ERR(ctx->clk)) {
+               /* HW requires an toggle of the clock */
+               clk_prepare_enable(ctx->clk);
+               clk_disable_unprepare(ctx->clk);
+               clk_prepare_enable(ctx->clk);
+       }
+
+       /* Compute average value */
+       for (i = 0; i < MAX_LANE; i++)
+               xgene_phy_gen_avg_val(ctx, i);
+
+       dev_dbg(ctx->dev, "PHY initialized\n");
+       return 0;
+}
+
+static const struct phy_ops xgene_phy_ops = {
+       .init           = xgene_phy_hw_init,
+       .owner          = THIS_MODULE,
+};
+
+static struct phy *xgene_phy_xlate(struct device *dev,
+                                  struct of_phandle_args *args)
+{
+       struct xgene_phy_ctx *ctx = dev_get_drvdata(dev);
+
+       if (args->args_count <= 0)
+               return ERR_PTR(-EINVAL);
+       if (args->args[0] < MODE_SATA || args->args[0] >= MODE_MAX)
+               return ERR_PTR(-EINVAL);
+
+       ctx->mode = args->args[0];
+       return ctx->phy;
+}
+
+static void xgene_phy_get_param(struct platform_device *pdev,
+                               const char *name, u32 *buffer,
+                               int count, u32 *default_val,
+                               u32 conv_factor)
+{
+       int i;
+
+       if (!of_property_read_u32_array(pdev->dev.of_node, name, buffer,
+                                       count)) {
+               for (i = 0; i < count; i++)
+                       buffer[i] /= conv_factor;
+               return;
+       }
+       /* Does not exist, load default */
+       for (i = 0; i < count; i++)
+               buffer[i] = default_val[i % 3];
+}
+
+static int xgene_phy_probe(struct platform_device *pdev)
+{
+       struct phy_provider *phy_provider;
+       struct xgene_phy_ctx *ctx;
+       struct resource *res;
+       int rc = 0;
+       u32 default_spd[] = DEFAULT_SATA_SPD_SEL;
+       u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN;
+       u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION;
+       u32 default_txeye_tuning[] = DEFAULT_SATA_TXEYETUNING;
+       u32 default_txamp[] = DEFAULT_SATA_TXAMP;
+       u32 default_txcn1[] = DEFAULT_SATA_TXCN1;
+       u32 default_txcn2[] = DEFAULT_SATA_TXCN2;
+       u32 default_txcp1[] = DEFAULT_SATA_TXCP1;
+       int i;
+
+       ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return -ENOMEM;
+
+       ctx->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       ctx->sds_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(ctx->sds_base)) {
+               rc = PTR_ERR(ctx->sds_base);
+               goto error;
+       }
+
+       /* Retrieve optional clock */
+       ctx->clk = clk_get(&pdev->dev, NULL);
+
+       /* Load override paramaters */
+       xgene_phy_get_param(pdev, "apm,tx-eye-tuning",
+               ctx->sata_param.txeyetuning, 6, default_txeye_tuning, 1);
+       xgene_phy_get_param(pdev, "apm,tx-eye-direction",
+               ctx->sata_param.txeyedirection, 6, default_txeye_direction, 1);
+       xgene_phy_get_param(pdev, "apm,tx-boost-gain",
+               ctx->sata_param.txboostgain, 6, default_txboost_gain, 1);
+       xgene_phy_get_param(pdev, "apm,tx-amplitude",
+               ctx->sata_param.txamplitude, 6, default_txamp, 13300);
+       xgene_phy_get_param(pdev, "apm,tx-pre-cursor1",
+               ctx->sata_param.txprecursor_cn1, 6, default_txcn1, 18200);
+       xgene_phy_get_param(pdev, "apm,tx-pre-cursor2",
+               ctx->sata_param.txprecursor_cn2, 6, default_txcn2, 18200);
+       xgene_phy_get_param(pdev, "apm,tx-post-cursor",
+               ctx->sata_param.txpostcursor_cp1, 6, default_txcp1, 18200);
+       xgene_phy_get_param(pdev, "apm,tx-speed",
+               ctx->sata_param.txspeed, 3, default_spd, 1);
+       for (i = 0; i < MAX_LANE; i++)
+               ctx->sata_param.speed[i] = 2; /* Default to Gen3 */
+
+       ctx->dev = &pdev->dev;
+       platform_set_drvdata(pdev, ctx);
+
+       ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL);
+       if (IS_ERR(ctx->phy)) {
+               dev_dbg(&pdev->dev, "Failed to create PHY\n");
+               rc = PTR_ERR(ctx->phy);
+               goto error;
+       }
+       phy_set_drvdata(ctx->phy, ctx);
+
+       phy_provider = devm_of_phy_provider_register(ctx->dev,
+                                                    xgene_phy_xlate);
+       if (IS_ERR(phy_provider)) {
+               rc = PTR_ERR(phy_provider);
+               goto error;
+       }
+
+       return 0;
+
+error:
+       return rc;
+}
+
+static const struct of_device_id xgene_phy_of_match[] = {
+       {.compatible = "apm,xgene-phy",},
+       {},
+};
+MODULE_DEVICE_TABLE(of, xgene_phy_of_match);
+
+static struct platform_driver xgene_phy_driver = {
+       .probe = xgene_phy_probe,
+       .driver = {
+                  .name = "xgene-phy",
+                  .of_match_table = xgene_phy_of_match,
+       },
+};
+module_platform_driver(xgene_phy_driver);
+
+MODULE_DESCRIPTION("APM X-Gene Multi-Purpose PHY driver");
+MODULE_AUTHOR("Loc Ho <lho@apm.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1");
diff --git a/include/linux/phy/omap_control_phy.h b/include/linux/phy/omap_control_phy.h
new file mode 100644 (file)
index 0000000..e9e6cfb
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * omap_control_phy.h - Header file for the PHY part of control module.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __OMAP_CONTROL_PHY_H__
+#define __OMAP_CONTROL_PHY_H__
+
+enum omap_control_phy_type {
+       OMAP_CTRL_TYPE_OTGHS = 1,       /* Mailbox OTGHS_CONTROL */
+       OMAP_CTRL_TYPE_USB2,    /* USB2_PHY, power down in CONTROL_DEV_CONF */
+       OMAP_CTRL_TYPE_PIPE3,   /* PIPE3 PHY, DPLL & seperate Rx/Tx power */
+       OMAP_CTRL_TYPE_PCIE,    /* RX TX control of ACSPCIE */
+       OMAP_CTRL_TYPE_DRA7USB2, /* USB2 PHY, power and power_aux e.g. DRA7 */
+       OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
+};
+
+struct omap_control_phy {
+       struct device *dev;
+
+       u32 __iomem *otghs_control;
+       u32 __iomem *power;
+       u32 __iomem *power_aux;
+       u32 __iomem *pcie_pcs;
+
+       struct clk *sys_clk;
+
+       enum omap_control_phy_type type;
+};
+
+enum omap_control_usb_mode {
+       USB_MODE_UNDEFINED = 0,
+       USB_MODE_HOST,
+       USB_MODE_DEVICE,
+       USB_MODE_DISCONNECT,
+};
+
+#define        OMAP_CTRL_DEV_PHY_PD            BIT(0)
+
+#define        OMAP_CTRL_DEV_AVALID            BIT(0)
+#define        OMAP_CTRL_DEV_BVALID            BIT(1)
+#define        OMAP_CTRL_DEV_VBUSVALID         BIT(2)
+#define        OMAP_CTRL_DEV_SESSEND           BIT(3)
+#define        OMAP_CTRL_DEV_IDDIG             BIT(4)
+
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK         0x003FC000
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT        0xE
+
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK        0xFFC00000
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT       0x16
+
+#define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON       0x3
+#define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
+
+#define        OMAP_CTRL_PCIE_PCS_MASK                 0xff
+#define        OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT    0x8
+
+#define OMAP_CTRL_USB2_PHY_PD          BIT(28)
+
+#define AM437X_CTRL_USB2_PHY_PD                BIT(0)
+#define AM437X_CTRL_USB2_OTG_PD                BIT(1)
+#define AM437X_CTRL_USB2_OTGVDET_EN    BIT(19)
+#define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20)
+
+#if IS_ENABLED(CONFIG_OMAP_CONTROL_PHY)
+void omap_control_phy_power(struct device *dev, int on);
+void omap_control_usb_set_mode(struct device *dev,
+                              enum omap_control_usb_mode mode);
+void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay);
+#else
+
+static inline void omap_control_phy_power(struct device *dev, int on)
+{
+}
+
+static inline void omap_control_usb_set_mode(struct device *dev,
+       enum omap_control_usb_mode mode)
+{
+}
+
+static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+{
+}
+#endif
+
+#endif /* __OMAP_CONTROL_PHY_H__ */
diff --git a/include/linux/phy/omap_usb.h b/include/linux/phy/omap_usb.h
new file mode 100644 (file)
index 0000000..dc2c541
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * omap_usb.h -- omap usb2 phy header file
+ *
+ * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __DRIVERS_OMAP_USB2_H
+#define __DRIVERS_OMAP_USB2_H
+
+#include <linux/io.h>
+#include <linux/usb/otg.h>
+
+struct usb_dpll_params {
+       u16     m;
+       u8      n;
+       u8      freq:3;
+       u8      sd;
+       u32     mf;
+};
+
+struct omap_usb {
+       struct usb_phy          phy;
+       struct phy_companion    *comparator;
+       void __iomem            *pll_ctrl_base;
+       void __iomem            *phy_base;
+       struct device           *dev;
+       struct device           *control_dev;
+       struct clk              *wkupclk;
+       struct clk              *optclk;
+       u8                      flags;
+};
+
+struct usb_phy_data {
+       const char *label;
+       u8 flags;
+};
+
+/* Driver Flags */
+#define OMAP_USB2_HAS_START_SRP (1 << 0)
+#define OMAP_USB2_HAS_SET_VBUS (1 << 1)
+#define OMAP_USB2_CALIBRATE_FALSE_DISCONNECT (1 << 2)
+
+#define        phy_to_omapusb(x)       container_of((x), struct omap_usb, phy)
+
+#if defined(CONFIG_OMAP_USB2) || defined(CONFIG_OMAP_USB2_MODULE)
+extern int omap_usb2_set_comparator(struct phy_companion *comparator);
+#else
+static inline int omap_usb2_set_comparator(struct phy_companion *comparator)
+{
+       return -ENODEV;
+}
+#endif
+
+static inline u32 omap_usb_readl(void __iomem *addr, unsigned offset)
+{
+       return __raw_readl(addr + offset);
+}
+
+static inline void omap_usb_writel(void __iomem *addr, unsigned offset,
+       u32 data)
+{
+       __raw_writel(data, addr + offset);
+}
+
+#endif /* __DRIVERS_OMAP_USB_H */
diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h
new file mode 100644 (file)
index 0000000..8cb6f81
--- /dev/null
@@ -0,0 +1,350 @@
+/*
+ * phy.h -- generic phy header file
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: Kishon Vijay Abraham I <kishon@ti.com>
+ *
+ * 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.
+ */
+
+#ifndef __DRIVERS_PHY_H
+#define __DRIVERS_PHY_H
+
+#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
+
+struct phy;
+
+/**
+ * struct phy_ops - set of function pointers for performing phy operations
+ * @init: operation to be performed for initializing phy
+ * @exit: operation to be performed while exiting
+ * @power_on: powering on the phy
+ * @power_off: powering off the phy
+ * @owner: the module owner containing the ops
+ */
+struct phy_ops {
+       int     (*init)(struct phy *phy);
+       int     (*exit)(struct phy *phy);
+       int     (*power_on)(struct phy *phy);
+       int     (*power_off)(struct phy *phy);
+       struct module *owner;
+};
+
+/**
+ * struct phy_attrs - represents phy attributes
+ * @bus_width: Data path width implemented by PHY
+ */
+struct phy_attrs {
+       u32                     bus_width;
+};
+
+/**
+ * struct phy - represents the phy device
+ * @dev: phy device
+ * @id: id of the phy device
+ * @ops: function pointers for performing phy operations
+ * @init_data: list of PHY consumers (non-dt only)
+ * @mutex: mutex to protect phy_ops
+ * @init_count: used to protect when the PHY is used by multiple consumers
+ * @power_count: used to protect when the PHY is used by multiple consumers
+ * @phy_attrs: used to specify PHY specific attributes
+ */
+struct phy {
+       struct device           dev;
+       int                     id;
+       const struct phy_ops    *ops;
+       struct phy_init_data    *init_data;
+       struct mutex            mutex;
+       int                     init_count;
+       int                     power_count;
+       struct phy_attrs        attrs;
+       struct regulator        *pwr;
+};
+
+/**
+ * struct phy_provider - represents the phy provider
+ * @dev: phy provider device
+ * @owner: the module owner having of_xlate
+ * @of_xlate: function pointer to obtain phy instance from phy pointer
+ * @list: to maintain a linked list of PHY providers
+ */
+struct phy_provider {
+       struct device           *dev;
+       struct module           *owner;
+       struct list_head        list;
+       struct phy * (*of_xlate)(struct device *dev,
+               struct of_phandle_args *args);
+};
+
+/**
+ * struct phy_consumer - represents the phy consumer
+ * @dev_name: the device name of the controller that will use this PHY device
+ * @port: name given to the consumer port
+ */
+struct phy_consumer {
+       const char *dev_name;
+       const char *port;
+};
+
+/**
+ * struct phy_init_data - contains the list of PHY consumers
+ * @num_consumers: number of consumers for this PHY device
+ * @consumers: list of PHY consumers
+ */
+struct phy_init_data {
+       unsigned int num_consumers;
+       struct phy_consumer *consumers;
+};
+
+#define PHY_CONSUMER(_dev_name, _port)                         \
+{                                                              \
+       .dev_name       = _dev_name,                            \
+       .port           = _port,                                \
+}
+
+#define        to_phy(dev)     (container_of((dev), struct phy, dev))
+
+#define        of_phy_provider_register(dev, xlate)    \
+       __of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+#define        devm_of_phy_provider_register(dev, xlate)       \
+       __devm_of_phy_provider_register((dev), THIS_MODULE, (xlate))
+
+static inline void phy_set_drvdata(struct phy *phy, void *data)
+{
+       dev_set_drvdata(&phy->dev, data);
+}
+
+static inline void *phy_get_drvdata(struct phy *phy)
+{
+       return dev_get_drvdata(&phy->dev);
+}
+
+#if IS_ENABLED(CONFIG_GENERIC_PHY)
+int phy_pm_runtime_get(struct phy *phy);
+int phy_pm_runtime_get_sync(struct phy *phy);
+int phy_pm_runtime_put(struct phy *phy);
+int phy_pm_runtime_put_sync(struct phy *phy);
+void phy_pm_runtime_allow(struct phy *phy);
+void phy_pm_runtime_forbid(struct phy *phy);
+int phy_init(struct phy *phy);
+int phy_exit(struct phy *phy);
+int phy_power_on(struct phy *phy);
+int phy_power_off(struct phy *phy);
+static inline int phy_get_bus_width(struct phy *phy)
+{
+       return phy->attrs.bus_width;
+}
+static inline void phy_set_bus_width(struct phy *phy, int bus_width)
+{
+       phy->attrs.bus_width = bus_width;
+}
+struct phy *phy_get(struct device *dev, const char *string);
+struct phy *phy_optional_get(struct device *dev, const char *string);
+struct phy *devm_phy_get(struct device *dev, const char *string);
+struct phy *devm_phy_optional_get(struct device *dev, const char *string);
+struct phy *devm_of_phy_get(struct device *dev, struct device_node *np,
+                           const char *con_id);
+void phy_put(struct phy *phy);
+void devm_phy_put(struct device *dev, struct phy *phy);
+struct phy *of_phy_get(struct device_node *np, const char *con_id);
+struct phy *of_phy_simple_xlate(struct device *dev,
+       struct of_phandle_args *args);
+struct phy *phy_create(struct device *dev, struct device_node *node,
+                      const struct phy_ops *ops,
+                      struct phy_init_data *init_data);
+struct phy *devm_phy_create(struct device *dev, struct device_node *node,
+       const struct phy_ops *ops, struct phy_init_data *init_data);
+void phy_destroy(struct phy *phy);
+void devm_phy_destroy(struct device *dev, struct phy *phy);
+struct phy_provider *__of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args));
+struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
+       struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args));
+void of_phy_provider_unregister(struct phy_provider *phy_provider);
+void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider);
+#else
+static inline int phy_pm_runtime_get(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_get_sync(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_put(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline int phy_pm_runtime_put_sync(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline void phy_pm_runtime_allow(struct phy *phy)
+{
+       return;
+}
+
+static inline void phy_pm_runtime_forbid(struct phy *phy)
+{
+       return;
+}
+
+static inline int phy_init(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline int phy_exit(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline int phy_power_on(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline int phy_power_off(struct phy *phy)
+{
+       if (!phy)
+               return 0;
+       return -ENOSYS;
+}
+
+static inline int phy_get_bus_width(struct phy *phy)
+{
+       return -ENOSYS;
+}
+
+static inline void phy_set_bus_width(struct phy *phy, int bus_width)
+{
+       return;
+}
+
+static inline struct phy *phy_get(struct device *dev, const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *phy_optional_get(struct device *dev,
+                                          const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_phy_get(struct device *dev, const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_phy_optional_get(struct device *dev,
+                                               const char *string)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_of_phy_get(struct device *dev,
+                                         struct device_node *np,
+                                         const char *con_id)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void phy_put(struct phy *phy)
+{
+}
+
+static inline void devm_phy_put(struct device *dev, struct phy *phy)
+{
+}
+
+static inline struct phy *of_phy_get(struct device_node *np, const char *con_id)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *of_phy_simple_xlate(struct device *dev,
+       struct of_phandle_args *args)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *phy_create(struct device *dev,
+                                    struct device_node *node,
+                                    const struct phy_ops *ops,
+                                    struct phy_init_data *init_data)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy *devm_phy_create(struct device *dev,
+                                         struct device_node *node,
+                                         const struct phy_ops *ops,
+                                         struct phy_init_data *init_data)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void phy_destroy(struct phy *phy)
+{
+}
+
+static inline void devm_phy_destroy(struct device *dev, struct phy *phy)
+{
+}
+
+static inline struct phy_provider *__of_phy_provider_register(
+       struct device *dev, struct module *owner, struct phy * (*of_xlate)(
+       struct device *dev, struct of_phandle_args *args))
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline struct phy_provider *__devm_of_phy_provider_register(struct device
+       *dev, struct module *owner, struct phy * (*of_xlate)(struct device *dev,
+       struct of_phandle_args *args))
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline void of_phy_provider_unregister(struct phy_provider *phy_provider)
+{
+}
+
+static inline void devm_of_phy_provider_unregister(struct device *dev,
+       struct phy_provider *phy_provider)
+{
+}
+#endif
+
+#endif /* __DRIVERS_PHY_H */