]> git.karo-electronics.de Git - linux-beck.git/commitdiff
Merge tag 'for_3.15' of git://git.kernel.org/pub/scm/linux/kernel/git/kishon/linux...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 9 Mar 2014 18:16:38 +0000 (11:16 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Sun, 9 Mar 2014 18:16:38 +0000 (11:16 -0700)
Kishon writes:

Add new PHY drivers for SATA and USB in exynos, for USB in sunxi,
and a multi-purpose PHY in APM, all adapted to generic PHY framework.
Adapted USB3 PHY driver in OMAP to generic PHY driver and also used
the same driver for SATA in OMAP. It also includes miscellaneous cleanups
and fixes.

28 files changed:
Documentation/devicetree/bindings/phy/apm-xgene-phy.txt [new file with mode: 0644]
Documentation/devicetree/bindings/phy/samsung-phy.txt
Documentation/devicetree/bindings/phy/sun4i-usb-phy.txt [new file with mode: 0644]
Documentation/phy/samsung-usb2.txt [new file with mode: 0644]
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/phy-bcm-kona-usb2.c
drivers/phy/phy-core.c
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-exynos5250-sata.c [new file with mode: 0644]
drivers/phy/phy-exynos5250-usb2.c [new file with mode: 0644]
drivers/phy/phy-omap-control.c [moved from drivers/usb/phy/phy-omap-control.c with 53% similarity]
drivers/phy/phy-omap-usb2.c
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-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
drivers/phy/phy-xgene.c [new file with mode: 0644]
drivers/usb/musb/omap2430.c
drivers/usb/phy/Kconfig
drivers/usb/phy/Makefile
drivers/usb/phy/phy-omap-usb3.c [deleted file]
drivers/usb/phy/phy-twl6030-usb.c
include/linux/phy/omap_control_phy.h [moved from include/linux/usb/omap_control_usb.h with 68% similarity]
include/linux/phy/omap_usb.h [moved from include/linux/usb/omap_usb.h with 86% similarity]
include/linux/phy/phy.h

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";
+               };
index c0fccaa1671ec8da7e403d0a8478860da3e586ba..28f9edb8f19c6f9966610fdac0589407245f3ea7 100644 (file)
@@ -20,3 +20,57 @@ Required properties:
 - compatible : should be "samsung,exynos5250-dp-video-phy";
 - reg : offset and length of the Display Port PHY register set;
 - #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,exynos4210-usb2-phy"
+       - "samsung,exynos4x12-usb2-phy"
+       - "samsung,exynos5250-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"),
+
+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.
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..a82361b
--- /dev/null
@@ -0,0 +1,26 @@
+Allwinner sun4i USB PHY
+-----------------------
+
+Required properties:
+- compatible : should be one of "allwinner,sun4i-a10-usb-phy",
+  "allwinner,sun5i-a13-usb-phy" or "allwinner,sun7i-a20-usb-phy"
+- reg : a list of offset + length pairs
+- reg-names : "phy_ctrl", "pmu1" and for sun4i or sun7i "pmu2"
+- #phy-cells : from the generic phy bindings, must be 1
+- clocks : phandle + clock specifier for the phy clock
+- clock-names : "usb_phy"
+- resets : a list of phandle + reset specifier pairs
+- reset-names : "usb0_reset", "usb1_reset" and for sun4i or sun7i "usb2_reset"
+
+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/phy/samsung-usb2.txt b/Documentation/phy/samsung-usb2.txt
new file mode 100644 (file)
index 0000000..ed12d43
--- /dev/null
@@ -0,0 +1,135 @@
+.------------------------------------------------------------------------------+
+|                      Samsung USB 2.0 PHY adaptation layer                   |
++-----------------------------------------------------------------------------+'
+
+| 1. Description
++----------------
+
+The architecture of the USB 2.0 PHY module in Samsung SoCs is similar
+among many SoCs. In spite of the similarities it proved difficult to
+create a one driver that would fit all these PHY controllers. Often
+the differences were minor and were found in particular bits of the
+registers of the PHY. In some rare cases the order of register writes or
+the PHY powering up process had to be altered. This adaptation layer is
+a compromise between having separate drivers and having a single driver
+with added support for many special cases.
+
+| 2. Files description
++----------------------
+
+- phy-samsung-usb2.c
+   This is the main file of the adaptation layer. This file contains
+   the probe function and provides two callbacks to the Generic PHY
+   Framework. This two callbacks are used to power on and power off the
+   phy. They carry out the common work that has to be done on all version
+   of the PHY module. Depending on which SoC was chosen they execute SoC
+   specific callbacks. The specific SoC version is selected by choosing
+   the appropriate compatible string. In addition, this file contains
+   struct of_device_id definitions for particular SoCs.
+
+- phy-samsung-usb2.h
+   This is the include file. It declares the structures used by this
+   driver. In addition it should contain extern declarations for
+   structures that describe particular SoCs.
+
+| 3. Supporting SoCs
++--------------------
+
+To support a new SoC a new file should be added to the drivers/phy
+directory. Each SoC's configuration is stored in an instance of the
+struct samsung_usb2_phy_config.
+
+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;
+};
+
+The num_phys is the number of phys handled by the driver. *phys is an
+array that contains the configuration for each phy. The has_mode_switch
+property is a boolean flag that determines whether the SoC has USB host
+and device on a single pair of pins. If so, a special register has to
+be modified to change the internal routing of these pins between a USB
+device or host module.
+
+For example the configuration for Exynos 4210 is following:
+
+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,
+}
+
+- int (*rate_to_clk)(unsigned long, u32 *)
+       The rate_to_clk callback is to convert the rate of the clock
+       used as the reference clock for the PHY module to the value
+       that should be written in the hardware register.
+
+The exynos4210_phys configuration array is as follows:
+
+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,
+       },
+       {},
+};
+
+- int (*power_on)(struct samsung_usb2_phy_instance *);
+- int (*power_off)(struct samsung_usb2_phy_instance *);
+       These two callbacks are used to power on and power off the phy
+       by modifying appropriate registers.
+
+Final change to the driver is adding appropriate compatible value to the
+phy-samsung-usb2.c file. In case of Exynos 4210 the following lines were
+added to the struct of_device_id samsung_usb2_phy_of_match[] array:
+
+#ifdef CONFIG_PHY_EXYNOS4210_USB2
+       {
+               .compatible = "samsung,exynos4210-usb2-phy",
+               .data = &exynos4210_usb2_phy_config,
+       },
+#endif
+
+To add further flexibility to the driver the Kconfig file enables to
+include support for selected SoCs in the compiled driver. The Kconfig
+entry for Exynos 4210 is following:
+
+config PHY_EXYNOS4210_USB2
+       bool "Support for Exynos 4210"
+       depends on PHY_SAMSUNG_USB2
+       depends on CPU_EXYNOS4210
+       help
+         Enable USB PHY support for Exynos 4210. 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 Exynos 4210 four
+         phys are available - device, host, HSCI0 and HSCI1.
+
+The newly created file that supports the new SoC has to be also added to the
+Makefile. In case of Exynos 4210 the added line is following:
+
+obj-$(CONFIG_PHY_EXYNOS4210_USB2)       += phy-exynos4210-usb2.o
+
+After completing these steps the support for the new SoC should be ready.
index c7a551c2d5f1b4d39b982d85222537ec3fbfb804..8d3c49cc500fadcd212620f62df6d3cc9df68e4a 100644 (file)
@@ -16,30 +16,56 @@ config GENERIC_PHY
          framework should select this config.
 
 config PHY_EXYNOS_MIPI_VIDEO
-       depends on HAS_IOMEM
        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_KIRKWOOD || ARCH_DOVE
+       depends on ARCH_KIRKWOOD || ARCH_DOVE || MACH_DOVE
        depends on OF
        select GENERIC_PHY
 
+config OMAP_CONTROL_PHY
+       tristate "OMAP CONTROL PHY Driver"
+       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_USB
+       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
@@ -54,6 +80,8 @@ config TWL4030_USB
 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.
@@ -65,4 +93,77 @@ config BCM_KONA_USB2_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_SUN4I_USB
+       tristate "Allwinner sunxi SoC USB PHY driver"
+       depends on ARCH_SUNXI && HAS_IOMEM && OF
+       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"
+       select GENERIC_PHY
+       select MFD_SYSCON
+       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 SoCs has to be enabled in addition to this driver. Number
+         and type of supported phys depends on the SoC.
+
+config PHY_EXYNOS4210_USB2
+       bool "Support for Exynos 4210"
+       depends on PHY_SAMSUNG_USB2
+       depends on CPU_EXYNOS4210
+       help
+         Enable USB PHY support for Exynos 4210. 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 Exynos 4210 four
+         phys are available - device, host, HSIC0 and HSIC1.
+
+config PHY_EXYNOS4X12_USB2
+       bool "Support for Exynos 4x12"
+       depends on PHY_SAMSUNG_USB2
+       depends on (SOC_EXYNOS4212 || SOC_EXYNOS4412)
+       help
+         Enable USB PHY support for Exynos 4x12. 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 Exynos 4x12 four
+         phys are available - device, host, HSIC0 and HSIC1.
+
+config PHY_EXYNOS5250_USB2
+       bool "Support for Exynos 5250"
+       depends on PHY_SAMSUNG_USB2
+       depends on SOC_EXYNOS5250
+       help
+         Enable USB PHY support for Exynos 5250. 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 Exynos 5250 four
+         phys are available - device, host, HSIC0 and HSIC.
+
+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.
+
 endmenu
index b57c25371cca48695cf6068f61cfbd85e44c281b..2faf78edc8649f2c5ef79c3093aaa14de04b0eea 100644 (file)
@@ -7,5 +7,14 @@ 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_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_SUN4I_USB)            += phy-sun4i-usb.o
+obj-$(CONFIG_PHY_SAMSUNG_USB2)         += phy-samsung-usb2.o
+obj-$(CONFIG_PHY_EXYNOS4210_USB2)      += phy-exynos4210-usb2.o
+obj-$(CONFIG_PHY_EXYNOS4X12_USB2)      += phy-exynos4x12-usb2.o
+obj-$(CONFIG_PHY_EXYNOS5250_USB2)      += phy-exynos5250-usb2.o
+obj-$(CONFIG_PHY_XGENE)                        += phy-xgene.o
index efc5c1a13a5d0cd1acb9c03c98820002839cae49..e94f5a6a5645a33acaad36e64da7ac9c2d44b364 100644 (file)
@@ -128,10 +128,8 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
 
        phy_provider = devm_of_phy_provider_register(dev,
                        of_phy_simple_xlate);
-       if (IS_ERR(phy_provider))
-               return PTR_ERR(phy_provider);
 
-       return 0;
+       return PTR_ERR_OR_ZERO(phy_provider);
 }
 
 static const struct of_device_id bcm_kona_usb2_dt_ids[] = {
index 6c738376daff5110a791c3c3f6492ab818971165..623b71c54b3e5f0a865d8563c8557919e9603900 100644 (file)
@@ -274,8 +274,8 @@ int phy_power_off(struct phy *phy)
 EXPORT_SYMBOL_GPL(phy_power_off);
 
 /**
- * of_phy_get() - lookup and obtain a reference to a phy by phandle
- * @dev: device that requests this phy
+ * _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,
@@ -284,20 +284,17 @@ EXPORT_SYMBOL_GPL(phy_power_off);
  * 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 *dev, int index)
+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(dev->of_node, "phys", "#phy-cells",
+       ret = of_parse_phandle_with_args(np, "phys", "#phy-cells",
                index, &args);
-       if (ret) {
-               dev_dbg(dev, "failed to get phy in %s node\n",
-                       dev->of_node->full_name);
+       if (ret)
                return ERR_PTR(-ENODEV);
-       }
 
        mutex_lock(&phy_provider_mutex);
        phy_provider = of_phy_provider_lookup(args.np);
@@ -316,6 +313,36 @@ err0:
        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()
@@ -407,7 +434,7 @@ struct phy *phy_get(struct device *dev, const char *string)
        if (dev->of_node) {
                index = of_property_match_string(dev->of_node, "phy-names",
                        string);
-               phy = of_phy_get(dev, index);
+               phy = _of_phy_get(dev->of_node, index);
        } else {
                phy = phy_lookup(dev, string);
        }
@@ -498,6 +525,37 @@ struct phy *devm_phy_optional_get(struct device *dev, const char *string)
 }
 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
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..d92a7cc
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * 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_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)
+#define EXYNOS_4x12_URSTCON_PHY1               BIT(4)
+#define EXYNOS_4x12_URSTCON_HSIC0              BIT(5)
+#define EXYNOS_4x12_URSTCON_HSIC1              BIT(6)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_ALL      BIT(7)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P0       BIT(8)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P1       BIT(9)
+#define EXYNOS_4x12_URSTCON_HOST_LINK_P2       BIT(10)
+
+/* 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;
+       clk |= drv->ref_reg_val << EXYNOS_4x12_UPHYCLK_PHYFSEL_OFFSET;
+       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;
+       u32 mode = 0;
+       u32 switch_mode = 0;
+
+       switch (inst->cfg->id) {
+       case EXYNOS4x12_DEVICE:
+               phypwr =        EXYNOS_4x12_UPHYPWR_PHY0;
+               rstbits =       EXYNOS_4x12_URSTCON_PHY0;
+               mode =          EXYNOS_4x12_MODE_SWITCH_DEVICE;
+               switch_mode =   1;
+               break;
+       case EXYNOS4x12_HOST:
+               phypwr =        EXYNOS_4x12_UPHYPWR_PHY1;
+               rstbits =       EXYNOS_4x12_URSTCON_HOST_PHY;
+               mode =          EXYNOS_4x12_MODE_SWITCH_HOST;
+               switch_mode =   1;
+               break;
+       case EXYNOS4x12_HSIC0:
+               phypwr =        EXYNOS_4x12_UPHYPWR_HSIC0;
+               rstbits =       EXYNOS_4x12_URSTCON_HSIC1 |
+                               EXYNOS_4x12_URSTCON_HOST_LINK_P0 |
+                               EXYNOS_4x12_URSTCON_HOST_PHY;
+               break;
+       case EXYNOS4x12_HSIC1:
+               phypwr =        EXYNOS_4x12_UPHYPWR_HSIC1;
+               rstbits =       EXYNOS_4x12_URSTCON_HSIC1 |
+                               EXYNOS_4x12_URSTCON_HOST_LINK_P1;
+               break;
+       };
+
+       if (on) {
+               if (switch_mode)
+                       regmap_update_bits(drv->reg_sys,
+                                          EXYNOS_4x12_MODE_SWITCH_OFFSET,
+                                          EXYNOS_4x12_MODE_SWITCH_MASK, mode);
+
+               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 int exynos4x12_power_on(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+
+       inst->enabled = 1;
+       exynos4x12_setup_clk(inst);
+       exynos4x12_phy_pwr(inst, 1);
+       exynos4x12_isol(inst, 0);
+
+       /* Power on the device, as it is necessary for HSIC to work */
+       if (inst->cfg->id == EXYNOS4x12_HSIC0) {
+               struct samsung_usb2_phy_instance *device =
+                                       &drv->instances[EXYNOS4x12_DEVICE];
+               exynos4x12_phy_pwr(device, 1);
+               exynos4x12_isol(device, 0);
+       }
+
+       return 0;
+}
+
+static int exynos4x12_power_off(struct samsung_usb2_phy_instance *inst)
+{
+       struct samsung_usb2_phy_driver *drv = inst->drv;
+       struct samsung_usb2_phy_instance *device =
+                                       &drv->instances[EXYNOS4x12_DEVICE];
+
+       inst->enabled = 0;
+       exynos4x12_isol(inst, 1);
+       exynos4x12_phy_pwr(inst, 0);
+
+       if (inst->cfg->id == EXYNOS4x12_HSIC0 && !device->enabled) {
+               exynos4x12_isol(device, 1);
+               exynos4x12_phy_pwr(device, 0);
+       }
+
+       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 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-exynos5250-sata.c b/drivers/phy/phy-exynos5250-sata.c
new file mode 100644 (file)
index 0000000..c9361b7
--- /dev/null
@@ -0,0 +1,251 @@
+/*
+ * 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, &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",
+               .owner = THIS_MODULE,
+       }
+};
+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..94179af
--- /dev/null
@@ -0,0 +1,404 @@
+/*
+ * 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;
+       }
+       inst->enabled = 1;
+       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;
+
+       inst->enabled = 0;
+       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,
+};
similarity index 53%
rename from drivers/usb/phy/phy-omap-control.c
rename to drivers/phy/phy-omap-control.c
index e7253182e47d72882b27a2126ba5a0b34183cdee..311b4f9a51323a7eda3c35ea16a2ab3a9a61cc06 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * omap-control-usb.c - The USB part of control module.
+ * 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
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/clk.h>
-#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/omap_control_phy.h>
 
 /**
- * omap_control_usb_phy_power - power on/off the phy using control module reg
+ * 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_usb_phy_power(struct device *dev, int on)
+void omap_control_phy_power(struct device *dev, int on)
 {
        u32 val;
        unsigned long rate;
-       struct omap_control_usb *control_usb;
+       struct omap_control_phy *control_phy;
 
        if (IS_ERR(dev) || !dev) {
                pr_err("%s: invalid device\n", __func__);
                return;
        }
 
-       control_usb = dev_get_drvdata(dev);
-       if (!control_usb) {
-               dev_err(dev, "%s: invalid control usb device\n", __func__);
+       control_phy = dev_get_drvdata(dev);
+       if (!control_phy) {
+               dev_err(dev, "%s: invalid control phy device\n", __func__);
                return;
        }
 
-       if (control_usb->type == OMAP_CTRL_TYPE_OTGHS)
+       if (control_phy->type == OMAP_CTRL_TYPE_OTGHS)
                return;
 
-       val = readl(control_usb->power);
+       val = readl(control_phy->power);
 
-       switch (control_usb->type) {
+       switch (control_phy->type) {
        case OMAP_CTRL_TYPE_USB2:
                if (on)
                        val &= ~OMAP_CTRL_DEV_PHY_PD;
@@ -62,19 +62,20 @@ void omap_control_usb_phy_power(struct device *dev, int on)
                break;
 
        case OMAP_CTRL_TYPE_PIPE3:
-               rate = clk_get_rate(control_usb->sys_clk);
+               rate = clk_get_rate(control_phy->sys_clk);
                rate = rate/1000000;
 
                if (on) {
-                       val &= ~(OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK |
-                                       OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK);
-                       val |= OMAP_CTRL_USB3_PHY_TX_RX_POWERON <<
-                               OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
-                       val |= rate << OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT;
+                       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_USB_PWRCTL_CLK_CMD_MASK;
-                       val |= OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF <<
-                               OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT;
+                       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;
 
@@ -100,66 +101,66 @@ void omap_control_usb_phy_power(struct device *dev, int on)
                break;
        default:
                dev_err(dev, "%s: type %d not recognized\n",
-                                       __func__, control_usb->type);
+                       __func__, control_phy->type);
                break;
        }
 
-       writel(val, control_usb->power);
+       writel(val, control_phy->power);
 }
-EXPORT_SYMBOL_GPL(omap_control_usb_phy_power);
+EXPORT_SYMBOL_GPL(omap_control_phy_power);
 
 /**
  * omap_control_usb_host_mode - set AVALID, VBUSVALID and ID pin in grounded
- * @ctrl_usb: struct omap_control_usb *
+ * @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_usb *ctrl_usb)
+static void omap_control_usb_host_mode(struct omap_control_phy *ctrl_phy)
 {
        u32 val;
 
-       val = readl(ctrl_usb->otghs_control);
+       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_usb->otghs_control);
+       writel(val, ctrl_phy->otghs_control);
 }
 
 /**
  * omap_control_usb_device_mode - set AVALID, VBUSVALID and ID pin in high
  * impedance
- * @ctrl_usb: struct omap_control_usb *
+ * @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_usb *ctrl_usb)
+static void omap_control_usb_device_mode(struct omap_control_phy *ctrl_phy)
 {
        u32 val;
 
-       val = readl(ctrl_usb->otghs_control);
+       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_usb->otghs_control);
+       writel(val, ctrl_phy->otghs_control);
 }
 
 /**
  * omap_control_usb_set_sessionend - Enable SESSIONEND and IDIG to high
  * impedance
- * @ctrl_usb: struct omap_control_usb *
+ * @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_usb *ctrl_usb)
+static void omap_control_usb_set_sessionend(struct omap_control_phy *ctrl_phy)
 {
        u32 val;
 
-       val = readl(ctrl_usb->otghs_control);
+       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_usb->otghs_control);
+       writel(val, ctrl_phy->otghs_control);
 }
 
 /**
@@ -174,30 +175,30 @@ static void omap_control_usb_set_sessionend(struct omap_control_usb *ctrl_usb)
 void omap_control_usb_set_mode(struct device *dev,
        enum omap_control_usb_mode mode)
 {
-       struct omap_control_usb *ctrl_usb;
+       struct omap_control_phy *ctrl_phy;
 
        if (IS_ERR(dev) || !dev)
                return;
 
-       ctrl_usb = dev_get_drvdata(dev);
+       ctrl_phy = dev_get_drvdata(dev);
 
-       if (!ctrl_usb) {
-               dev_err(dev, "Invalid control usb device\n");
+       if (!ctrl_phy) {
+               dev_err(dev, "Invalid control phy device\n");
                return;
        }
 
-       if (ctrl_usb->type != OMAP_CTRL_TYPE_OTGHS)
+       if (ctrl_phy->type != OMAP_CTRL_TYPE_OTGHS)
                return;
 
        switch (mode) {
        case USB_MODE_HOST:
-               omap_control_usb_host_mode(ctrl_usb);
+               omap_control_usb_host_mode(ctrl_phy);
                break;
        case USB_MODE_DEVICE:
-               omap_control_usb_device_mode(ctrl_usb);
+               omap_control_usb_device_mode(ctrl_phy);
                break;
        case USB_MODE_DISCONNECT:
-               omap_control_usb_set_sessionend(ctrl_usb);
+               omap_control_usb_set_sessionend(ctrl_phy);
                break;
        default:
                dev_vdbg(dev, "invalid omap control usb mode\n");
@@ -207,13 +208,13 @@ EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
 
 #ifdef CONFIG_OF
 
-static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
-static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
-static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
-static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
-static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
+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 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_usb_id_table[] = {
+static const struct of_device_id omap_control_phy_id_table[] = {
        {
                .compatible = "ti,control-phy-otghs",
                .data = &otghs_data,
@@ -227,93 +228,93 @@ static const struct of_device_id omap_control_usb_id_table[] = {
                .data = &pipe3_data,
        },
        {
-               .compatible = "ti,control-phy-dra7usb2",
+               .compatible = "ti,control-phy-usb2-dra7",
                .data = &dra7usb2_data,
        },
        {
-               .compatible = "ti,control-phy-am437usb2",
+               .compatible = "ti,control-phy-usb2-am437",
                .data = &am437usb2_data,
        },
        {},
 };
-MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
+MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
 #endif
 
 
-static int omap_control_usb_probe(struct platform_device *pdev)
+static int omap_control_phy_probe(struct platform_device *pdev)
 {
        struct resource *res;
        const struct of_device_id *of_id;
-       struct omap_control_usb *control_usb;
+       struct omap_control_phy *control_phy;
 
-       of_id = of_match_device(of_match_ptr(omap_control_usb_id_table),
-                                                               &pdev->dev);
+       of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
+                               &pdev->dev);
        if (!of_id)
                return -EINVAL;
 
-       control_usb = devm_kzalloc(&pdev->dev, sizeof(*control_usb),
+       control_phy = devm_kzalloc(&pdev->dev, sizeof(*control_phy),
                GFP_KERNEL);
-       if (!control_usb) {
-               dev_err(&pdev->dev, "unable to alloc memory for control usb\n");
+       if (!control_phy) {
+               dev_err(&pdev->dev, "unable to alloc memory for control phy\n");
                return -ENOMEM;
        }
 
-       control_usb->dev = &pdev->dev;
-       control_usb->type = *(enum omap_control_usb_type *)of_id->data;
+       control_phy->dev = &pdev->dev;
+       control_phy->type = *(enum omap_control_phy_type *)of_id->data;
 
-       if (control_usb->type == OMAP_CTRL_TYPE_OTGHS) {
+       if (control_phy->type == OMAP_CTRL_TYPE_OTGHS) {
                res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
                        "otghs_control");
-               control_usb->otghs_control = devm_ioremap_resource(
+               control_phy->otghs_control = devm_ioremap_resource(
                        &pdev->dev, res);
-               if (IS_ERR(control_usb->otghs_control))
-                       return PTR_ERR(control_usb->otghs_control);
+               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_usb->power = devm_ioremap_resource(&pdev->dev, res);
-               if (IS_ERR(control_usb->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_usb->power);
+                       return PTR_ERR(control_phy->power);
                }
        }
 
-       if (control_usb->type == OMAP_CTRL_TYPE_PIPE3) {
-               control_usb->sys_clk = devm_clk_get(control_usb->dev,
+       if (control_phy->type == OMAP_CTRL_TYPE_PIPE3) {
+               control_phy->sys_clk = devm_clk_get(control_phy->dev,
                        "sys_clkin");
-               if (IS_ERR(control_usb->sys_clk)) {
+               if (IS_ERR(control_phy->sys_clk)) {
                        pr_err("%s: unable to get sys_clkin\n", __func__);
                        return -EINVAL;
                }
        }
 
-       dev_set_drvdata(control_usb->dev, control_usb);
+       dev_set_drvdata(control_phy->dev, control_phy);
 
        return 0;
 }
 
-static struct platform_driver omap_control_usb_driver = {
-       .probe          = omap_control_usb_probe,
+static struct platform_driver omap_control_phy_driver = {
+       .probe          = omap_control_phy_probe,
        .driver         = {
-               .name   = "omap-control-usb",
+               .name   = "omap-control-phy",
                .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(omap_control_usb_id_table),
+               .of_match_table = of_match_ptr(omap_control_phy_id_table),
        },
 };
 
-static int __init omap_control_usb_init(void)
+static int __init omap_control_phy_init(void)
 {
-       return platform_driver_register(&omap_control_usb_driver);
+       return platform_driver_register(&omap_control_phy_driver);
 }
-subsys_initcall(omap_control_usb_init);
+subsys_initcall(omap_control_phy_init);
 
-static void __exit omap_control_usb_exit(void)
+static void __exit omap_control_phy_exit(void)
 {
-       platform_driver_unregister(&omap_control_usb_driver);
+       platform_driver_unregister(&omap_control_phy_driver);
 }
-module_exit(omap_control_usb_exit);
+module_exit(omap_control_phy_exit);
 
-MODULE_ALIAS("platform: omap_control_usb");
+MODULE_ALIAS("platform: omap_control_phy");
 MODULE_AUTHOR("Texas Instruments Inc.");
-MODULE_DESCRIPTION("OMAP Control Module USB Driver");
+MODULE_DESCRIPTION("OMAP Control Module PHY Driver");
 MODULE_LICENSE("GPL v2");
index 7699752fba11bfbfa8acff589d794262d2882cc4..a2205a841e5e37266594692f2bb12f35f6321bf0 100644 (file)
 #include <linux/slab.h>
 #include <linux/of.h>
 #include <linux/io.h>
-#include <linux/usb/omap_usb.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/usb/omap_control_usb.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
@@ -98,65 +101,116 @@ static int omap_usb_set_peripheral(struct usb_otg *otg,
        return 0;
 }
 
-static int omap_usb2_suspend(struct usb_phy *x, int suspend)
+static int omap_usb_power_off(struct phy *x)
 {
-       struct omap_usb *phy = phy_to_omapusb(x);
-       int ret;
+       struct omap_usb *phy = phy_get_drvdata(x);
 
-       if (suspend && !phy->is_suspended) {
-               omap_control_usb_phy_power(phy->control_dev, 0);
-               pm_runtime_put_sync(phy->dev);
-               phy->is_suspended = 1;
-       } else if (!suspend && phy->is_suspended) {
-               ret = pm_runtime_get_sync(phy->dev);
-               if (ret < 0) {
-                       dev_err(phy->dev, "get_sync failed with err %d\n", ret);
-                       return ret;
-               }
-               omap_control_usb_phy_power(phy->control_dev, 1);
-               phy->is_suspended = 0;
-       }
+       omap_control_phy_power(phy->control_dev, 0);
 
        return 0;
 }
 
-static int omap_usb_power_off(struct phy *x)
+static int omap_usb_power_on(struct phy *x)
 {
        struct omap_usb *phy = phy_get_drvdata(x);
 
-       omap_control_usb_phy_power(phy->control_dev, 0);
+       omap_control_phy_power(phy->control_dev, 1);
 
        return 0;
 }
 
-static int omap_usb_power_on(struct phy *x)
+static int omap_usb_init(struct phy *x)
 {
        struct omap_usb *phy = phy_get_drvdata(x);
-
-       omap_control_usb_phy_power(phy->control_dev, 1);
+       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 (!node)
+       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) {
                dev_err(&pdev->dev, "unable to allocate memory for USB2 PHY\n");
@@ -172,11 +226,18 @@ static int omap_usb2_probe(struct platform_device *pdev)
        phy->dev                = &pdev->dev;
 
        phy->phy.dev            = phy->dev;
-       phy->phy.label          = "omap-usb2";
-       phy->phy.set_suspend    = omap_usb2_suspend;
+       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 (!phy->phy_base)
+                       return -ENOMEM;
+               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");
@@ -190,14 +251,14 @@ static int omap_usb2_probe(struct platform_device *pdev)
        }
 
        phy->control_dev = &control_pdev->dev;
-
-       phy->is_suspended       = 1;
-       omap_control_usb_phy_power(phy->control_dev, 0);
+       omap_control_phy_power(phy->control_dev, 0);
 
        otg->set_host           = omap_usb_set_host;
        otg->set_peripheral     = omap_usb_set_peripheral;
-       otg->set_vbus           = omap_usb_set_vbus;
-       otg->start_srp          = omap_usb_start_srp;
+       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->phy                = &phy->phy;
 
        platform_set_drvdata(pdev, phy);
@@ -297,14 +358,6 @@ static const struct dev_pm_ops omap_usb2_pm_ops = {
 #define DEV_PM_OPS     NULL
 #endif
 
-#ifdef CONFIG_OF
-static const struct of_device_id omap_usb2_id_table[] = {
-       { .compatible = "ti,omap-usb2" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
-#endif
-
 static struct platform_driver omap_usb2_driver = {
        .probe          = omap_usb2_probe,
        .remove         = omap_usb2_remove,
diff --git a/drivers/phy/phy-samsung-usb2.c b/drivers/phy/phy-samsung-usb2.c
new file mode 100644 (file)
index 0000000..8a8c6bc
--- /dev/null
@@ -0,0 +1,228 @@
+/*
+ * 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_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
+       { },
+};
+
+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, &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",
+               .owner          = THIS_MODULE,
+       }
+};
+
+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..45b3170
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * 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;
+       bool enabled;
+};
+
+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;
+};
+
+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;
+#endif
diff --git a/drivers/phy/phy-sun4i-usb.c b/drivers/phy/phy-sun4i-usb.c
new file mode 100644 (file)
index 0000000..e6e6c4b
--- /dev/null
@@ -0,0 +1,331 @@
+/*
+ * 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/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 {
+       struct clk *clk;
+       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;
+               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(data->clk);
+       if (ret)
+               return ret;
+
+       ret = reset_control_deassert(phy->reset);
+       if (ret) {
+               clk_disable_unprepare(data->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);
+       struct sun4i_usb_phy_data *data = to_sun4i_usb_phy_data(phy);
+
+       sun4i_usb_phy_passby(phy, 0);
+       reset_control_assert(phy->reset);
+       clk_disable_unprepare(data->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;
+       void __iomem *pmu = NULL;
+       struct phy_provider *phy_provider;
+       struct reset_control *reset;
+       struct regulator *vbus;
+       struct resource *res;
+       struct phy *phy;
+       char name[16];
+       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;
+
+       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);
+
+       data->clk = devm_clk_get(dev, "usb_phy");
+       if (IS_ERR(data->clk)) {
+               dev_err(dev, "could not get usb_phy clock\n");
+               return PTR_ERR(data->clk);
+       }
+
+       /* Skip 0, 0 is the phy for otg which is not yet supported. */
+       for (i = 1; i < data->num_phys; i++) {
+               snprintf(name, sizeof(name), "usb%d_vbus", i);
+               vbus = devm_regulator_get_optional(dev, name);
+               if (IS_ERR(vbus)) {
+                       if (PTR_ERR(vbus) == -EPROBE_DEFER)
+                               return -EPROBE_DEFER;
+                       vbus = NULL;
+               }
+
+               snprintf(name, sizeof(name), "usb%d_reset", i);
+               reset = devm_reset_control_get(dev, name);
+               if (IS_ERR(reset)) {
+                       dev_err(dev, "failed to get reset %s\n", name);
+                       return PTR_ERR(reset);
+               }
+
+               if (i) { /* No pmu for usbc0 */
+                       snprintf(name, sizeof(name), "pmu%d", i);
+                       res = platform_get_resource_byname(pdev,
+                                                       IORESOURCE_MEM, name);
+                       pmu = devm_ioremap_resource(dev, res);
+                       if (IS_ERR(pmu))
+                               return PTR_ERR(pmu);
+               }
+
+               phy = devm_phy_create(dev, &sun4i_usb_phy_ops, NULL);
+               if (IS_ERR(phy)) {
+                       dev_err(dev, "failed to create PHY %d\n", i);
+                       return PTR_ERR(phy);
+               }
+
+               data->phys[i].phy = phy;
+               data->phys[i].pmu = pmu;
+               data->phys[i].vbus = vbus;
+               data->phys[i].reset = reset;
+               data->phys[i].index = i;
+               phy_set_drvdata(phy, &data->phys[i]);
+       }
+
+       dev_set_drvdata(dev, data);
+       phy_provider = devm_of_phy_provider_register(dev, sun4i_usb_phy_xlate);
+       if (IS_ERR(phy_provider))
+               return PTR_ERR(phy_provider);
+
+       return 0;
+}
+
+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,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",
+               .owner = THIS_MODULE,
+       }
+};
+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..5913676
--- /dev/null
@@ -0,0 +1,470 @@
+/*
+ * 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 pipe3_dpll_map   *dpll_map;
+};
+
+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;
+
+       /* 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 */
+       if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
+               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;
+
+       match = of_match_device(of_match_ptr(ti_pipe3_id_table), &pdev->dev);
+       if (!match)
+               return -EINVAL;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
+               return -ENOMEM;
+       }
+
+       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->dev                = &pdev->dev;
+
+       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);
+       }
+
+       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;
+       }
+
+       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, &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);
+
+       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;
+               }
+       }
+
+       return 0;
+
+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,
+       },
+       {}
+};
+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",
+               .owner  = THIS_MODULE,
+               .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");
index c3ace1db8136eedef379691bc6fa532b4ff5d67c..2e0e9b3774c88d7501462466dd858d3a3bf7edd0 100644 (file)
@@ -338,7 +338,7 @@ static void twl4030_usb_set_mode(struct twl4030_usb *twl, int mode)
                dev_err(twl->dev, "unsupported T2 transceiver mode %d\n",
                                mode);
                break;
-       };
+       }
 }
 
 static void twl4030_i2c_access(struct twl4030_usb *twl, int on)
@@ -661,7 +661,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        struct phy_provider     *phy_provider;
        struct phy_init_data    *init_data = NULL;
 
-       twl = devm_kzalloc(&pdev->dev, sizeof *twl, GFP_KERNEL);
+       twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
        if (!twl)
                return -ENOMEM;
 
@@ -676,7 +676,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       otg = devm_kzalloc(&pdev->dev, sizeof *otg, GFP_KERNEL);
+       otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
        if (!otg)
                return -ENOMEM;
 
diff --git a/drivers/phy/phy-xgene.c b/drivers/phy/phy-xgene.c
new file mode 100644 (file)
index 0000000..4aa1ccd
--- /dev/null
@@ -0,0 +1,1750 @@
+/*
+ * 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, &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",
+                  .owner = THIS_MODULE,
+                  .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");
index 8aa59a2c5eb2485c823244012cc738ce5df7d127..d341c149a2f90c1372201b9a78aff8a89d4f3e6f 100644 (file)
@@ -37,7 +37,7 @@
 #include <linux/err.h>
 #include <linux/delay.h>
 #include <linux/usb/musb-omap.h>
-#include <linux/usb/omap_control_usb.h>
+#include <linux/phy/omap_control_phy.h>
 #include <linux/of_platform.h>
 
 #include "musb_core.h"
index 7d1451d5bbea961204376b23126e9545ed3509f8..416e0c8cf6ff165b2aec8d8d602852a359ec22ff 100644 (file)
@@ -75,27 +75,6 @@ config NOP_USB_XCEIV
          built-in with usb ip or which are autonomous and doesn't require any
          phy programming such as ISP1x04 etc.
 
-config OMAP_CONTROL_USB
-       tristate "OMAP CONTROL USB Driver"
-       depends on ARCH_OMAP2PLUS || COMPILE_TEST
-       help
-         Enable this to add support for the USB 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.
-
-config OMAP_USB3
-       tristate "OMAP USB3 PHY Driver"
-       depends on ARCH_OMAP2PLUS || COMPILE_TEST
-       select OMAP_CONTROL_USB
-       select USB_PHY
-       help
-         Enable this to support the USB3 PHY that is part of SOC. This
-         driver takes care of all the PHY functionality apart from comparator.
-         This driver interacts with the "OMAP Control USB Driver" to power
-         on/off the PHY.
-
 config AM335X_CONTROL_USB
        tristate
 
index be58adae34960de4508596beb16066ae113348a3..f8fa719a31b9b9b8a86454b11a632992ceda7048 100644 (file)
@@ -13,11 +13,9 @@ obj-$(CONFIG_ISP1301_OMAP)           += phy-isp1301-omap.o
 obj-$(CONFIG_MV_U3D_PHY)               += phy-mv-u3d-usb.o
 obj-$(CONFIG_NOP_USB_XCEIV)            += phy-generic.o
 obj-$(CONFIG_TAHVO_USB)                        += phy-tahvo.o
-obj-$(CONFIG_OMAP_CONTROL_USB)         += phy-omap-control.o
 obj-$(CONFIG_AM335X_CONTROL_USB)       += phy-am335x-control.o
 obj-$(CONFIG_AM335X_PHY_USB)           += phy-am335x.o
 obj-$(CONFIG_OMAP_OTG)                 += phy-omap-otg.o
-obj-$(CONFIG_OMAP_USB3)                        += phy-omap-usb3.o
 obj-$(CONFIG_SAMSUNG_USBPHY)           += phy-samsung-usb.o
 obj-$(CONFIG_SAMSUNG_USB2PHY)          += phy-samsung-usb2.o
 obj-$(CONFIG_SAMSUNG_USB3PHY)          += phy-samsung-usb3.o
diff --git a/drivers/usb/phy/phy-omap-usb3.c b/drivers/usb/phy/phy-omap-usb3.c
deleted file mode 100644 (file)
index 0c6ba29..0000000
+++ /dev/null
@@ -1,361 +0,0 @@
-/*
- * omap-usb3 - USB PHY, talking to dwc3 controller in OMAP.
- *
- * 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/usb/omap_usb.h>
-#include <linux/of.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/pm_runtime.h>
-#include <linux/delay.h>
-#include <linux/usb/omap_control_usb.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            0x9
-#define        SET_PLL_GO              0x1
-#define        PLL_TICOPWDN            0x10000
-#define        PLL_LOCK                0x2
-#define        PLL_IDLE                0x1
-
-/*
- * This is an Empirical value that works, need to confirm the actual
- * value required for the USB3PHY_PLL_CONFIGURATION2.PLL_IDLE status
- * to be correctly reflected in the USB3PHY_PLL_STATUS register.
- */
-# define PLL_IDLE_TIME  100;
-
-struct usb_dpll_map {
-       unsigned long rate;
-       struct usb_dpll_params params;
-};
-
-static struct usb_dpll_map dpll_map[] = {
-       {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 */
-};
-
-static struct usb_dpll_params *omap_usb3_get_dpll_params(unsigned long rate)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(dpll_map); i++) {
-               if (rate == dpll_map[i].rate)
-                       return &dpll_map[i].params;
-       }
-
-       return NULL;
-}
-
-static int omap_usb3_suspend(struct usb_phy *x, int suspend)
-{
-       struct omap_usb *phy = phy_to_omapusb(x);
-       int     val;
-       int timeout = PLL_IDLE_TIME;
-
-       if (suspend && !phy->is_suspended) {
-               val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
-               val |= PLL_IDLE;
-               omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
-
-               do {
-                       val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
-                       if (val & PLL_TICOPWDN)
-                               break;
-                       udelay(1);
-               } while (--timeout);
-
-               omap_control_usb_phy_power(phy->control_dev, 0);
-
-               phy->is_suspended       = 1;
-       } else if (!suspend && phy->is_suspended) {
-               phy->is_suspended       = 0;
-
-               val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
-               val &= ~PLL_IDLE;
-               omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
-
-               do {
-                       val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
-                       if (!(val & PLL_TICOPWDN))
-                               break;
-                       udelay(1);
-               } while (--timeout);
-       }
-
-       return 0;
-}
-
-static void omap_usb_dpll_relock(struct omap_usb *phy)
-{
-       u32             val;
-       unsigned long   timeout;
-
-       omap_usb_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
-
-       timeout = jiffies + msecs_to_jiffies(20);
-       do {
-               val = omap_usb_readl(phy->pll_ctrl_base, PLL_STATUS);
-               if (val & PLL_LOCK)
-                       break;
-       } while (!WARN_ON(time_after(jiffies, timeout)));
-}
-
-static int omap_usb_dpll_lock(struct omap_usb *phy)
-{
-       u32                     val;
-       unsigned long           rate;
-       struct usb_dpll_params *dpll_params;
-
-       rate = clk_get_rate(phy->sys_clk);
-       dpll_params = omap_usb3_get_dpll_params(rate);
-       if (!dpll_params) {
-               dev_err(phy->dev,
-                         "No DPLL configuration for %lu Hz SYS CLK\n", rate);
-               return -EINVAL;
-       }
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
-       val &= ~PLL_REGN_MASK;
-       val |= dpll_params->n << PLL_REGN_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
-       val &= ~PLL_SELFREQDCO_MASK;
-       val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
-       val &= ~PLL_REGM_MASK;
-       val |= dpll_params->m << PLL_REGM_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
-       val &= ~PLL_REGM_F_MASK;
-       val |= dpll_params->mf << PLL_REGM_F_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
-
-       val = omap_usb_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
-       val &= ~PLL_SD_MASK;
-       val |= dpll_params->sd << PLL_SD_SHIFT;
-       omap_usb_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
-
-       omap_usb_dpll_relock(phy);
-
-       return 0;
-}
-
-static int omap_usb3_init(struct usb_phy *x)
-{
-       struct omap_usb *phy = phy_to_omapusb(x);
-       int ret;
-
-       ret = omap_usb_dpll_lock(phy);
-       if (ret)
-               return ret;
-
-       omap_control_usb_phy_power(phy->control_dev, 1);
-
-       return 0;
-}
-
-static int omap_usb3_probe(struct platform_device *pdev)
-{
-       struct omap_usb *phy;
-       struct resource *res;
-       struct device_node *node = pdev->dev.of_node;
-       struct device_node *control_node;
-       struct platform_device *control_pdev;
-
-       if (!node)
-               return -EINVAL;
-
-       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
-       if (!phy) {
-               dev_err(&pdev->dev, "unable to alloc mem for OMAP USB3 PHY\n");
-               return -ENOMEM;
-       }
-
-       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->dev                = &pdev->dev;
-
-       phy->phy.dev            = phy->dev;
-       phy->phy.label          = "omap-usb3";
-       phy->phy.init           = omap_usb3_init;
-       phy->phy.set_suspend    = omap_usb3_suspend;
-       phy->phy.type           = USB_PHY_TYPE_USB3;
-
-       phy->is_suspended       = 1;
-       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);
-       }
-       clk_prepare(phy->wkupclk);
-
-       phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
-       if (IS_ERR(phy->optclk)) {
-               dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n");
-               return PTR_ERR(phy->optclk);
-       }
-       clk_prepare(phy->optclk);
-
-       phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin");
-       if (IS_ERR(phy->sys_clk)) {
-               pr_err("%s: unable to get sys_clkin\n", __func__);
-               return -EINVAL;
-       }
-
-       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_usb_phy_power(phy->control_dev, 0);
-       usb_add_phy_dev(&phy->phy);
-
-       platform_set_drvdata(pdev, phy);
-
-       pm_runtime_enable(phy->dev);
-       pm_runtime_get(&pdev->dev);
-
-       return 0;
-}
-
-static int omap_usb3_remove(struct platform_device *pdev)
-{
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       clk_unprepare(phy->wkupclk);
-       clk_unprepare(phy->optclk);
-       usb_remove_phy(&phy->phy);
-       if (!pm_runtime_suspended(&pdev->dev))
-               pm_runtime_put(&pdev->dev);
-       pm_runtime_disable(&pdev->dev);
-
-       return 0;
-}
-
-#ifdef CONFIG_PM_RUNTIME
-
-static int omap_usb3_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);
-       clk_disable(phy->optclk);
-
-       return 0;
-}
-
-static int omap_usb3_runtime_resume(struct device *dev)
-{
-       u32 ret = 0;
-       struct platform_device  *pdev = to_platform_device(dev);
-       struct omap_usb *phy = platform_get_drvdata(pdev);
-
-       ret = clk_enable(phy->optclk);
-       if (ret) {
-               dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
-               goto err1;
-       }
-
-       ret = clk_enable(phy->wkupclk);
-       if (ret) {
-               dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
-               goto err2;
-       }
-
-       return 0;
-
-err2:
-       clk_disable(phy->optclk);
-
-err1:
-       return ret;
-}
-
-static const struct dev_pm_ops omap_usb3_pm_ops = {
-       SET_RUNTIME_PM_OPS(omap_usb3_runtime_suspend, omap_usb3_runtime_resume,
-               NULL)
-};
-
-#define DEV_PM_OPS     (&omap_usb3_pm_ops)
-#else
-#define DEV_PM_OPS     NULL
-#endif
-
-#ifdef CONFIG_OF
-static const struct of_device_id omap_usb3_id_table[] = {
-       { .compatible = "ti,omap-usb3" },
-       {}
-};
-MODULE_DEVICE_TABLE(of, omap_usb3_id_table);
-#endif
-
-static struct platform_driver omap_usb3_driver = {
-       .probe          = omap_usb3_probe,
-       .remove         = omap_usb3_remove,
-       .driver         = {
-               .name   = "omap-usb3",
-               .owner  = THIS_MODULE,
-               .pm     = DEV_PM_OPS,
-               .of_match_table = of_match_ptr(omap_usb3_id_table),
-       },
-};
-
-module_platform_driver(omap_usb3_driver);
-
-MODULE_ALIAS("platform: omap_usb3");
-MODULE_AUTHOR("Texas Instruments Inc.");
-MODULE_DESCRIPTION("OMAP USB3 phy driver");
-MODULE_LICENSE("GPL v2");
index 214172b68d5d7a54fa1d223c098fead15089fd7f..04778cf80d60cf0095a6311a0f19896554fb67ce 100644 (file)
@@ -27,7 +27,7 @@
 #include <linux/io.h>
 #include <linux/usb/musb-omap.h>
 #include <linux/usb/phy_companion.h>
-#include <linux/usb/omap_usb.h>
+#include <linux/phy/omap_usb.h>
 #include <linux/i2c/twl.h>
 #include <linux/regulator/consumer.h>
 #include <linux/err.h>
similarity index 68%
rename from include/linux/usb/omap_control_usb.h
rename to include/linux/phy/omap_control_phy.h
index 69ae383ee3cc5af06adf82561f8c0ee25a22f334..5450403c7546d4e9429468a9225cf62e0fa9d7f6 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * omap_control_usb.h - Header file for the USB part of control module.
+ * 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
  *
  */
 
-#ifndef __OMAP_CONTROL_USB_H__
-#define __OMAP_CONTROL_USB_H__
+#ifndef __OMAP_CONTROL_PHY_H__
+#define __OMAP_CONTROL_PHY_H__
 
-enum omap_control_usb_type {
+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 */
@@ -27,7 +27,7 @@ enum omap_control_usb_type {
        OMAP_CTRL_TYPE_AM437USB2, /* USB2 PHY, power e.g. AM437x */
 };
 
-struct omap_control_usb {
+struct omap_control_phy {
        struct device *dev;
 
        u32 __iomem *otghs_control;
@@ -36,7 +36,7 @@ struct omap_control_usb {
 
        struct clk *sys_clk;
 
-       enum omap_control_usb_type type;
+       enum omap_control_phy_type type;
 };
 
 enum omap_control_usb_mode {
@@ -54,14 +54,14 @@ enum omap_control_usb_mode {
 #define        OMAP_CTRL_DEV_SESSEND           BIT(3)
 #define        OMAP_CTRL_DEV_IDDIG             BIT(4)
 
-#define        OMAP_CTRL_USB_PWRCTL_CLK_CMD_MASK       0x003FC000
-#define        OMAP_CTRL_USB_PWRCTL_CLK_CMD_SHIFT      0xE
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_MASK         0x003FC000
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_CMD_SHIFT        0xE
 
-#define        OMAP_CTRL_USB_PWRCTL_CLK_FREQ_MASK      0xFFC00000
-#define        OMAP_CTRL_USB_PWRCTL_CLK_FREQ_SHIFT     0x16
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_MASK        0xFFC00000
+#define        OMAP_CTRL_PIPE3_PHY_PWRCTL_CLK_FREQ_SHIFT       0x16
 
-#define        OMAP_CTRL_USB3_PHY_TX_RX_POWERON        0x3
-#define        OMAP_CTRL_USB3_PHY_TX_RX_POWEROFF       0x0
+#define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWERON       0x3
+#define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
 
 #define OMAP_CTRL_USB2_PHY_PD          BIT(28)
 
@@ -70,13 +70,13 @@ enum omap_control_usb_mode {
 #define AM437X_CTRL_USB2_OTGVDET_EN    BIT(19)
 #define AM437X_CTRL_USB2_OTGSESSEND_EN BIT(20)
 
-#if IS_ENABLED(CONFIG_OMAP_CONTROL_USB)
-extern void omap_control_usb_phy_power(struct device *dev, int on);
-extern void omap_control_usb_set_mode(struct device *dev,
-       enum omap_control_usb_mode mode);
+#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);
 #else
 
-static inline void omap_control_usb_phy_power(struct device *dev, int on)
+static inline void omap_control_phy_power(struct device *dev, int on)
 {
 }
 
@@ -86,4 +86,4 @@ static inline void omap_control_usb_set_mode(struct device *dev,
 }
 #endif
 
-#endif /* __OMAP_CONTROL_USB_H__ */
+#endif /* __OMAP_CONTROL_PHY_H__ */
similarity index 86%
rename from include/linux/usb/omap_usb.h
rename to include/linux/phy/omap_usb.h
index 6ae29360e1d2a1be611a6ec188af9addc68cabd5..dc2c541a619b89eb45fbe2e2fa30af5a0b393506 100644 (file)
@@ -34,14 +34,24 @@ 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              *sys_clk;
        struct clk              *optclk;
-       u8                      is_suspended:1;
+       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)
index 3f83459dbb20b557ec067b9d14d8dbf1f1e56ba3..e2f5ca96cddc521fcc62a868039c8361f4e48997 100644 (file)
@@ -149,8 +149,11 @@ 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, const struct phy_ops *ops,
@@ -251,6 +254,13 @@ static inline struct phy *devm_phy_optional_get(struct device *dev,
        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)
 {
 }
@@ -259,6 +269,11 @@ 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)
 {