phy: merge and backport phy core from linux kernel 4.1
authorlyz <lyz@rock-chips.com>
Tue, 28 Apr 2015 09:49:12 +0000 (17:49 +0800)
committerlyz <lyz@rock-chips.com>
Wed, 6 May 2015 06:53:16 +0000 (14:53 +0800)
Signed-off-by: lyz <lyz@rock-chips.com>
41 files changed:
drivers/phy/Kconfig
drivers/phy/Makefile
drivers/phy/phy-armada375-usb2.c [new file with mode: 0644]
drivers/phy/phy-bcm-kona-usb2.c
drivers/phy/phy-berlin-sata.c
drivers/phy/phy-berlin-usb.c [new file with mode: 0644]
drivers/phy/phy-core.c
drivers/phy/phy-dm816x-usb.c [new file with mode: 0644]
drivers/phy/phy-exynos-dp-video.c
drivers/phy/phy-exynos-mipi-video.c
drivers/phy/phy-exynos5-usbdrd.c
drivers/phy/phy-exynos5250-sata.c
drivers/phy/phy-hix5hd2-sata.c
drivers/phy/phy-miphy28lp.c [new file with mode: 0644]
drivers/phy/phy-miphy365x.c
drivers/phy/phy-mvebu-sata.c
drivers/phy/phy-omap-control.c
drivers/phy/phy-omap-usb2.c
drivers/phy/phy-qcom-apq8064-sata.c
drivers/phy/phy-qcom-ipq806x-sata.c
drivers/phy/phy-qcom-ufs-i.h [new file with mode: 0644]
drivers/phy/phy-qcom-ufs-qmp-14nm.c [new file with mode: 0644]
drivers/phy/phy-qcom-ufs-qmp-14nm.h [new file with mode: 0644]
drivers/phy/phy-qcom-ufs-qmp-20nm.c [new file with mode: 0644]
drivers/phy/phy-qcom-ufs-qmp-20nm.h [new file with mode: 0644]
drivers/phy/phy-qcom-ufs.c [new file with mode: 0644]
drivers/phy/phy-rcar-gen2.c
drivers/phy/phy-rockchip-usb.c
drivers/phy/phy-samsung-usb2.c
drivers/phy/phy-spear1310-miphy.c
drivers/phy/phy-spear1340-miphy.c
drivers/phy/phy-stih407-usb.c
drivers/phy/phy-stih41x-usb.c
drivers/phy/phy-sun4i-usb.c
drivers/phy/phy-sun9i-usb.c [new file with mode: 0644]
drivers/phy/phy-ti-pipe3.c
drivers/phy/phy-twl4030-usb.c
drivers/phy/phy-xgene.c
include/linux/phy/omap_control_phy.h
include/linux/phy/phy-qcom-ufs.h [new file with mode: 0644]
include/linux/phy/phy.h

index 2a436e607f997462d29558e49bacbb53cc8b7ea0..a53bd5b52df97ff48fa921a5009f2fa6937aa377 100644 (file)
@@ -15,6 +15,13 @@ config GENERIC_PHY
          phy users can obtain reference to the PHY. All the users of this
          framework should select this config.
 
          phy users can obtain reference to the PHY. All the users of this
          framework should select this config.
 
+config PHY_BERLIN_USB
+       tristate "Marvell Berlin USB PHY Driver"
+       depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
+       select GENERIC_PHY
+       help
+         Enable this to support the USB PHY on Marvell Berlin SoCs.
+
 config PHY_BERLIN_SATA
        tristate "Marvell Berlin SATA PHY driver"
        depends on ARCH_BERLIN && HAS_IOMEM && OF
 config PHY_BERLIN_SATA
        tristate "Marvell Berlin SATA PHY driver"
        depends on ARCH_BERLIN && HAS_IOMEM && OF
@@ -22,6 +29,19 @@ config PHY_BERLIN_SATA
        help
          Enable this to support the SATA PHY on Marvell Berlin SoCs.
 
        help
          Enable this to support the SATA PHY on Marvell Berlin SoCs.
 
+config ARMADA375_USBCLUSTER_PHY
+       def_bool y
+       depends on MACH_ARMADA_375 || COMPILE_TEST
+       depends on OF
+       select GENERIC_PHY
+
+config PHY_DM816X_USB
+       tristate "TI dm816x USB PHY driver"
+       depends on ARCH_OMAP2PLUS
+       select GENERIC_PHY
+       help
+         Enable this for dm816x USB to work.
+
 config PHY_EXYNOS_MIPI_VIDEO
        tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
        depends on HAS_IOMEM
 config PHY_EXYNOS_MIPI_VIDEO
        tristate "S5P/EXYNOS SoC series MIPI CSI-2/DSI PHY driver"
        depends on HAS_IOMEM
@@ -38,6 +58,14 @@ config PHY_MVEBU_SATA
        depends on OF
        select GENERIC_PHY
 
        depends on OF
        select GENERIC_PHY
 
+config PHY_MIPHY28LP
+       tristate "STMicroelectronics MIPHY28LP PHY driver for STiH407"
+       depends on ARCH_STI
+       select GENERIC_PHY
+       help
+         Enable this to support the miphy transceiver (for SATA/PCIE/USB3)
+         that is part of STMicroelectronics STiH407 SoC.
+
 config PHY_MIPHY365X
        tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
        depends on ARCH_STI
 config PHY_MIPHY365X
        tristate "STMicroelectronics MIPHY365X PHY driver for STiH41x series"
        depends on ARCH_STI
@@ -153,6 +181,17 @@ config PHY_SUN4I_USB
          This driver controls the entire USB PHY block, both the USB OTG
          parts, as well as the 2 regular USB 2 host PHYs.
 
          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_SUN9I_USB
+       tristate "Allwinner sun9i SoC USB PHY driver"
+       depends on ARCH_SUNXI && HAS_IOMEM && OF
+       depends on RESET_CONTROLLER
+       select GENERIC_PHY
+       help
+         Enable this to support the transceiver that is part of Allwinner
+         sun9i SoCs.
+
+         This driver controls each individual USB 2 host PHY.
+
 config PHY_SAMSUNG_USB2
        tristate "Samsung USB 2.0 PHY driver"
        depends on HAS_IOMEM
 config PHY_SAMSUNG_USB2
        tristate "Samsung USB 2.0 PHY driver"
        depends on HAS_IOMEM
@@ -193,7 +232,7 @@ config PHY_EXYNOS5250_USB2
 
 config PHY_EXYNOS5_USBDRD
        tristate "Exynos5 SoC series USB DRD PHY driver"
 
 config PHY_EXYNOS5_USBDRD
        tristate "Exynos5 SoC series USB DRD PHY driver"
-       depends on ARCH_EXYNOS5 && OF
+       depends on ARCH_EXYNOS && OF
        depends on HAS_IOMEM
        depends on USB_DWC3_EXYNOS
        select GENERIC_PHY
        depends on HAS_IOMEM
        depends on USB_DWC3_EXYNOS
        select GENERIC_PHY
@@ -218,6 +257,13 @@ config PHY_QCOM_IPQ806X_SATA
        depends on OF
        select GENERIC_PHY
 
        depends on OF
        select GENERIC_PHY
 
+config PHY_ROCKCHIP_USB
+       tristate "Rockchip USB2 PHY Driver"
+       depends on ARCH_ROCKCHIP && OF
+       select GENERIC_PHY
+       help
+         Enable this to support the Rockchip USB 2.0 PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
        tristate "ST SPEAR1310-MIPHY driver"
        select GENERIC_PHY
 config PHY_ST_SPEAR1310_MIPHY
        tristate "ST SPEAR1310-MIPHY driver"
        select GENERIC_PHY
@@ -256,4 +302,11 @@ config PHY_STIH41X_USB
          Enable this to support the USB transceiver that is part of
          STMicroelectronics STiH41x SoC series.
 
          Enable this to support the USB transceiver that is part of
          STMicroelectronics STiH41x SoC series.
 
+config PHY_QCOM_UFS
+       tristate "Qualcomm UFS PHY driver"
+       depends on OF && ARCH_MSM
+       select GENERIC_PHY
+       help
+         Support for UFS PHY on QCOM chipsets.
+
 endmenu
 endmenu
index c4590fce082fac512008dec40656128ad0a21603..f1262517878008a587a2a8b2c52ef97b1c8103b7 100644 (file)
@@ -3,11 +3,15 @@
 #
 
 obj-$(CONFIG_GENERIC_PHY)              += phy-core.o
 #
 
 obj-$(CONFIG_GENERIC_PHY)              += phy-core.o
+obj-$(CONFIG_PHY_BERLIN_USB)           += phy-berlin-usb.o
 obj-$(CONFIG_PHY_BERLIN_SATA)          += phy-berlin-sata.o
 obj-$(CONFIG_PHY_BERLIN_SATA)          += phy-berlin-sata.o
+obj-$(CONFIG_PHY_DM816X_USB)           += phy-dm816x-usb.o
+obj-$(CONFIG_ARMADA375_USBCLUSTER_PHY) += phy-armada375-usb2.o
 obj-$(CONFIG_BCM_KONA_USB2_PHY)                += phy-bcm-kona-usb2.o
 obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)      += phy-exynos-dp-video.o
 obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)    += phy-exynos-mipi-video.o
 obj-$(CONFIG_PHY_MVEBU_SATA)           += phy-mvebu-sata.o
 obj-$(CONFIG_BCM_KONA_USB2_PHY)                += phy-bcm-kona-usb2.o
 obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO)      += phy-exynos-dp-video.o
 obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO)    += phy-exynos-mipi-video.o
 obj-$(CONFIG_PHY_MVEBU_SATA)           += phy-mvebu-sata.o
+obj-$(CONFIG_PHY_MIPHY28LP)            += phy-miphy28lp.o
 obj-$(CONFIG_PHY_MIPHY365X)            += phy-miphy365x.o
 obj-$(CONFIG_PHY_RCAR_GEN2)            += phy-rcar-gen2.o
 obj-$(CONFIG_OMAP_CONTROL_PHY)         += phy-omap-control.o
 obj-$(CONFIG_PHY_MIPHY365X)            += phy-miphy365x.o
 obj-$(CONFIG_PHY_RCAR_GEN2)            += phy-rcar-gen2.o
 obj-$(CONFIG_OMAP_CONTROL_PHY)         += phy-omap-control.o
@@ -17,6 +21,7 @@ obj-$(CONFIG_TWL4030_USB)             += phy-twl4030-usb.o
 obj-$(CONFIG_PHY_EXYNOS5250_SATA)      += phy-exynos5250-sata.o
 obj-$(CONFIG_PHY_HIX5HD2_SATA)         += phy-hix5hd2-sata.o
 obj-$(CONFIG_PHY_SUN4I_USB)            += phy-sun4i-usb.o
 obj-$(CONFIG_PHY_EXYNOS5250_SATA)      += phy-exynos5250-sata.o
 obj-$(CONFIG_PHY_HIX5HD2_SATA)         += phy-hix5hd2-sata.o
 obj-$(CONFIG_PHY_SUN4I_USB)            += phy-sun4i-usb.o
+obj-$(CONFIG_PHY_SUN9I_USB)            += phy-sun9i-usb.o
 obj-$(CONFIG_PHY_SAMSUNG_USB2)         += phy-exynos-usb2.o
 phy-exynos-usb2-y                      += phy-samsung-usb2.o
 phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2)  += phy-exynos4210-usb2.o
 obj-$(CONFIG_PHY_SAMSUNG_USB2)         += phy-exynos-usb2.o
 phy-exynos-usb2-y                      += phy-samsung-usb2.o
 phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2)  += phy-exynos4210-usb2.o
@@ -25,9 +30,13 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2)        += phy-exynos5250-usb2.o
 phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)     += phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)       += phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)    += phy-qcom-apq8064-sata.o
 phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)     += phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)       += phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)    += phy-qcom-apq8064-sata.o
+obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)    += phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)   += phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)   += phy-spear1340-miphy.o
 obj-$(CONFIG_PHY_XGENE)                        += phy-xgene.o
 obj-$(CONFIG_PHY_STIH407_USB)          += phy-stih407-usb.o
 obj-$(CONFIG_PHY_STIH41X_USB)          += phy-stih41x-usb.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)    += phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)   += phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)   += phy-spear1340-miphy.o
 obj-$(CONFIG_PHY_XGENE)                        += phy-xgene.o
 obj-$(CONFIG_PHY_STIH407_USB)          += phy-stih407-usb.o
 obj-$(CONFIG_PHY_STIH41X_USB)          += phy-stih41x-usb.o
+obj-$(CONFIG_PHY_QCOM_UFS)     += phy-qcom-ufs.o
+obj-$(CONFIG_PHY_QCOM_UFS)     += phy-qcom-ufs-qmp-20nm.o
+obj-$(CONFIG_PHY_QCOM_UFS)     += phy-qcom-ufs-qmp-14nm.o
diff --git a/drivers/phy/phy-armada375-usb2.c b/drivers/phy/phy-armada375-usb2.c
new file mode 100644 (file)
index 0000000..7c99ca2
--- /dev/null
@@ -0,0 +1,158 @@
+/*
+ * USB cluster support for Armada 375 platform.
+ *
+ * Copyright (C) 2014 Marvell
+ *
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2 or later. This program is licensed "as is"
+ * without any warranty of any kind, whether express or implied.
+ *
+ * Armada 375 comes with an USB2 host and device controller and an
+ * USB3 controller. The USB cluster control register allows to manage
+ * common features of both USB controllers.
+ */
+
+#include <dt-bindings/phy/phy.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+
+#define USB2_PHY_CONFIG_DISABLE BIT(0)
+
+struct armada375_cluster_phy {
+       struct phy *phy;
+       void __iomem *reg;
+       bool use_usb3;
+       int phy_provided;
+};
+
+static int armada375_usb_phy_init(struct phy *phy)
+{
+       struct armada375_cluster_phy *cluster_phy;
+       u32 reg;
+
+       cluster_phy = dev_get_drvdata(phy->dev.parent);
+       if (!cluster_phy)
+               return -ENODEV;
+
+       reg = readl(cluster_phy->reg);
+       if (cluster_phy->use_usb3)
+               reg |= USB2_PHY_CONFIG_DISABLE;
+       else
+               reg &= ~USB2_PHY_CONFIG_DISABLE;
+       writel(reg, cluster_phy->reg);
+
+       return 0;
+}
+
+static struct phy_ops armada375_usb_phy_ops = {
+       .init = armada375_usb_phy_init,
+       .owner = THIS_MODULE,
+};
+
+/*
+ * Only one controller can use this PHY. We shouldn't have the case
+ * when two controllers want to use this PHY. But if this case occurs
+ * then we provide a phy to the first one and return an error for the
+ * next one. This error has also to be an error returned by
+ * devm_phy_optional_get() so different from ENODEV for USB2. In the
+ * USB3 case it still optional and we use ENODEV.
+ */
+static struct phy *armada375_usb_phy_xlate(struct device *dev,
+                                       struct of_phandle_args *args)
+{
+       struct armada375_cluster_phy *cluster_phy = dev_get_drvdata(dev);
+
+       if (!cluster_phy)
+               return  ERR_PTR(-ENODEV);
+
+       /*
+        * Either the phy had never been requested and then the first
+        * usb claiming it can get it, or it had already been
+        * requested in this case, we only allow to use it with the
+        * same configuration.
+        */
+       if (WARN_ON((cluster_phy->phy_provided != PHY_NONE) &&
+                       (cluster_phy->phy_provided != args->args[0]))) {
+               dev_err(dev, "This PHY has already been provided!\n");
+               dev_err(dev, "Check your device tree, only one controller can use it\n.");
+               if (args->args[0] == PHY_TYPE_USB2)
+                       return ERR_PTR(-EBUSY);
+               else
+                       return ERR_PTR(-ENODEV);
+       }
+
+       if (args->args[0] == PHY_TYPE_USB2)
+               cluster_phy->use_usb3 = false;
+       else if (args->args[0] == PHY_TYPE_USB3)
+               cluster_phy->use_usb3 = true;
+       else {
+               dev_err(dev, "Invalid PHY mode\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       /* Store which phy mode is used for next test */
+       cluster_phy->phy_provided = args->args[0];
+
+       return cluster_phy->phy;
+}
+
+static int armada375_usb_phy_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *phy;
+       struct phy_provider *phy_provider;
+       void __iomem *usb_cluster_base;
+       struct resource *res;
+       struct armada375_cluster_phy *cluster_phy;
+
+       cluster_phy = devm_kzalloc(dev, sizeof(*cluster_phy), GFP_KERNEL);
+       if (!cluster_phy)
+               return  -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(usb_cluster_base))
+               return PTR_ERR(usb_cluster_base);
+
+       phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
+       if (IS_ERR(phy)) {
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       cluster_phy->phy = phy;
+       cluster_phy->reg = usb_cluster_base;
+
+       dev_set_drvdata(dev, cluster_phy);
+
+       phy_provider = devm_of_phy_provider_register(&pdev->dev,
+                                                    armada375_usb_phy_xlate);
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id of_usb_cluster_table[] = {
+       { .compatible = "marvell,armada-375-usb-cluster", },
+       { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, of_usb_cluster_table);
+
+static struct platform_driver armada375_usb_phy_driver = {
+       .probe  = armada375_usb_phy_probe,
+       .driver = {
+               .of_match_table = of_usb_cluster_table,
+               .name  = "armada-375-usb-cluster",
+               .owner = THIS_MODULE,
+       }
+};
+module_platform_driver(armada375_usb_phy_driver);
+
+MODULE_DESCRIPTION("Armada 375 USB cluster driver");
+MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@free-electrons.com>");
+MODULE_LICENSE("GPL");
index c1e0ca335c0ea5eb33588bb270ff0d9ae7435d42..ef2dc1aab2b9b5ed03cf0511da89c6da1f78393d 100644 (file)
@@ -117,7 +117,7 @@ static int bcm_kona_usb2_probe(struct platform_device *pdev)
 
        platform_set_drvdata(pdev, phy);
 
 
        platform_set_drvdata(pdev, phy);
 
-       gphy = devm_phy_create(dev, NULL, &ops, NULL);
+       gphy = devm_phy_create(dev, NULL, &ops);
        if (IS_ERR(gphy))
                return PTR_ERR(gphy);
 
        if (IS_ERR(gphy))
                return PTR_ERR(gphy);
 
index 69ced52d72aa7be906d92ecae3ef962c647586f2..6f3e06d687de99260b0300e10d0864387d740177 100644 (file)
@@ -30,7 +30,8 @@
 #define MBUS_WRITE_REQUEST_SIZE_128    (BIT(2) << 16)
 #define MBUS_READ_REQUEST_SIZE_128     (BIT(2) << 19)
 
 #define MBUS_WRITE_REQUEST_SIZE_128    (BIT(2) << 16)
 #define MBUS_READ_REQUEST_SIZE_128     (BIT(2) << 19)
 
-#define PHY_BASE               0x200
+#define BG2_PHY_BASE           0x080
+#define BG2Q_PHY_BASE          0x200
 
 /* register 0x01 */
 #define REF_FREF_SEL_25                BIT(0)
 
 /* register 0x01 */
 #define REF_FREF_SEL_25                BIT(0)
@@ -61,15 +62,16 @@ struct phy_berlin_priv {
        struct clk              *clk;
        struct phy_berlin_desc  **phys;
        unsigned                nphys;
        struct clk              *clk;
        struct phy_berlin_desc  **phys;
        unsigned                nphys;
+       u32                     phy_base;
 };
 
 };
 
-static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg, u32 reg,
-                                              u32 mask, u32 val)
+static inline void phy_berlin_sata_reg_setbits(void __iomem *ctrl_reg,
+                              u32 phy_base, u32 reg, u32 mask, u32 val)
 {
        u32 regval;
 
        /* select register */
 {
        u32 regval;
 
        /* select register */
-       writel(PHY_BASE + reg, ctrl_reg + PORT_VSR_ADDR);
+       writel(phy_base + reg, ctrl_reg + PORT_VSR_ADDR);
 
        /* set bits */
        regval = readl(ctrl_reg + PORT_VSR_DATA);
 
        /* set bits */
        regval = readl(ctrl_reg + PORT_VSR_DATA);
@@ -103,17 +105,20 @@ static int phy_berlin_sata_power_on(struct phy *phy)
        writel(regval, priv->base + HOST_VSA_DATA);
 
        /* set PHY mode and ref freq to 25 MHz */
        writel(regval, priv->base + HOST_VSA_DATA);
 
        /* set PHY mode and ref freq to 25 MHz */
-       phy_berlin_sata_reg_setbits(ctrl_reg, 0x1, 0xff,
-                                   REF_FREF_SEL_25 | PHY_MODE_SATA);
+       phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x01,
+                                   0x00ff, REF_FREF_SEL_25 | PHY_MODE_SATA);
 
        /* set PHY up to 6 Gbps */
 
        /* set PHY up to 6 Gbps */
-       phy_berlin_sata_reg_setbits(ctrl_reg, 0x25, 0xc00, PHY_GEN_MAX_6_0);
+       phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x25,
+                                   0x0c00, PHY_GEN_MAX_6_0);
 
        /* set 40 bits width */
 
        /* set 40 bits width */
-       phy_berlin_sata_reg_setbits(ctrl_reg, 0x23,  0xc00, DATA_BIT_WIDTH_40);
+       phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x23,
+                                   0x0c00, DATA_BIT_WIDTH_40);
 
        /* use max pll rate */
 
        /* use max pll rate */
-       phy_berlin_sata_reg_setbits(ctrl_reg, 0x2, 0x0, USE_MAX_PLL_RATE);
+       phy_berlin_sata_reg_setbits(ctrl_reg, priv->phy_base, 0x02,
+                                   0x0000, USE_MAX_PLL_RATE);
 
        /* set Gen3 controller speed */
        regval = readl(ctrl_reg + PORT_SCR_CTL);
 
        /* set Gen3 controller speed */
        regval = readl(ctrl_reg + PORT_SCR_CTL);
@@ -213,11 +218,16 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
        if (priv->nphys == 0)
                return -ENODEV;
 
        if (priv->nphys == 0)
                return -ENODEV;
 
-       priv->phys = devm_kzalloc(dev, priv->nphys * sizeof(*priv->phys),
+       priv->phys = devm_kcalloc(dev, priv->nphys, sizeof(*priv->phys),
                                  GFP_KERNEL);
        if (!priv->phys)
                return -ENOMEM;
 
                                  GFP_KERNEL);
        if (!priv->phys)
                return -ENOMEM;
 
+       if (of_device_is_compatible(dev->of_node, "marvell,berlin2-sata-phy"))
+               priv->phy_base = BG2_PHY_BASE;
+       else
+               priv->phy_base = BG2Q_PHY_BASE;
+
        dev_set_drvdata(dev, priv);
        spin_lock_init(&priv->lock);
 
        dev_set_drvdata(dev, priv);
        spin_lock_init(&priv->lock);
 
@@ -239,7 +249,7 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
                if (!phy_desc)
                        return -ENOMEM;
 
                if (!phy_desc)
                        return -ENOMEM;
 
-               phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops, NULL);
+               phy = devm_phy_create(dev, NULL, &phy_berlin_sata_ops);
                if (IS_ERR(phy)) {
                        dev_err(dev, "failed to create PHY %d\n", phy_id);
                        return PTR_ERR(phy);
                if (IS_ERR(phy)) {
                        dev_err(dev, "failed to create PHY %d\n", phy_id);
                        return PTR_ERR(phy);
@@ -258,13 +268,11 @@ static int phy_berlin_sata_probe(struct platform_device *pdev)
 
        phy_provider =
                devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
 
        phy_provider =
                devm_of_phy_provider_register(dev, phy_berlin_sata_phy_xlate);
-       if (IS_ERR(phy_provider))
-               return PTR_ERR(phy_provider);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(phy_provider);
 }
 
 static const struct of_device_id phy_berlin_sata_of_match[] = {
 }
 
 static const struct of_device_id phy_berlin_sata_of_match[] = {
+       { .compatible = "marvell,berlin2-sata-phy" },
        { .compatible = "marvell,berlin2q-sata-phy" },
        { },
 };
        { .compatible = "marvell,berlin2q-sata-phy" },
        { },
 };
diff --git a/drivers/phy/phy-berlin-usb.c b/drivers/phy/phy-berlin-usb.c
new file mode 100644 (file)
index 0000000..c6fc95b
--- /dev/null
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ * Jisheng Zhang <jszhang@marvell.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define USB_PHY_PLL            0x04
+#define USB_PHY_PLL_CONTROL    0x08
+#define USB_PHY_TX_CTRL0       0x10
+#define USB_PHY_TX_CTRL1       0x14
+#define USB_PHY_TX_CTRL2       0x18
+#define USB_PHY_RX_CTRL                0x20
+#define USB_PHY_ANALOG         0x34
+
+/* USB_PHY_PLL */
+#define CLK_REF_DIV(x)         ((x) << 4)
+#define FEEDBACK_CLK_DIV(x)    ((x) << 8)
+
+/* USB_PHY_PLL_CONTROL */
+#define CLK_STABLE             BIT(0)
+#define PLL_CTRL_PIN           BIT(1)
+#define PLL_CTRL_REG           BIT(2)
+#define PLL_ON                 BIT(3)
+#define PHASE_OFF_TOL_125      (0x0 << 5)
+#define PHASE_OFF_TOL_250      BIT(5)
+#define KVC0_CALIB             (0x0 << 9)
+#define KVC0_REG_CTRL          BIT(9)
+#define KVC0_HIGH              (0x0 << 10)
+#define KVC0_LOW               (0x3 << 10)
+#define CLK_BLK_EN             BIT(13)
+
+/* USB_PHY_TX_CTRL0 */
+#define EXT_HS_RCAL_EN         BIT(3)
+#define EXT_FS_RCAL_EN         BIT(4)
+#define IMPCAL_VTH_DIV(x)      ((x) << 5)
+#define EXT_RS_RCAL_DIV(x)     ((x) << 8)
+#define EXT_FS_RCAL_DIV(x)     ((x) << 12)
+
+/* USB_PHY_TX_CTRL1 */
+#define TX_VDD15_14            (0x0 << 4)
+#define TX_VDD15_15            BIT(4)
+#define TX_VDD15_16            (0x2 << 4)
+#define TX_VDD15_17            (0x3 << 4)
+#define TX_VDD12_VDD           (0x0 << 6)
+#define TX_VDD12_11            BIT(6)
+#define TX_VDD12_12            (0x2 << 6)
+#define TX_VDD12_13            (0x3 << 6)
+#define LOW_VDD_EN             BIT(8)
+#define TX_OUT_AMP(x)          ((x) << 9)
+
+/* USB_PHY_TX_CTRL2 */
+#define TX_CHAN_CTRL_REG(x)    ((x) << 0)
+#define DRV_SLEWRATE(x)                ((x) << 4)
+#define IMP_CAL_FS_HS_DLY_0    (0x0 << 6)
+#define IMP_CAL_FS_HS_DLY_1    BIT(6)
+#define IMP_CAL_FS_HS_DLY_2    (0x2 << 6)
+#define IMP_CAL_FS_HS_DLY_3    (0x3 << 6)
+#define FS_DRV_EN_MASK(x)      ((x) << 8)
+#define HS_DRV_EN_MASK(x)      ((x) << 12)
+
+/* USB_PHY_RX_CTRL */
+#define PHASE_FREEZE_DLY_2_CL  (0x0 << 0)
+#define PHASE_FREEZE_DLY_4_CL  BIT(0)
+#define ACK_LENGTH_8_CL                (0x0 << 2)
+#define ACK_LENGTH_12_CL       BIT(2)
+#define ACK_LENGTH_16_CL       (0x2 << 2)
+#define ACK_LENGTH_20_CL       (0x3 << 2)
+#define SQ_LENGTH_3            (0x0 << 4)
+#define SQ_LENGTH_6            BIT(4)
+#define SQ_LENGTH_9            (0x2 << 4)
+#define SQ_LENGTH_12           (0x3 << 4)
+#define DISCON_THRESHOLD_260   (0x0 << 6)
+#define DISCON_THRESHOLD_270   BIT(6)
+#define DISCON_THRESHOLD_280   (0x2 << 6)
+#define DISCON_THRESHOLD_290   (0x3 << 6)
+#define SQ_THRESHOLD(x)                ((x) << 8)
+#define LPF_COEF(x)            ((x) << 12)
+#define INTPL_CUR_10           (0x0 << 14)
+#define INTPL_CUR_20           BIT(14)
+#define INTPL_CUR_30           (0x2 << 14)
+#define INTPL_CUR_40           (0x3 << 14)
+
+/* USB_PHY_ANALOG */
+#define ANA_PWR_UP             BIT(1)
+#define ANA_PWR_DOWN           BIT(2)
+#define V2I_VCO_RATIO(x)       ((x) << 7)
+#define R_ROTATE_90            (0x0 << 10)
+#define R_ROTATE_0             BIT(10)
+#define MODE_TEST_EN           BIT(11)
+#define ANA_TEST_DC_CTRL(x)    ((x) << 12)
+
+static const u32 phy_berlin_pll_dividers[] = {
+       /* Berlin 2 */
+       CLK_REF_DIV(0xc) | FEEDBACK_CLK_DIV(0x54),
+       /* Berlin 2CD */
+       CLK_REF_DIV(0x6) | FEEDBACK_CLK_DIV(0x55),
+};
+
+struct phy_berlin_usb_priv {
+       void __iomem            *base;
+       struct reset_control    *rst_ctrl;
+       u32                     pll_divider;
+};
+
+static int phy_berlin_usb_power_on(struct phy *phy)
+{
+       struct phy_berlin_usb_priv *priv = phy_get_drvdata(phy);
+
+       reset_control_reset(priv->rst_ctrl);
+
+       writel(priv->pll_divider,
+              priv->base + USB_PHY_PLL);
+       writel(CLK_STABLE | PLL_CTRL_REG | PHASE_OFF_TOL_250 | KVC0_REG_CTRL |
+              CLK_BLK_EN, priv->base + USB_PHY_PLL_CONTROL);
+       writel(V2I_VCO_RATIO(0x5) | R_ROTATE_0 | ANA_TEST_DC_CTRL(0x5),
+              priv->base + USB_PHY_ANALOG);
+       writel(PHASE_FREEZE_DLY_4_CL | ACK_LENGTH_16_CL | SQ_LENGTH_12 |
+              DISCON_THRESHOLD_260 | SQ_THRESHOLD(0xa) | LPF_COEF(0x2) |
+              INTPL_CUR_30, priv->base + USB_PHY_RX_CTRL);
+
+       writel(TX_VDD12_13 | TX_OUT_AMP(0x3), priv->base + USB_PHY_TX_CTRL1);
+       writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
+              priv->base + USB_PHY_TX_CTRL0);
+
+       writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4) |
+              EXT_FS_RCAL_DIV(0x2), priv->base + USB_PHY_TX_CTRL0);
+
+       writel(EXT_HS_RCAL_EN | IMPCAL_VTH_DIV(0x3) | EXT_RS_RCAL_DIV(0x4),
+              priv->base + USB_PHY_TX_CTRL0);
+       writel(TX_CHAN_CTRL_REG(0xf) | DRV_SLEWRATE(0x3) | IMP_CAL_FS_HS_DLY_3 |
+              FS_DRV_EN_MASK(0xd), priv->base + USB_PHY_TX_CTRL2);
+
+       return 0;
+}
+
+static struct phy_ops phy_berlin_usb_ops = {
+       .power_on       = phy_berlin_usb_power_on,
+       .owner          = THIS_MODULE,
+};
+
+static const struct of_device_id phy_berlin_sata_of_match[] = {
+       {
+               .compatible = "marvell,berlin2-usb-phy",
+               .data = &phy_berlin_pll_dividers[0],
+       },
+       {
+               .compatible = "marvell,berlin2cd-usb-phy",
+               .data = &phy_berlin_pll_dividers[1],
+       },
+       { },
+};
+MODULE_DEVICE_TABLE(of, phy_berlin_sata_of_match);
+
+static int phy_berlin_usb_probe(struct platform_device *pdev)
+{
+       const struct of_device_id *match =
+               of_match_device(phy_berlin_sata_of_match, &pdev->dev);
+       struct phy_berlin_usb_priv *priv;
+       struct resource *res;
+       struct phy *phy;
+       struct phy_provider *phy_provider;
+
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       priv->base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(priv->base))
+               return PTR_ERR(priv->base);
+
+       priv->rst_ctrl = devm_reset_control_get(&pdev->dev, NULL);
+       if (IS_ERR(priv->rst_ctrl))
+               return PTR_ERR(priv->rst_ctrl);
+
+       priv->pll_divider = *((u32 *)match->data);
+
+       phy = devm_phy_create(&pdev->dev, NULL, &phy_berlin_usb_ops);
+       if (IS_ERR(phy)) {
+               dev_err(&pdev->dev, "failed to create PHY\n");
+               return PTR_ERR(phy);
+       }
+
+       platform_set_drvdata(pdev, priv);
+       phy_set_drvdata(phy, priv);
+
+       phy_provider =
+               devm_of_phy_provider_register(&pdev->dev, of_phy_simple_xlate);
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static struct platform_driver phy_berlin_usb_driver = {
+       .probe  = phy_berlin_usb_probe,
+       .driver = {
+               .name           = "phy-berlin-usb",
+               .owner          = THIS_MODULE,
+               .of_match_table = phy_berlin_sata_of_match,
+        },
+};
+module_platform_driver(phy_berlin_usb_driver);
+
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_DESCRIPTION("Marvell Berlin PHY driver for USB");
+MODULE_LICENSE("GPL");
index ff5eec5af8173d4b4356152a63f71f3e9d2741c3..a12d35338313bd4f4a67117f3c5c3f76030e53fd 100644 (file)
@@ -26,6 +26,7 @@
 static struct class *phy_class;
 static DEFINE_MUTEX(phy_provider_mutex);
 static LIST_HEAD(phy_provider_list);
 static struct class *phy_class;
 static DEFINE_MUTEX(phy_provider_mutex);
 static LIST_HEAD(phy_provider_list);
+static LIST_HEAD(phys);
 static DEFINE_IDA(phy_ida);
 
 static void devm_phy_release(struct device *dev, void *res)
 static DEFINE_IDA(phy_ida);
 
 static void devm_phy_release(struct device *dev, void *res)
@@ -54,34 +55,79 @@ static int devm_phy_match(struct device *dev, void *res, void *match_data)
        return res == match_data;
 }
 
        return res == match_data;
 }
 
-static struct phy *phy_lookup(struct device *device, const char *port)
+/**
+ * phy_create_lookup() - allocate and register PHY/device association
+ * @phy: the phy of the association
+ * @con_id: connection ID string on device
+ * @dev_id: the device of the association
+ *
+ * Creates and registers phy_lookup entry.
+ */
+int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id)
 {
 {
-       unsigned int count;
-       struct phy *phy;
-       struct device *dev;
-       struct phy_consumer *consumers;
-       struct class_dev_iter iter;
+       struct phy_lookup *pl;
 
 
-       class_dev_iter_init(&iter, phy_class, NULL, NULL);
-       while ((dev = class_dev_iter_next(&iter))) {
-               phy = to_phy(dev);
+       if (!phy || !dev_id || !con_id)
+               return -EINVAL;
 
 
-               if (!phy->init_data)
-                       continue;
-               count = phy->init_data->num_consumers;
-               consumers = phy->init_data->consumers;
-               while (count--) {
-                       if (!strcmp(consumers->dev_name, dev_name(device)) &&
-                                       !strcmp(consumers->port, port)) {
-                               class_dev_iter_exit(&iter);
-                               return phy;
-                       }
-                       consumers++;
+       pl = kzalloc(sizeof(*pl), GFP_KERNEL);
+       if (!pl)
+               return -ENOMEM;
+
+       pl->dev_id = dev_id;
+       pl->con_id = con_id;
+       pl->phy = phy;
+
+       mutex_lock(&phy_provider_mutex);
+       list_add_tail(&pl->node, &phys);
+       mutex_unlock(&phy_provider_mutex);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(phy_create_lookup);
+
+/**
+ * phy_remove_lookup() - find and remove PHY/device association
+ * @phy: the phy of the association
+ * @con_id: connection ID string on device
+ * @dev_id: the device of the association
+ *
+ * Finds and unregisters phy_lookup entry that was created with
+ * phy_create_lookup().
+ */
+void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id)
+{
+       struct phy_lookup *pl;
+
+       if (!phy || !dev_id || !con_id)
+               return;
+
+       mutex_lock(&phy_provider_mutex);
+       list_for_each_entry(pl, &phys, node)
+               if (pl->phy == phy && !strcmp(pl->dev_id, dev_id) &&
+                   !strcmp(pl->con_id, con_id)) {
+                       list_del(&pl->node);
+                       kfree(pl);
+                       break;
                }
                }
-       }
+       mutex_unlock(&phy_provider_mutex);
+}
+EXPORT_SYMBOL_GPL(phy_remove_lookup);
 
 
-       class_dev_iter_exit(&iter);
-       return ERR_PTR(-ENODEV);
+static struct phy *phy_find(struct device *dev, const char *con_id)
+{
+       const char *dev_id = dev_name(dev);
+       struct phy_lookup *p, *pl = NULL;
+
+       mutex_lock(&phy_provider_mutex);
+       list_for_each_entry(p, &phys, node)
+               if (!strcmp(p->dev_id, dev_id) && !strcmp(p->con_id, con_id)) {
+                       pl = p;
+                       break;
+               }
+       mutex_unlock(&phy_provider_mutex);
+
+       return pl ? pl->phy : ERR_PTR(-ENODEV);
 }
 
 static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
 }
 
 static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
@@ -414,21 +460,13 @@ struct phy *of_phy_simple_xlate(struct device *dev, struct of_phandle_args
 {
        struct phy *phy;
        struct class_dev_iter iter;
 {
        struct phy *phy;
        struct class_dev_iter iter;
-       struct device_node *node = dev->of_node;
-       struct device_node *child;
 
        class_dev_iter_init(&iter, phy_class, NULL, NULL);
        while ((dev = class_dev_iter_next(&iter))) {
                phy = to_phy(dev);
 
        class_dev_iter_init(&iter, phy_class, NULL, NULL);
        while ((dev = class_dev_iter_next(&iter))) {
                phy = to_phy(dev);
-               if (node != phy->dev.of_node) {
-                       for_each_child_of_node(node, child) {
-                               if (child == phy->dev.of_node)
-                                       goto phy_found;
-                       }
+               if (args->np != phy->dev.of_node)
                        continue;
                        continue;
-               }
 
 
-phy_found:
                class_dev_iter_exit(&iter);
                return phy;
        }
                class_dev_iter_exit(&iter);
                return phy;
        }
@@ -463,7 +501,7 @@ struct phy *phy_get(struct device *dev, const char *string)
                        string);
                phy = _of_phy_get(dev->of_node, index);
        } else {
                        string);
                phy = _of_phy_get(dev->of_node, index);
        } else {
-               phy = phy_lookup(dev, string);
+               phy = phy_find(dev, string);
        }
        if (IS_ERR(phy))
                return phy;
        }
        if (IS_ERR(phy))
                return phy;
@@ -588,13 +626,11 @@ EXPORT_SYMBOL_GPL(devm_of_phy_get);
  * @dev: device that is creating the new phy
  * @node: device node of the phy
  * @ops: function pointers for performing phy operations
  * @dev: device that is creating the new phy
  * @node: device node of the phy
  * @ops: function pointers for performing phy operations
- * @init_data: contains the list of PHY consumers or NULL
  *
  * Called to create a phy using phy framework.
  */
 struct phy *phy_create(struct device *dev, struct device_node *node,
  *
  * Called to create a phy using phy framework.
  */
 struct phy *phy_create(struct device *dev, struct device_node *node,
-                      const struct phy_ops *ops,
-                      struct phy_init_data *init_data)
+                      const struct phy_ops *ops)
 {
        int ret;
        int id;
 {
        int ret;
        int id;
@@ -632,7 +668,6 @@ struct phy *phy_create(struct device *dev, struct device_node *node,
        phy->dev.of_node = node ?: dev->of_node;
        phy->id = id;
        phy->ops = ops;
        phy->dev.of_node = node ?: dev->of_node;
        phy->id = id;
        phy->ops = ops;
-       phy->init_data = init_data;
 
        ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
        if (ret)
 
        ret = dev_set_name(&phy->dev, "phy-%s.%d", dev_name(dev), id);
        if (ret)
@@ -667,7 +702,6 @@ EXPORT_SYMBOL_GPL(phy_create);
  * @dev: device that is creating the new phy
  * @node: device node of the phy
  * @ops: function pointers for performing phy operations
  * @dev: device that is creating the new phy
  * @node: device node of the phy
  * @ops: function pointers for performing phy operations
- * @init_data: contains the list of PHY consumers or NULL
  *
  * Creates a new PHY device adding it to the PHY class.
  * While at that, it also associates the device with the phy using devres.
  *
  * Creates a new PHY device adding it to the PHY class.
  * While at that, it also associates the device with the phy using devres.
@@ -675,8 +709,7 @@ EXPORT_SYMBOL_GPL(phy_create);
  * then, devres data is freed.
  */
 struct phy *devm_phy_create(struct device *dev, struct device_node *node,
  * then, devres data is freed.
  */
 struct phy *devm_phy_create(struct device *dev, struct device_node *node,
-                           const struct phy_ops *ops,
-                           struct phy_init_data *init_data)
+                           const struct phy_ops *ops)
 {
        struct phy **ptr, *phy;
 
 {
        struct phy **ptr, *phy;
 
@@ -684,7 +717,7 @@ struct phy *devm_phy_create(struct device *dev, struct device_node *node,
        if (!ptr)
                return ERR_PTR(-ENOMEM);
 
        if (!ptr)
                return ERR_PTR(-ENOMEM);
 
-       phy = phy_create(dev, node, ops, init_data);
+       phy = phy_create(dev, node, ops);
        if (!IS_ERR(phy)) {
                *ptr = phy;
                devres_add(dev, ptr);
        if (!IS_ERR(phy)) {
                *ptr = phy;
                devres_add(dev, ptr);
diff --git a/drivers/phy/phy-dm816x-usb.c b/drivers/phy/phy-dm816x-usb.c
new file mode 100644 (file)
index 0000000..7b42555
--- /dev/null
@@ -0,0 +1,290 @@
+/*
+ * 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 version 2.
+ *
+ * This program is distributed "as is" WITHOUT ANY WARRANTY of any
+ * kind, whether express or implied; 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/regmap.h>
+
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/io.h>
+#include <linux/usb/phy_companion.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/pm_runtime.h>
+#include <linux/delay.h>
+#include <linux/phy/phy.h>
+#include <linux/of_platform.h>
+
+#include <linux/mfd/syscon.h>
+
+/*
+ * TRM has two sets of USB_CTRL registers.. The correct register bits
+ * are in TRM section 24.9.8.2 USB_CTRL Register. The TRM documents the
+ * phy as being SR70LX Synopsys USB 2.0 OTG nanoPHY. It also seems at
+ * least dm816x rev c ignores writes to USB_CTRL register, but the TI
+ * kernel is writing to those so it's possible that later revisions
+ * have worknig USB_CTRL register.
+ *
+ * Also note that At least USB_CTRL register seems to be dm816x specific
+ * according to the TRM. It's possible that USBPHY_CTRL is more generic,
+ * but that would have to be checked against the SR70LX documentation
+ * which does not seem to be publicly available.
+ *
+ * Finally, the phy on dm814x and am335x is different from dm816x.
+ */
+#define DM816X_USB_CTRL_PHYCLKSRC      BIT(8)  /* 1 = PLL ref clock */
+#define DM816X_USB_CTRL_PHYSLEEP1      BIT(1)  /* Enable the first phy */
+#define DM816X_USB_CTRL_PHYSLEEP0      BIT(0)  /* Enable the second phy */
+
+#define DM816X_USBPHY_CTRL_TXRISETUNE  1
+#define DM816X_USBPHY_CTRL_TXVREFTUNE  0xc
+#define DM816X_USBPHY_CTRL_TXPREEMTUNE 0x2
+
+struct dm816x_usb_phy {
+       struct regmap *syscon;
+       struct device *dev;
+       unsigned int instance;
+       struct clk *refclk;
+       struct usb_phy phy;
+       unsigned int usb_ctrl;          /* Shared between phy0 and phy1 */
+       unsigned int usbphy_ctrl;
+};
+
+static int dm816x_usb_phy_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+       otg->host = host;
+       if (!host)
+               otg->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int dm816x_usb_phy_set_peripheral(struct usb_otg *otg,
+                                        struct usb_gadget *gadget)
+{
+       otg->gadget = gadget;
+       if (!gadget)
+               otg->state = OTG_STATE_UNDEFINED;
+
+       return 0;
+}
+
+static int dm816x_usb_phy_init(struct phy *x)
+{
+       struct dm816x_usb_phy *phy = phy_get_drvdata(x);
+       unsigned int val;
+       int error;
+
+       if (clk_get_rate(phy->refclk) != 24000000)
+               dev_warn(phy->dev, "nonstandard phy refclk\n");
+
+       /* Set PLL ref clock and put phys to sleep */
+       error = regmap_update_bits(phy->syscon, phy->usb_ctrl,
+                                  DM816X_USB_CTRL_PHYCLKSRC |
+                                  DM816X_USB_CTRL_PHYSLEEP1 |
+                                  DM816X_USB_CTRL_PHYSLEEP0,
+                                  0);
+       regmap_read(phy->syscon, phy->usb_ctrl, &val);
+       if ((val & 3) != 0)
+               dev_info(phy->dev,
+                        "Working dm816x USB_CTRL! (0x%08x)\n",
+                        val);
+
+       /*
+        * TI kernel sets these values for "symmetrical eye diagram and
+        * better signal quality" so let's assume somebody checked the
+        * values with a scope and set them here too.
+        */
+       regmap_read(phy->syscon, phy->usbphy_ctrl, &val);
+       val |= DM816X_USBPHY_CTRL_TXRISETUNE |
+               DM816X_USBPHY_CTRL_TXVREFTUNE |
+               DM816X_USBPHY_CTRL_TXPREEMTUNE;
+       regmap_write(phy->syscon, phy->usbphy_ctrl, val);
+
+       return 0;
+}
+
+static struct phy_ops ops = {
+       .init           = dm816x_usb_phy_init,
+       .owner          = THIS_MODULE,
+};
+
+static int dm816x_usb_phy_runtime_suspend(struct device *dev)
+{
+       struct dm816x_usb_phy *phy = dev_get_drvdata(dev);
+       unsigned int mask, val;
+       int error = 0;
+
+       mask = BIT(phy->instance);
+       val = ~BIT(phy->instance);
+       error = regmap_update_bits(phy->syscon, phy->usb_ctrl,
+                                  mask, val);
+       if (error)
+               dev_err(phy->dev, "phy%i failed to power off\n",
+                       phy->instance);
+       clk_disable(phy->refclk);
+
+       return 0;
+}
+
+static int dm816x_usb_phy_runtime_resume(struct device *dev)
+{
+       struct dm816x_usb_phy *phy = dev_get_drvdata(dev);
+       unsigned int mask, val;
+       int error;
+
+       error = clk_enable(phy->refclk);
+       if (error)
+               return error;
+
+       /*
+        * Note that at least dm816x rev c does not seem to do
+        * anything with the USB_CTRL register. But let's follow
+        * what the TI tree is doing in case later revisions use
+        * USB_CTRL.
+        */
+       mask = BIT(phy->instance);
+       val = BIT(phy->instance);
+       error = regmap_update_bits(phy->syscon, phy->usb_ctrl,
+                                  mask, val);
+       if (error) {
+               dev_err(phy->dev, "phy%i failed to power on\n",
+                       phy->instance);
+               clk_disable(phy->refclk);
+               return error;
+       }
+
+       return 0;
+}
+
+static UNIVERSAL_DEV_PM_OPS(dm816x_usb_phy_pm_ops,
+                           dm816x_usb_phy_runtime_suspend,
+                           dm816x_usb_phy_runtime_resume,
+                           NULL);
+
+#ifdef CONFIG_OF
+static const struct of_device_id dm816x_usb_phy_id_table[] = {
+       {
+               .compatible = "ti,dm8168-usb-phy",
+       },
+       {},
+};
+MODULE_DEVICE_TABLE(of, dm816x_usb_phy_id_table);
+#endif
+
+static int dm816x_usb_phy_probe(struct platform_device *pdev)
+{
+       struct dm816x_usb_phy *phy;
+       struct resource *res;
+       struct phy *generic_phy;
+       struct phy_provider *phy_provider;
+       struct usb_otg *otg;
+       const struct of_device_id *of_id;
+       const struct usb_phy_data *phy_data;
+       int error;
+
+       of_id = of_match_device(of_match_ptr(dm816x_usb_phy_id_table),
+                               &pdev->dev);
+       if (!of_id)
+               return -EINVAL;
+
+       phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENOENT;
+
+       phy->syscon = syscon_regmap_lookup_by_phandle(pdev->dev.of_node,
+                                                     "syscon");
+       if (IS_ERR(phy->syscon))
+               return PTR_ERR(phy->syscon);
+
+       /*
+        * According to sprs614e.pdf, the first usb_ctrl is shared and
+        * the second instance for usb_ctrl is reserved.. Also the
+        * register bits are different from earlier TRMs.
+        */
+       phy->usb_ctrl = 0x20;
+       phy->usbphy_ctrl = (res->start & 0xff) + 4;
+       if (phy->usbphy_ctrl == 0x2c)
+               phy->instance = 1;
+
+       phy_data = of_id->data;
+
+       otg = devm_kzalloc(&pdev->dev, sizeof(*otg), GFP_KERNEL);
+       if (!otg)
+               return -ENOMEM;
+
+       phy->dev = &pdev->dev;
+       phy->phy.dev = phy->dev;
+       phy->phy.label = "dm8168_usb_phy";
+       phy->phy.otg = otg;
+       phy->phy.type = USB_PHY_TYPE_USB2;
+       otg->set_host = dm816x_usb_phy_set_host;
+       otg->set_peripheral = dm816x_usb_phy_set_peripheral;
+       otg->usb_phy = &phy->phy;
+
+       platform_set_drvdata(pdev, phy);
+
+       phy->refclk = devm_clk_get(phy->dev, "refclk");
+       if (IS_ERR(phy->refclk))
+               return PTR_ERR(phy->refclk);
+       error = clk_prepare(phy->refclk);
+       if (error)
+               return error;
+
+       pm_runtime_enable(phy->dev);
+       generic_phy = devm_phy_create(phy->dev, NULL, &ops);
+       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);
+
+       usb_add_phy_dev(&phy->phy);
+
+       return 0;
+}
+
+static int dm816x_usb_phy_remove(struct platform_device *pdev)
+{
+       struct dm816x_usb_phy *phy = platform_get_drvdata(pdev);
+
+       usb_remove_phy(&phy->phy);
+       pm_runtime_disable(phy->dev);
+       clk_unprepare(phy->refclk);
+
+       return 0;
+}
+
+static struct platform_driver dm816x_usb_phy_driver = {
+       .probe          = dm816x_usb_phy_probe,
+       .remove         = dm816x_usb_phy_remove,
+       .driver         = {
+               .name   = "dm816x-usb-phy",
+               .pm     = &dm816x_usb_phy_pm_ops,
+               .of_match_table = of_match_ptr(dm816x_usb_phy_id_table),
+       },
+};
+
+module_platform_driver(dm816x_usb_phy_driver);
+
+MODULE_ALIAS("platform:dm816x_usb");
+MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>");
+MODULE_DESCRIPTION("dm816x usb phy driver");
+MODULE_LICENSE("GPL v2");
index 84f49e5a3f24315850eb8b26d69fef1a696137f9..f86cbe68ddaf30fd1a5a377c2f7d9dfb1c259c8f 100644 (file)
@@ -112,7 +112,7 @@ static int exynos_dp_video_phy_probe(struct platform_device *pdev)
        match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node);
        state->drvdata = match->data;
 
        match = of_match_node(exynos_dp_video_phy_of_match, dev->of_node);
        state->drvdata = match->data;
 
-       phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops, NULL);
+       phy = devm_phy_create(dev, NULL, &exynos_dp_video_phy_ops);
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create Display Port PHY\n");
                return PTR_ERR(phy);
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create Display Port PHY\n");
                return PTR_ERR(phy);
index 6a9bef138617627ffca69a855ede1994d8887498..f017b2f2a54ecb18c8aa7b0fed3c375f790e48d4 100644 (file)
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
+#include <linux/mfd/syscon/exynos4-pmu.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/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/spinlock.h>
+#include <linux/mfd/syscon.h>
 
 
-/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
+/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */
 #define EXYNOS_MIPI_PHY_CONTROL(n)     ((n) * 4)
 #define EXYNOS_MIPI_PHY_CONTROL(n)     ((n) * 4)
-#define EXYNOS_MIPI_PHY_ENABLE         (1 << 0)
-#define EXYNOS_MIPI_PHY_SRESETN                (1 << 1)
-#define EXYNOS_MIPI_PHY_MRESETN                (1 << 2)
-#define EXYNOS_MIPI_PHY_RESET_MASK     (3 << 1)
 
 enum exynos_mipi_phy_id {
        EXYNOS_MIPI_PHY_ID_CSIS0,
 
 enum exynos_mipi_phy_id {
        EXYNOS_MIPI_PHY_ID_CSIS0,
@@ -38,43 +37,62 @@ enum exynos_mipi_phy_id {
        ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
 
 struct exynos_mipi_video_phy {
        ((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
 
 struct exynos_mipi_video_phy {
-       spinlock_t slock;
        struct video_phy_desc {
                struct phy *phy;
                unsigned int index;
        } phys[EXYNOS_MIPI_PHYS_NUM];
        struct video_phy_desc {
                struct phy *phy;
                unsigned int index;
        } phys[EXYNOS_MIPI_PHYS_NUM];
+       spinlock_t slock;
        void __iomem *regs;
        void __iomem *regs;
+       struct mutex mutex;
+       struct regmap *regmap;
 };
 
 static int __set_phy_state(struct exynos_mipi_video_phy *state,
                        enum exynos_mipi_phy_id id, unsigned int on)
 {
 };
 
 static int __set_phy_state(struct exynos_mipi_video_phy *state,
                        enum exynos_mipi_phy_id id, unsigned int on)
 {
+       const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2);
        void __iomem *addr;
        void __iomem *addr;
-       u32 reg, reset;
-
-       addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+       u32 val, reset;
 
        if (is_mipi_dsim_phy_id(id))
 
        if (is_mipi_dsim_phy_id(id))
-               reset = EXYNOS_MIPI_PHY_MRESETN;
-       else
-               reset = EXYNOS_MIPI_PHY_SRESETN;
-
-       spin_lock(&state->slock);
-       reg = readl(addr);
-       if (on)
-               reg |= reset;
+               reset = EXYNOS4_MIPI_PHY_MRESETN;
        else
        else
-               reg &= ~reset;
-       writel(reg, addr);
-
-       /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
-       if (on)
-               reg |= EXYNOS_MIPI_PHY_ENABLE;
-       else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
-               reg &= ~EXYNOS_MIPI_PHY_ENABLE;
+               reset = EXYNOS4_MIPI_PHY_SRESETN;
+
+       if (state->regmap) {
+               mutex_lock(&state->mutex);
+               regmap_read(state->regmap, offset, &val);
+               if (on)
+                       val |= reset;
+               else
+                       val &= ~reset;
+               regmap_write(state->regmap, offset, val);
+               if (on)
+                       val |= EXYNOS4_MIPI_PHY_ENABLE;
+               else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
+                       val &= ~EXYNOS4_MIPI_PHY_ENABLE;
+               regmap_write(state->regmap, offset, val);
+               mutex_unlock(&state->mutex);
+       } else {
+               addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+
+               spin_lock(&state->slock);
+               val = readl(addr);
+               if (on)
+                       val |= reset;
+               else
+                       val &= ~reset;
+               writel(val, addr);
+               /* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */
+               if (on)
+                       val |= EXYNOS4_MIPI_PHY_ENABLE;
+               else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
+                       val &= ~EXYNOS4_MIPI_PHY_ENABLE;
+
+               writel(val, addr);
+               spin_unlock(&state->slock);
+       }
 
 
-       writel(reg, addr);
-       spin_unlock(&state->slock);
        return 0;
 }
 
        return 0;
 }
 
@@ -118,7 +136,6 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
 {
        struct exynos_mipi_video_phy *state;
        struct device *dev = &pdev->dev;
 {
        struct exynos_mipi_video_phy *state;
        struct device *dev = &pdev->dev;
-       struct resource *res;
        struct phy_provider *phy_provider;
        unsigned int i;
 
        struct phy_provider *phy_provider;
        unsigned int i;
 
@@ -126,18 +143,26 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
        if (!state)
                return -ENOMEM;
 
        if (!state)
                return -ENOMEM;
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
+       if (IS_ERR(state->regmap)) {
+               struct resource *res;
 
 
-       state->regs = devm_ioremap_resource(dev, res);
-       if (IS_ERR(state->regs))
-               return PTR_ERR(state->regs);
+               dev_info(dev, "regmap lookup failed: %ld\n",
+                        PTR_ERR(state->regmap));
+
+               res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+               state->regs = devm_ioremap_resource(dev, res);
+               if (IS_ERR(state->regs))
+                       return PTR_ERR(state->regs);
+       }
 
        dev_set_drvdata(dev, state);
        spin_lock_init(&state->slock);
 
        dev_set_drvdata(dev, state);
        spin_lock_init(&state->slock);
+       mutex_init(&state->mutex);
 
        for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
                struct phy *phy = devm_phy_create(dev, NULL,
 
        for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
                struct phy *phy = devm_phy_create(dev, NULL,
-                                       &exynos_mipi_video_phy_ops, NULL);
+                                                 &exynos_mipi_video_phy_ops);
                if (IS_ERR(phy)) {
                        dev_err(dev, "failed to create PHY %d\n", i);
                        return PTR_ERR(phy);
                if (IS_ERR(phy)) {
                        dev_err(dev, "failed to create PHY %d\n", i);
                        return PTR_ERR(phy);
index f756aca871dba7ba05befd5f24bcf9e133a5ad89..597e7dd3782a08cc2bca0a4e2717eb649735602e 100644 (file)
@@ -141,6 +141,7 @@ struct exynos5_usbdrd_phy_drvdata {
        const struct exynos5_usbdrd_phy_config *phy_cfg;
        u32 pmu_offset_usbdrd0_phy;
        u32 pmu_offset_usbdrd1_phy;
        const struct exynos5_usbdrd_phy_config *phy_cfg;
        u32 pmu_offset_usbdrd0_phy;
        u32 pmu_offset_usbdrd1_phy;
+       bool has_common_clk_gate;
 };
 
 /**
 };
 
 /**
@@ -148,6 +149,9 @@ struct exynos5_usbdrd_phy_drvdata {
  * @dev: pointer to device instance of this platform device
  * @reg_phy: usb phy controller register memory base
  * @clk: phy clock for register access
  * @dev: pointer to device instance of this platform device
  * @reg_phy: usb phy controller register memory base
  * @clk: phy clock for register access
+ * @pipeclk: clock for pipe3 phy
+ * @utmiclk: clock for utmi+ phy
+ * @itpclk: clock for ITP generation
  * @drv_data: pointer to SoC level driver data structure
  * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
  *         instances each with its 'phy' and 'phy_cfg'.
  * @drv_data: pointer to SoC level driver data structure
  * @phys[]: array for 'EXYNOS5_DRDPHYS_NUM' number of PHY
  *         instances each with its 'phy' and 'phy_cfg'.
@@ -155,12 +159,16 @@ struct exynos5_usbdrd_phy_drvdata {
  *            reference clocks' for SS and HS operations
  * @ref_clk: reference clock to PHY block from which PHY's
  *          operational clocks are derived
  *            reference clocks' for SS and HS operations
  * @ref_clk: reference clock to PHY block from which PHY's
  *          operational clocks are derived
- * @ref_rate: rate of above reference clock
+ * vbus: VBUS regulator for phy
+ * vbus_boost: Boost regulator for VBUS present on few Exynos boards
  */
 struct exynos5_usbdrd_phy {
        struct device *dev;
        void __iomem *reg_phy;
        struct clk *clk;
  */
 struct exynos5_usbdrd_phy {
        struct device *dev;
        void __iomem *reg_phy;
        struct clk *clk;
+       struct clk *pipeclk;
+       struct clk *utmiclk;
+       struct clk *itpclk;
        const struct exynos5_usbdrd_phy_drvdata *drv_data;
        struct phy_usb_instance {
                struct phy *phy;
        const struct exynos5_usbdrd_phy_drvdata *drv_data;
        struct phy_usb_instance {
                struct phy *phy;
@@ -172,6 +180,7 @@ struct exynos5_usbdrd_phy {
        u32 extrefclk;
        struct clk *ref_clk;
        struct regulator *vbus;
        u32 extrefclk;
        struct clk *ref_clk;
        struct regulator *vbus;
+       struct regulator *vbus_boost;
 };
 
 static inline
 };
 
 static inline
@@ -447,13 +456,27 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy)
        dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
 
        clk_prepare_enable(phy_drd->ref_clk);
        dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n");
 
        clk_prepare_enable(phy_drd->ref_clk);
+       if (!phy_drd->drv_data->has_common_clk_gate) {
+               clk_prepare_enable(phy_drd->pipeclk);
+               clk_prepare_enable(phy_drd->utmiclk);
+               clk_prepare_enable(phy_drd->itpclk);
+       }
 
        /* Enable VBUS supply */
 
        /* Enable VBUS supply */
+       if (phy_drd->vbus_boost) {
+               ret = regulator_enable(phy_drd->vbus_boost);
+               if (ret) {
+                       dev_err(phy_drd->dev,
+                               "Failed to enable VBUS boost supply\n");
+                       goto fail_vbus;
+               }
+       }
+
        if (phy_drd->vbus) {
                ret = regulator_enable(phy_drd->vbus);
                if (ret) {
                        dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
        if (phy_drd->vbus) {
                ret = regulator_enable(phy_drd->vbus);
                if (ret) {
                        dev_err(phy_drd->dev, "Failed to enable VBUS supply\n");
-                       goto fail_vbus;
+                       goto fail_vbus_boost;
                }
        }
 
                }
        }
 
@@ -462,8 +485,17 @@ static int exynos5_usbdrd_phy_power_on(struct phy *phy)
 
        return 0;
 
 
        return 0;
 
+fail_vbus_boost:
+       if (phy_drd->vbus_boost)
+               regulator_disable(phy_drd->vbus_boost);
+
 fail_vbus:
        clk_disable_unprepare(phy_drd->ref_clk);
 fail_vbus:
        clk_disable_unprepare(phy_drd->ref_clk);
+       if (!phy_drd->drv_data->has_common_clk_gate) {
+               clk_disable_unprepare(phy_drd->itpclk);
+               clk_disable_unprepare(phy_drd->utmiclk);
+               clk_disable_unprepare(phy_drd->pipeclk);
+       }
 
        return ret;
 }
 
        return ret;
 }
@@ -481,8 +513,15 @@ static int exynos5_usbdrd_phy_power_off(struct phy *phy)
        /* Disable VBUS supply */
        if (phy_drd->vbus)
                regulator_disable(phy_drd->vbus);
        /* Disable VBUS supply */
        if (phy_drd->vbus)
                regulator_disable(phy_drd->vbus);
+       if (phy_drd->vbus_boost)
+               regulator_disable(phy_drd->vbus_boost);
 
        clk_disable_unprepare(phy_drd->ref_clk);
 
        clk_disable_unprepare(phy_drd->ref_clk);
+       if (!phy_drd->drv_data->has_common_clk_gate) {
+               clk_disable_unprepare(phy_drd->itpclk);
+               clk_disable_unprepare(phy_drd->pipeclk);
+               clk_disable_unprepare(phy_drd->utmiclk);
+       }
 
        return 0;
 }
 
        return 0;
 }
@@ -506,6 +545,57 @@ static struct phy_ops exynos5_usbdrd_phy_ops = {
        .owner          = THIS_MODULE,
 };
 
        .owner          = THIS_MODULE,
 };
 
+static int exynos5_usbdrd_phy_clk_handle(struct exynos5_usbdrd_phy *phy_drd)
+{
+       unsigned long ref_rate;
+       int ret;
+
+       phy_drd->clk = devm_clk_get(phy_drd->dev, "phy");
+       if (IS_ERR(phy_drd->clk)) {
+               dev_err(phy_drd->dev, "Failed to get phy clock\n");
+               return PTR_ERR(phy_drd->clk);
+       }
+
+       phy_drd->ref_clk = devm_clk_get(phy_drd->dev, "ref");
+       if (IS_ERR(phy_drd->ref_clk)) {
+               dev_err(phy_drd->dev, "Failed to get phy reference clock\n");
+               return PTR_ERR(phy_drd->ref_clk);
+       }
+       ref_rate = clk_get_rate(phy_drd->ref_clk);
+
+       ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
+       if (ret) {
+               dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
+                       ref_rate);
+               return ret;
+       }
+
+       if (!phy_drd->drv_data->has_common_clk_gate) {
+               phy_drd->pipeclk = devm_clk_get(phy_drd->dev, "phy_pipe");
+               if (IS_ERR(phy_drd->pipeclk)) {
+                       dev_info(phy_drd->dev,
+                                "PIPE3 phy operational clock not specified\n");
+                       phy_drd->pipeclk = NULL;
+               }
+
+               phy_drd->utmiclk = devm_clk_get(phy_drd->dev, "phy_utmi");
+               if (IS_ERR(phy_drd->utmiclk)) {
+                       dev_info(phy_drd->dev,
+                                "UTMI phy operational clock not specified\n");
+                       phy_drd->utmiclk = NULL;
+               }
+
+               phy_drd->itpclk = devm_clk_get(phy_drd->dev, "itp");
+               if (IS_ERR(phy_drd->itpclk)) {
+                       dev_info(phy_drd->dev,
+                                "ITP clock from main OSC not specified\n");
+                       phy_drd->itpclk = NULL;
+               }
+       }
+
+       return 0;
+}
+
 static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
        {
                .id             = EXYNOS5_DRDPHY_UTMI,
 static const struct exynos5_usbdrd_phy_config phy_cfg_exynos5[] = {
        {
                .id             = EXYNOS5_DRDPHY_UTMI,
@@ -525,11 +615,26 @@ static const struct exynos5_usbdrd_phy_drvdata exynos5420_usbdrd_phy = {
        .phy_cfg                = phy_cfg_exynos5,
        .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
        .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
        .phy_cfg                = phy_cfg_exynos5,
        .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
        .pmu_offset_usbdrd1_phy = EXYNOS5420_USBDRD1_PHY_CONTROL,
+       .has_common_clk_gate    = true,
 };
 
 static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
        .phy_cfg                = phy_cfg_exynos5,
        .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
 };
 
 static const struct exynos5_usbdrd_phy_drvdata exynos5250_usbdrd_phy = {
        .phy_cfg                = phy_cfg_exynos5,
        .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
+       .has_common_clk_gate    = true,
+};
+
+static const struct exynos5_usbdrd_phy_drvdata exynos5433_usbdrd_phy = {
+       .phy_cfg                = phy_cfg_exynos5,
+       .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
+       .pmu_offset_usbdrd1_phy = EXYNOS5433_USBHOST30_PHY_CONTROL,
+       .has_common_clk_gate    = false,
+};
+
+static const struct exynos5_usbdrd_phy_drvdata exynos7_usbdrd_phy = {
+       .phy_cfg                = phy_cfg_exynos5,
+       .pmu_offset_usbdrd0_phy = EXYNOS5_USBDRD_PHY_CONTROL,
+       .has_common_clk_gate    = false,
 };
 
 static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
 };
 
 static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
@@ -539,6 +644,12 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = {
        }, {
                .compatible = "samsung,exynos5420-usbdrd-phy",
                .data = &exynos5420_usbdrd_phy
        }, {
                .compatible = "samsung,exynos5420-usbdrd-phy",
                .data = &exynos5420_usbdrd_phy
+       }, {
+               .compatible = "samsung,exynos5433-usbdrd-phy",
+               .data = &exynos5433_usbdrd_phy
+       }, {
+               .compatible = "samsung,exynos7-usbdrd-phy",
+               .data = &exynos7_usbdrd_phy
        },
        { },
 };
        },
        { },
 };
@@ -555,7 +666,6 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
        const struct exynos5_usbdrd_phy_drvdata *drv_data;
        struct regmap *reg_pmu;
        u32 pmu_offset;
        const struct exynos5_usbdrd_phy_drvdata *drv_data;
        struct regmap *reg_pmu;
        u32 pmu_offset;
-       unsigned long ref_rate;
        int i, ret;
        int channel;
 
        int i, ret;
        int channel;
 
@@ -576,23 +686,9 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
        drv_data = match->data;
        phy_drd->drv_data = drv_data;
 
        drv_data = match->data;
        phy_drd->drv_data = drv_data;
 
-       phy_drd->clk = devm_clk_get(dev, "phy");
-       if (IS_ERR(phy_drd->clk)) {
-               dev_err(dev, "Failed to get clock of phy controller\n");
-               return PTR_ERR(phy_drd->clk);
-       }
-
-       phy_drd->ref_clk = devm_clk_get(dev, "ref");
-       if (IS_ERR(phy_drd->ref_clk)) {
-               dev_err(dev, "Failed to get reference clock of usbdrd phy\n");
-               return PTR_ERR(phy_drd->ref_clk);
-       }
-       ref_rate = clk_get_rate(phy_drd->ref_clk);
-
-       ret = exynos5_rate_to_clk(ref_rate, &phy_drd->extrefclk);
+       ret = exynos5_usbdrd_phy_clk_handle(phy_drd);
        if (ret) {
        if (ret) {
-               dev_err(phy_drd->dev, "Clock rate (%ld) not supported\n",
-                       ref_rate);
+               dev_err(dev, "Failed to initialize clocks\n");
                return ret;
        }
 
                return ret;
        }
 
@@ -622,7 +718,7 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
                break;
        }
 
                break;
        }
 
-       /* Get Vbus regulator */
+       /* Get Vbus regulators */
        phy_drd->vbus = devm_regulator_get(dev, "vbus");
        if (IS_ERR(phy_drd->vbus)) {
                ret = PTR_ERR(phy_drd->vbus);
        phy_drd->vbus = devm_regulator_get(dev, "vbus");
        if (IS_ERR(phy_drd->vbus)) {
                ret = PTR_ERR(phy_drd->vbus);
@@ -633,12 +729,21 @@ static int exynos5_usbdrd_phy_probe(struct platform_device *pdev)
                phy_drd->vbus = NULL;
        }
 
                phy_drd->vbus = NULL;
        }
 
+       phy_drd->vbus_boost = devm_regulator_get(dev, "vbus-boost");
+       if (IS_ERR(phy_drd->vbus_boost)) {
+               ret = PTR_ERR(phy_drd->vbus_boost);
+               if (ret == -EPROBE_DEFER)
+                       return ret;
+
+               dev_warn(dev, "Failed to get VBUS boost supply regulator\n");
+               phy_drd->vbus_boost = NULL;
+       }
+
        dev_vdbg(dev, "Creating usbdrd_phy phy\n");
 
        for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
                struct phy *phy = devm_phy_create(dev, NULL,
        dev_vdbg(dev, "Creating usbdrd_phy phy\n");
 
        for (i = 0; i < EXYNOS5_DRDPHYS_NUM; i++) {
                struct phy *phy = devm_phy_create(dev, NULL,
-                                                 &exynos5_usbdrd_phy_ops,
-                                                 NULL);
+                                                 &exynos5_usbdrd_phy_ops);
                if (IS_ERR(phy)) {
                        dev_err(dev, "Failed to create usbdrd_phy phy\n");
                        return PTR_ERR(phy);
                if (IS_ERR(phy)) {
                        dev_err(dev, "Failed to create usbdrd_phy phy\n");
                        return PTR_ERR(phy);
index 54cf4ae60d297f9471459a3b212dd15b3f87b73e..bc858cc800a12bbbedad8bf18586cd9d9ee1ef78 100644 (file)
@@ -210,7 +210,7 @@ static int exynos_sata_phy_probe(struct platform_device *pdev)
                return ret;
        }
 
                return ret;
        }
 
-       sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops, NULL);
+       sata_phy->phy = devm_phy_create(dev, NULL, &exynos_sata_phy_ops);
        if (IS_ERR(sata_phy->phy)) {
                clk_disable_unprepare(sata_phy->phyclk);
                dev_err(dev, "failed to create PHY\n");
        if (IS_ERR(sata_phy->phy)) {
                clk_disable_unprepare(sata_phy->phyclk);
                dev_err(dev, "failed to create PHY\n");
index d5d978085c6d43e5220c5f0756930719e6c79313..34915b4202f18c5249a059dd42aa914fee2e7f2d 100644 (file)
@@ -156,7 +156,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
        if (IS_ERR(priv->peri_ctrl))
                priv->peri_ctrl = NULL;
 
        if (IS_ERR(priv->peri_ctrl))
                priv->peri_ctrl = NULL;
 
-       phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops, NULL);
+       phy = devm_phy_create(dev, NULL, &hix5hd2_sata_phy_ops);
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create PHY\n");
                return PTR_ERR(phy);
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create PHY\n");
                return PTR_ERR(phy);
@@ -164,10 +164,7 @@ static int hix5hd2_sata_phy_probe(struct platform_device *pdev)
 
        phy_set_drvdata(phy, priv);
        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 
        phy_set_drvdata(phy, priv);
        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
-       if (IS_ERR(phy_provider))
-               return PTR_ERR(phy_provider);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(phy_provider);
 }
 
 static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
 }
 
 static const struct of_device_id hix5hd2_sata_phy_of_match[] = {
diff --git a/drivers/phy/phy-miphy28lp.c b/drivers/phy/phy-miphy28lp.c
new file mode 100644 (file)
index 0000000..174ffd0
--- /dev/null
@@ -0,0 +1,1284 @@
+/*
+ * Copyright (C) 2014 STMicroelectronics
+ *
+ * STMicroelectronics PHY driver MiPHY28lp (for SoC STiH407).
+ *
+ * Author: Alexandre Torgue <alexandre.torgue@st.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2, as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/phy/phy.h>
+#include <linux/delay.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/reset.h>
+
+#include <dt-bindings/phy/phy.h>
+
+/* MiPHY registers */
+#define MIPHY_CONF_RESET               0x00
+#define RST_APPLI_SW           BIT(0)
+#define RST_CONF_SW            BIT(1)
+#define RST_MACRO_SW           BIT(2)
+
+#define MIPHY_RESET                    0x01
+#define RST_PLL_SW             BIT(0)
+#define RST_COMP_SW            BIT(2)
+
+#define MIPHY_STATUS_1                 0x02
+#define PHY_RDY                        BIT(0)
+#define HFC_RDY                        BIT(1)
+#define HFC_PLL                        BIT(2)
+
+#define MIPHY_CONTROL                  0x04
+#define TERM_EN_SW             BIT(2)
+#define DIS_LINK_RST           BIT(3)
+#define AUTO_RST_RX            BIT(4)
+#define PX_RX_POL              BIT(5)
+
+#define MIPHY_BOUNDARY_SEL             0x0a
+#define TX_SEL                 BIT(6)
+#define SSC_SEL                        BIT(4)
+#define GENSEL_SEL             BIT(0)
+
+#define MIPHY_BOUNDARY_1               0x0b
+#define MIPHY_BOUNDARY_2               0x0c
+#define SSC_EN_SW              BIT(2)
+
+#define MIPHY_PLL_CLKREF_FREQ          0x0d
+#define MIPHY_SPEED                    0x0e
+#define TX_SPDSEL_80DEC                0
+#define TX_SPDSEL_40DEC                1
+#define TX_SPDSEL_20DEC                2
+#define RX_SPDSEL_80DEC                0
+#define RX_SPDSEL_40DEC                (1 << 2)
+#define RX_SPDSEL_20DEC                (2 << 2)
+
+#define MIPHY_CONF                     0x0f
+#define MIPHY_CTRL_TEST_SEL            0x20
+#define MIPHY_CTRL_TEST_1              0x21
+#define MIPHY_CTRL_TEST_2              0x22
+#define MIPHY_CTRL_TEST_3              0x23
+#define MIPHY_CTRL_TEST_4              0x24
+#define MIPHY_FEEDBACK_TEST            0x25
+#define MIPHY_DEBUG_BUS                        0x26
+#define MIPHY_DEBUG_STATUS_MSB         0x27
+#define MIPHY_DEBUG_STATUS_LSB         0x28
+#define MIPHY_PWR_RAIL_1               0x29
+#define MIPHY_PWR_RAIL_2               0x2a
+#define MIPHY_SYNCHAR_CONTROL          0x30
+
+#define MIPHY_COMP_FSM_1               0x3a
+#define COMP_START             BIT(6)
+
+#define MIPHY_COMP_FSM_6               0x3f
+#define COMP_DONE              BIT(7)
+
+#define MIPHY_COMP_POSTP               0x42
+#define MIPHY_TX_CTRL_1                        0x49
+#define TX_REG_STEP_0V         0
+#define TX_REG_STEP_P_25MV     1
+#define TX_REG_STEP_P_50MV     2
+#define TX_REG_STEP_N_25MV     7
+#define TX_REG_STEP_N_50MV     6
+#define TX_REG_STEP_N_75MV     5
+
+#define MIPHY_TX_CTRL_2                        0x4a
+#define TX_SLEW_SW_40_PS       0
+#define TX_SLEW_SW_80_PS       1
+#define TX_SLEW_SW_120_PS      2
+
+#define MIPHY_TX_CTRL_3                        0x4b
+#define MIPHY_TX_CAL_MAN               0x4e
+#define TX_SLEW_CAL_MAN_EN     BIT(0)
+
+#define MIPHY_TST_BIAS_BOOST_2         0x62
+#define MIPHY_BIAS_BOOST_1             0x63
+#define MIPHY_BIAS_BOOST_2             0x64
+#define MIPHY_RX_DESBUFF_FDB_2         0x67
+#define MIPHY_RX_DESBUFF_FDB_3         0x68
+#define MIPHY_SIGDET_COMPENS1          0x69
+#define MIPHY_SIGDET_COMPENS2          0x6a
+#define MIPHY_JITTER_PERIOD            0x6b
+#define MIPHY_JITTER_AMPLITUDE_1       0x6c
+#define MIPHY_JITTER_AMPLITUDE_2       0x6d
+#define MIPHY_JITTER_AMPLITUDE_3       0x6e
+#define MIPHY_RX_K_GAIN                        0x78
+#define MIPHY_RX_BUFFER_CTRL           0x7a
+#define VGA_GAIN               BIT(0)
+#define EQ_DC_GAIN             BIT(2)
+#define EQ_BOOST_GAIN          BIT(3)
+
+#define MIPHY_RX_VGA_GAIN              0x7b
+#define MIPHY_RX_EQU_GAIN_1            0x7f
+#define MIPHY_RX_EQU_GAIN_2            0x80
+#define MIPHY_RX_EQU_GAIN_3            0x81
+#define MIPHY_RX_CAL_CTRL_1            0x97
+#define MIPHY_RX_CAL_CTRL_2            0x98
+
+#define MIPHY_RX_CAL_OFFSET_CTRL       0x99
+#define CAL_OFFSET_VGA_64      (0x03 << 0)
+#define CAL_OFFSET_THRESHOLD_64        (0x03 << 2)
+#define VGA_OFFSET_POLARITY    BIT(4)
+#define OFFSET_COMPENSATION_EN BIT(6)
+
+#define MIPHY_RX_CAL_VGA_STEP          0x9a
+#define MIPHY_RX_CAL_EYE_MIN           0x9d
+#define MIPHY_RX_CAL_OPT_LENGTH                0x9f
+#define MIPHY_RX_LOCK_CTRL_1           0xc1
+#define MIPHY_RX_LOCK_SETTINGS_OPT     0xc2
+#define MIPHY_RX_LOCK_STEP             0xc4
+
+#define MIPHY_RX_SIGDET_SLEEP_OA       0xc9
+#define MIPHY_RX_SIGDET_SLEEP_SEL      0xca
+#define MIPHY_RX_SIGDET_WAIT_SEL       0xcb
+#define MIPHY_RX_SIGDET_DATA_SEL       0xcc
+#define EN_ULTRA_LOW_POWER     BIT(0)
+#define EN_FIRST_HALF          BIT(1)
+#define EN_SECOND_HALF         BIT(2)
+#define EN_DIGIT_SIGNAL_CHECK  BIT(3)
+
+#define MIPHY_RX_POWER_CTRL_1          0xcd
+#define MIPHY_RX_POWER_CTRL_2          0xce
+#define MIPHY_PLL_CALSET_CTRL          0xd3
+#define MIPHY_PLL_CALSET_1             0xd4
+#define MIPHY_PLL_CALSET_2             0xd5
+#define MIPHY_PLL_CALSET_3             0xd6
+#define MIPHY_PLL_CALSET_4             0xd7
+#define MIPHY_PLL_SBR_1                        0xe3
+#define SET_NEW_CHANGE         BIT(1)
+
+#define MIPHY_PLL_SBR_2                        0xe4
+#define MIPHY_PLL_SBR_3                        0xe5
+#define MIPHY_PLL_SBR_4                        0xe6
+#define MIPHY_PLL_COMMON_MISC_2                0xe9
+#define START_ACT_FILT         BIT(6)
+
+#define MIPHY_PLL_SPAREIN              0xeb
+
+/*
+ * On STiH407 the glue logic can be different among MiPHY devices; for example:
+ * MiPHY0: OSC_FORCE_EXT means:
+ *  0: 30MHz crystal clk - 1: 100MHz ext clk routed through MiPHY1
+ * MiPHY1: OSC_FORCE_EXT means:
+ *  1: 30MHz crystal clk - 0: 100MHz ext clk routed through MiPHY1
+ * Some devices have not the possibility to check if the osc is ready.
+ */
+#define MIPHY_OSC_FORCE_EXT    BIT(3)
+#define MIPHY_OSC_RDY          BIT(5)
+
+#define MIPHY_CTRL_MASK                0x0f
+#define MIPHY_CTRL_DEFAULT     0
+#define MIPHY_CTRL_SYNC_D_EN   BIT(2)
+
+/* SATA / PCIe defines */
+#define SATA_CTRL_MASK         0x07
+#define PCIE_CTRL_MASK         0xff
+#define SATA_CTRL_SELECT_SATA  1
+#define SATA_CTRL_SELECT_PCIE  0
+#define SYSCFG_PCIE_PCIE_VAL   0x80
+#define SATA_SPDMODE           1
+
+#define MIPHY_SATA_BANK_NB     3
+#define MIPHY_PCIE_BANK_NB     2
+
+enum {
+       SYSCFG_CTRL,
+       SYSCFG_STATUS,
+       SYSCFG_PCI,
+       SYSCFG_SATA,
+       SYSCFG_REG_MAX,
+};
+
+struct miphy28lp_phy {
+       struct phy *phy;
+       struct miphy28lp_dev *phydev;
+       void __iomem *base;
+       void __iomem *pipebase;
+
+       bool osc_force_ext;
+       bool osc_rdy;
+       bool px_rx_pol_inv;
+       bool ssc;
+       bool tx_impedance;
+
+       struct reset_control *miphy_rst;
+
+       u32 sata_gen;
+
+       /* Sysconfig registers offsets needed to configure the device */
+       u32 syscfg_reg[SYSCFG_REG_MAX];
+       u8 type;
+};
+
+struct miphy28lp_dev {
+       struct device *dev;
+       struct regmap *regmap;
+       struct mutex miphy_mutex;
+       struct miphy28lp_phy **phys;
+};
+
+struct miphy_initval {
+       u16 reg;
+       u16 val;
+};
+
+enum miphy_sata_gen { SATA_GEN1, SATA_GEN2, SATA_GEN3 };
+
+static char *PHY_TYPE_name[] = { "sata-up", "pcie-up", "", "usb3-up" };
+
+struct pll_ratio {
+       int clk_ref;
+       int calset_1;
+       int calset_2;
+       int calset_3;
+       int calset_4;
+       int cal_ctrl;
+};
+
+static struct pll_ratio sata_pll_ratio = {
+       .clk_ref = 0x1e,
+       .calset_1 = 0xc8,
+       .calset_2 = 0x00,
+       .calset_3 = 0x00,
+       .calset_4 = 0x00,
+       .cal_ctrl = 0x00,
+};
+
+static struct pll_ratio pcie_pll_ratio = {
+       .clk_ref = 0x1e,
+       .calset_1 = 0xa6,
+       .calset_2 = 0xaa,
+       .calset_3 = 0xaa,
+       .calset_4 = 0x00,
+       .cal_ctrl = 0x00,
+};
+
+static struct pll_ratio usb3_pll_ratio = {
+       .clk_ref = 0x1e,
+       .calset_1 = 0xa6,
+       .calset_2 = 0xaa,
+       .calset_3 = 0xaa,
+       .calset_4 = 0x04,
+       .cal_ctrl = 0x00,
+};
+
+struct miphy28lp_pll_gen {
+       int bank;
+       int speed;
+       int bias_boost_1;
+       int bias_boost_2;
+       int tx_ctrl_1;
+       int tx_ctrl_2;
+       int tx_ctrl_3;
+       int rx_k_gain;
+       int rx_vga_gain;
+       int rx_equ_gain_1;
+       int rx_equ_gain_2;
+       int rx_equ_gain_3;
+       int rx_buff_ctrl;
+};
+
+static struct miphy28lp_pll_gen sata_pll_gen[] = {
+       {
+               .bank           = 0x00,
+               .speed          = TX_SPDSEL_80DEC | RX_SPDSEL_80DEC,
+               .bias_boost_1   = 0x00,
+               .bias_boost_2   = 0xae,
+               .tx_ctrl_2      = 0x53,
+               .tx_ctrl_3      = 0x00,
+               .rx_buff_ctrl   = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
+               .rx_vga_gain    = 0x00,
+               .rx_equ_gain_1  = 0x7d,
+               .rx_equ_gain_2  = 0x56,
+               .rx_equ_gain_3  = 0x00,
+       },
+       {
+               .bank           = 0x01,
+               .speed          = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC,
+               .bias_boost_1   = 0x00,
+               .bias_boost_2   = 0xae,
+               .tx_ctrl_2      = 0x72,
+               .tx_ctrl_3      = 0x20,
+               .rx_buff_ctrl   = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
+               .rx_vga_gain    = 0x00,
+               .rx_equ_gain_1  = 0x7d,
+               .rx_equ_gain_2  = 0x56,
+               .rx_equ_gain_3  = 0x00,
+       },
+       {
+               .bank           = 0x02,
+               .speed          = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC,
+               .bias_boost_1   = 0x00,
+               .bias_boost_2   = 0xae,
+               .tx_ctrl_2      = 0xc0,
+               .tx_ctrl_3      = 0x20,
+               .rx_buff_ctrl   = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
+               .rx_vga_gain    = 0x00,
+               .rx_equ_gain_1  = 0x7d,
+               .rx_equ_gain_2  = 0x56,
+               .rx_equ_gain_3  = 0x00,
+       },
+};
+
+static struct miphy28lp_pll_gen pcie_pll_gen[] = {
+       {
+               .bank           = 0x00,
+               .speed          = TX_SPDSEL_40DEC | RX_SPDSEL_40DEC,
+               .bias_boost_1   = 0x00,
+               .bias_boost_2   = 0xa5,
+               .tx_ctrl_1      = TX_REG_STEP_N_25MV,
+               .tx_ctrl_2      = 0x71,
+               .tx_ctrl_3      = 0x60,
+               .rx_k_gain      = 0x98,
+               .rx_buff_ctrl   = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
+               .rx_vga_gain    = 0x00,
+               .rx_equ_gain_1  = 0x79,
+               .rx_equ_gain_2  = 0x56,
+       },
+       {
+               .bank           = 0x01,
+               .speed          = TX_SPDSEL_20DEC | RX_SPDSEL_20DEC,
+               .bias_boost_1   = 0x00,
+               .bias_boost_2   = 0xa5,
+               .tx_ctrl_1      = TX_REG_STEP_N_25MV,
+               .tx_ctrl_2      = 0x70,
+               .tx_ctrl_3      = 0x60,
+               .rx_k_gain      = 0xcc,
+               .rx_buff_ctrl   = EQ_BOOST_GAIN | EQ_DC_GAIN | VGA_GAIN,
+               .rx_vga_gain    = 0x00,
+               .rx_equ_gain_1  = 0x78,
+               .rx_equ_gain_2  = 0x07,
+       },
+};
+
+static inline void miphy28lp_set_reset(struct miphy28lp_phy *miphy_phy)
+{
+       void *base = miphy_phy->base;
+       u8 val;
+
+       /* Putting Macro in reset */
+       writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET);
+
+       val = RST_APPLI_SW | RST_CONF_SW;
+       writeb_relaxed(val, base + MIPHY_CONF_RESET);
+
+       writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET);
+
+       /* Bringing the MIPHY-CPU registers out of reset */
+       if (miphy_phy->type == PHY_TYPE_PCIE) {
+               val = AUTO_RST_RX | TERM_EN_SW;
+               writeb_relaxed(val, base + MIPHY_CONTROL);
+       } else {
+               val = AUTO_RST_RX | TERM_EN_SW | DIS_LINK_RST;
+               writeb_relaxed(val, base + MIPHY_CONTROL);
+       }
+}
+
+static inline void miphy28lp_pll_calibration(struct miphy28lp_phy *miphy_phy,
+               struct pll_ratio *pll_ratio)
+{
+       void *base = miphy_phy->base;
+       u8 val;
+
+       /* Applying PLL Settings */
+       writeb_relaxed(0x1d, base + MIPHY_PLL_SPAREIN);
+       writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ);
+
+       /* PLL Ratio */
+       writeb_relaxed(pll_ratio->calset_1, base + MIPHY_PLL_CALSET_1);
+       writeb_relaxed(pll_ratio->calset_2, base + MIPHY_PLL_CALSET_2);
+       writeb_relaxed(pll_ratio->calset_3, base + MIPHY_PLL_CALSET_3);
+       writeb_relaxed(pll_ratio->calset_4, base + MIPHY_PLL_CALSET_4);
+       writeb_relaxed(pll_ratio->cal_ctrl, base + MIPHY_PLL_CALSET_CTRL);
+
+       writeb_relaxed(TX_SEL, base + MIPHY_BOUNDARY_SEL);
+
+       val = (0x68 << 1) | TX_SLEW_CAL_MAN_EN;
+       writeb_relaxed(val, base + MIPHY_TX_CAL_MAN);
+
+       val = VGA_OFFSET_POLARITY | CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64;
+
+       if (miphy_phy->type != PHY_TYPE_SATA)
+               val |= OFFSET_COMPENSATION_EN;
+
+       writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL);
+
+       if (miphy_phy->type == PHY_TYPE_USB3) {
+               writeb_relaxed(0x00, base + MIPHY_CONF);
+               writeb_relaxed(0x70, base + MIPHY_RX_LOCK_STEP);
+               writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_OA);
+               writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_SLEEP_SEL);
+               writeb_relaxed(EN_FIRST_HALF, base + MIPHY_RX_SIGDET_WAIT_SEL);
+
+               val = EN_DIGIT_SIGNAL_CHECK | EN_FIRST_HALF;
+               writeb_relaxed(val, base + MIPHY_RX_SIGDET_DATA_SEL);
+       }
+
+}
+
+static inline void miphy28lp_sata_config_gen(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sata_pll_gen); i++) {
+               struct miphy28lp_pll_gen *gen = &sata_pll_gen[i];
+
+               /* Banked settings */
+               writeb_relaxed(gen->bank, base + MIPHY_CONF);
+               writeb_relaxed(gen->speed, base + MIPHY_SPEED);
+               writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1);
+               writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2);
+
+               /* TX buffer Settings */
+               writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2);
+               writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3);
+
+               /* RX Buffer Settings */
+               writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL);
+               writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN);
+               writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1);
+               writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2);
+               writeb_relaxed(gen->rx_equ_gain_3, base + MIPHY_RX_EQU_GAIN_3);
+       }
+}
+
+static inline void miphy28lp_pcie_config_gen(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(pcie_pll_gen); i++) {
+               struct miphy28lp_pll_gen *gen = &pcie_pll_gen[i];
+
+               /* Banked settings */
+               writeb_relaxed(gen->bank, base + MIPHY_CONF);
+               writeb_relaxed(gen->speed, base + MIPHY_SPEED);
+               writeb_relaxed(gen->bias_boost_1, base + MIPHY_BIAS_BOOST_1);
+               writeb_relaxed(gen->bias_boost_2, base + MIPHY_BIAS_BOOST_2);
+
+               /* TX buffer Settings */
+               writeb_relaxed(gen->tx_ctrl_1, base + MIPHY_TX_CTRL_1);
+               writeb_relaxed(gen->tx_ctrl_2, base + MIPHY_TX_CTRL_2);
+               writeb_relaxed(gen->tx_ctrl_3, base + MIPHY_TX_CTRL_3);
+
+               writeb_relaxed(gen->rx_k_gain, base + MIPHY_RX_K_GAIN);
+
+               /* RX Buffer Settings */
+               writeb_relaxed(gen->rx_buff_ctrl, base + MIPHY_RX_BUFFER_CTRL);
+               writeb_relaxed(gen->rx_vga_gain, base + MIPHY_RX_VGA_GAIN);
+               writeb_relaxed(gen->rx_equ_gain_1, base + MIPHY_RX_EQU_GAIN_1);
+               writeb_relaxed(gen->rx_equ_gain_2, base + MIPHY_RX_EQU_GAIN_2);
+       }
+}
+
+static inline int miphy28lp_wait_compensation(struct miphy28lp_phy *miphy_phy)
+{
+       unsigned long finish = jiffies + 5 * HZ;
+       u8 val;
+
+       /* Waiting for Compensation to complete */
+       do {
+               val = readb_relaxed(miphy_phy->base + MIPHY_COMP_FSM_6);
+
+               if (time_after_eq(jiffies, finish))
+                       return -EBUSY;
+               cpu_relax();
+       } while (!(val & COMP_DONE));
+
+       return 0;
+}
+
+
+static inline int miphy28lp_compensation(struct miphy28lp_phy *miphy_phy,
+               struct pll_ratio *pll_ratio)
+{
+       void __iomem *base = miphy_phy->base;
+
+       /* Poll for HFC ready after reset release */
+       /* Compensation measurement */
+       writeb_relaxed(RST_PLL_SW | RST_COMP_SW, base + MIPHY_RESET);
+
+       writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2);
+       writeb_relaxed(pll_ratio->clk_ref, base + MIPHY_PLL_CLKREF_FREQ);
+       writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1);
+
+       if (miphy_phy->type == PHY_TYPE_PCIE)
+               writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET);
+
+       writeb_relaxed(0x00, base + MIPHY_RESET);
+       writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2);
+       writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
+
+       /* TX compensation offset to re-center TX impedance */
+       writeb_relaxed(0x00, base + MIPHY_COMP_POSTP);
+
+       if (miphy_phy->type == PHY_TYPE_PCIE)
+               return miphy28lp_wait_compensation(miphy_phy);
+
+       return 0;
+}
+
+static inline void miphy28_usb3_miphy_reset(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       u8 val;
+
+       /* MIPHY Reset */
+       writeb_relaxed(RST_APPLI_SW, base + MIPHY_CONF_RESET);
+       writeb_relaxed(0x00, base + MIPHY_CONF_RESET);
+       writeb_relaxed(RST_COMP_SW, base + MIPHY_RESET);
+
+       val = RST_COMP_SW | RST_PLL_SW;
+       writeb_relaxed(val, base + MIPHY_RESET);
+
+       writeb_relaxed(0x00, base + MIPHY_PLL_COMMON_MISC_2);
+       writeb_relaxed(0x1e, base + MIPHY_PLL_CLKREF_FREQ);
+       writeb_relaxed(COMP_START, base + MIPHY_COMP_FSM_1);
+       writeb_relaxed(RST_PLL_SW, base + MIPHY_RESET);
+       writeb_relaxed(0x00, base + MIPHY_RESET);
+       writeb_relaxed(START_ACT_FILT, base + MIPHY_PLL_COMMON_MISC_2);
+       writeb_relaxed(0x00, base + MIPHY_CONF);
+       writeb_relaxed(0x00, base + MIPHY_BOUNDARY_1);
+       writeb_relaxed(0x00, base + MIPHY_TST_BIAS_BOOST_2);
+       writeb_relaxed(0x00, base + MIPHY_CONF);
+       writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
+       writeb_relaxed(0xa5, base + MIPHY_DEBUG_BUS);
+       writeb_relaxed(0x00, base + MIPHY_CONF);
+}
+
+static void miphy_sata_tune_ssc(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       u8 val;
+
+       /* Compensate Tx impedance to avoid out of range values */
+       /*
+        * Enable the SSC on PLL for all banks
+        * SSC Modulation @ 31 KHz and 4000 ppm modulation amp
+        */
+       val = readb_relaxed(base + MIPHY_BOUNDARY_2);
+       val |= SSC_EN_SW;
+       writeb_relaxed(val, base + MIPHY_BOUNDARY_2);
+
+       val = readb_relaxed(base + MIPHY_BOUNDARY_SEL);
+       val |= SSC_SEL;
+       writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL);
+
+       for (val = 0; val < MIPHY_SATA_BANK_NB; val++) {
+               writeb_relaxed(val, base + MIPHY_CONF);
+
+               /* Add value to each reference clock cycle  */
+               /* and define the period length of the SSC */
+               writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2);
+               writeb_relaxed(0x6c, base + MIPHY_PLL_SBR_3);
+               writeb_relaxed(0x81, base + MIPHY_PLL_SBR_4);
+
+               /* Clear any previous request */
+               writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
+
+               /* requests the PLL to take in account new parameters */
+               writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
+
+               /* To be sure there is no other pending requests */
+               writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
+       }
+}
+
+static void miphy_pcie_tune_ssc(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       u8 val;
+
+       /* Compensate Tx impedance to avoid out of range values */
+       /*
+        * Enable the SSC on PLL for all banks
+        * SSC Modulation @ 31 KHz and 4000 ppm modulation amp
+        */
+       val = readb_relaxed(base + MIPHY_BOUNDARY_2);
+       val |= SSC_EN_SW;
+       writeb_relaxed(val, base + MIPHY_BOUNDARY_2);
+
+       val = readb_relaxed(base + MIPHY_BOUNDARY_SEL);
+       val |= SSC_SEL;
+       writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL);
+
+       for (val = 0; val < MIPHY_PCIE_BANK_NB; val++) {
+               writeb_relaxed(val, base + MIPHY_CONF);
+
+               /* Validate Step component */
+               writeb_relaxed(0x69, base + MIPHY_PLL_SBR_3);
+               writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4);
+
+               /* Validate Period component */
+               writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2);
+               writeb_relaxed(0x21, base + MIPHY_PLL_SBR_4);
+
+               /* Clear any previous request */
+               writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
+
+               /* requests the PLL to take in account new parameters */
+               writeb_relaxed(SET_NEW_CHANGE, base + MIPHY_PLL_SBR_1);
+
+               /* To be sure there is no other pending requests */
+               writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
+       }
+}
+
+static inline void miphy_tune_tx_impedance(struct miphy28lp_phy *miphy_phy)
+{
+       /* Compensate Tx impedance to avoid out of range values */
+       writeb_relaxed(0x02, miphy_phy->base + MIPHY_COMP_POSTP);
+}
+
+static inline int miphy28lp_configure_sata(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       int err;
+       u8 val;
+
+       /* Putting Macro in reset */
+       miphy28lp_set_reset(miphy_phy);
+
+       /* PLL calibration */
+       miphy28lp_pll_calibration(miphy_phy, &sata_pll_ratio);
+
+       /* Banked settings Gen1/Gen2/Gen3 */
+       miphy28lp_sata_config_gen(miphy_phy);
+
+       /* Power control */
+       /* Input bridge enable, manual input bridge control */
+       writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1);
+
+       /* Macro out of reset */
+       writeb_relaxed(0x00, base + MIPHY_CONF_RESET);
+
+       /* Poll for HFC ready after reset release */
+       /* Compensation measurement */
+       err = miphy28lp_compensation(miphy_phy, &sata_pll_ratio);
+       if (err)
+               return err;
+
+       if (miphy_phy->px_rx_pol_inv) {
+               /* Invert Rx polarity */
+               val = readb_relaxed(miphy_phy->base + MIPHY_CONTROL);
+               val |= PX_RX_POL;
+               writeb_relaxed(val, miphy_phy->base + MIPHY_CONTROL);
+       }
+
+       if (miphy_phy->ssc)
+               miphy_sata_tune_ssc(miphy_phy);
+
+       if (miphy_phy->tx_impedance)
+               miphy_tune_tx_impedance(miphy_phy);
+
+       return 0;
+}
+
+static inline int miphy28lp_configure_pcie(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       int err;
+
+       /* Putting Macro in reset */
+       miphy28lp_set_reset(miphy_phy);
+
+       /* PLL calibration */
+       miphy28lp_pll_calibration(miphy_phy, &pcie_pll_ratio);
+
+       /* Banked settings Gen1/Gen2 */
+       miphy28lp_pcie_config_gen(miphy_phy);
+
+       /* Power control */
+       /* Input bridge enable, manual input bridge control */
+       writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1);
+
+       /* Macro out of reset */
+       writeb_relaxed(0x00, base + MIPHY_CONF_RESET);
+
+       /* Poll for HFC ready after reset release */
+       /* Compensation measurement */
+       err = miphy28lp_compensation(miphy_phy, &pcie_pll_ratio);
+       if (err)
+               return err;
+
+       if (miphy_phy->ssc)
+               miphy_pcie_tune_ssc(miphy_phy);
+
+       if (miphy_phy->tx_impedance)
+               miphy_tune_tx_impedance(miphy_phy);
+
+       return 0;
+}
+
+
+static inline void miphy28lp_configure_usb3(struct miphy28lp_phy *miphy_phy)
+{
+       void __iomem *base = miphy_phy->base;
+       u8 val;
+
+       /* Putting Macro in reset */
+       miphy28lp_set_reset(miphy_phy);
+
+       /* PLL calibration */
+       miphy28lp_pll_calibration(miphy_phy, &usb3_pll_ratio);
+
+       /* Writing The Speed Rate */
+       writeb_relaxed(0x00, base + MIPHY_CONF);
+
+       val = RX_SPDSEL_20DEC | TX_SPDSEL_20DEC;
+       writeb_relaxed(val, base + MIPHY_SPEED);
+
+       /* RX Channel compensation and calibration */
+       writeb_relaxed(0x1c, base + MIPHY_RX_LOCK_SETTINGS_OPT);
+       writeb_relaxed(0x51, base + MIPHY_RX_CAL_CTRL_1);
+       writeb_relaxed(0x70, base + MIPHY_RX_CAL_CTRL_2);
+
+       val = OFFSET_COMPENSATION_EN | VGA_OFFSET_POLARITY |
+             CAL_OFFSET_THRESHOLD_64 | CAL_OFFSET_VGA_64;
+       writeb_relaxed(val, base + MIPHY_RX_CAL_OFFSET_CTRL);
+       writeb_relaxed(0x22, base + MIPHY_RX_CAL_VGA_STEP);
+       writeb_relaxed(0x0e, base + MIPHY_RX_CAL_OPT_LENGTH);
+
+       val = EQ_DC_GAIN | VGA_GAIN;
+       writeb_relaxed(val, base + MIPHY_RX_BUFFER_CTRL);
+       writeb_relaxed(0x78, base + MIPHY_RX_EQU_GAIN_1);
+       writeb_relaxed(0x1b, base + MIPHY_SYNCHAR_CONTROL);
+
+       /* TX compensation offset to re-center TX impedance */
+       writeb_relaxed(0x02, base + MIPHY_COMP_POSTP);
+
+       /* Enable GENSEL_SEL and SSC */
+       /* TX_SEL=0 swing preemp forced by pipe registres */
+       val = SSC_SEL | GENSEL_SEL;
+       writeb_relaxed(val, base + MIPHY_BOUNDARY_SEL);
+
+       /* MIPHY Bias boost */
+       writeb_relaxed(0x00, base + MIPHY_BIAS_BOOST_1);
+       writeb_relaxed(0xa7, base + MIPHY_BIAS_BOOST_2);
+
+       /* SSC modulation */
+       writeb_relaxed(SSC_EN_SW, base + MIPHY_BOUNDARY_2);
+
+       /* MIPHY TX control */
+       writeb_relaxed(0x00, base + MIPHY_CONF);
+
+       /* Validate Step component */
+       writeb_relaxed(0x5a, base + MIPHY_PLL_SBR_3);
+       writeb_relaxed(0xa0, base + MIPHY_PLL_SBR_4);
+
+       /* Validate Period component */
+       writeb_relaxed(0x3c, base + MIPHY_PLL_SBR_2);
+       writeb_relaxed(0xa1, base + MIPHY_PLL_SBR_4);
+
+       /* Clear any previous request */
+       writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
+
+       /* requests the PLL to take in account new parameters */
+       writeb_relaxed(0x02, base + MIPHY_PLL_SBR_1);
+
+       /* To be sure there is no other pending requests */
+       writeb_relaxed(0x00, base + MIPHY_PLL_SBR_1);
+
+       /* Rx PI controller settings */
+       writeb_relaxed(0xca, base + MIPHY_RX_K_GAIN);
+
+       /* MIPHY RX input bridge control */
+       /* INPUT_BRIDGE_EN_SW=1, manual input bridge control[0]=1 */
+       writeb_relaxed(0x21, base + MIPHY_RX_POWER_CTRL_1);
+       writeb_relaxed(0x29, base + MIPHY_RX_POWER_CTRL_1);
+       writeb_relaxed(0x1a, base + MIPHY_RX_POWER_CTRL_2);
+
+       /* MIPHY Reset for usb3 */
+       miphy28_usb3_miphy_reset(miphy_phy);
+}
+
+static inline int miphy_is_ready(struct miphy28lp_phy *miphy_phy)
+{
+       unsigned long finish = jiffies + 5 * HZ;
+       u8 mask = HFC_PLL | HFC_RDY;
+       u8 val;
+
+       /*
+        * For PCIe and USB3 check only that PLL and HFC are ready
+        * For SATA check also that phy is ready!
+        */
+       if (miphy_phy->type == PHY_TYPE_SATA)
+               mask |= PHY_RDY;
+
+       do {
+               val = readb_relaxed(miphy_phy->base + MIPHY_STATUS_1);
+               if ((val & mask) != mask)
+                       cpu_relax();
+               else
+                       return 0;
+       } while (!time_after_eq(jiffies, finish));
+
+       return -EBUSY;
+}
+
+static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy)
+{
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+       unsigned long finish = jiffies + 5 * HZ;
+       u32 val;
+
+       if (!miphy_phy->osc_rdy)
+               return 0;
+
+       if (!miphy_phy->syscfg_reg[SYSCFG_STATUS])
+               return -EINVAL;
+
+       do {
+               regmap_read(miphy_dev->regmap,
+                               miphy_phy->syscfg_reg[SYSCFG_STATUS], &val);
+
+               if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY)
+                       cpu_relax();
+               else
+                       return 0;
+       } while (!time_after_eq(jiffies, finish));
+
+       return -EBUSY;
+}
+
+static int miphy28lp_get_resource_byname(struct device_node *child,
+                                         char *rname, struct resource *res)
+{
+       int index;
+
+       index = of_property_match_string(child, "reg-names", rname);
+       if (index < 0)
+               return -ENODEV;
+
+       return of_address_to_resource(child, index, res);
+}
+
+static int miphy28lp_get_one_addr(struct device *dev,
+                                 struct device_node *child, char *rname,
+                                 void __iomem **base)
+{
+       struct resource res;
+       int ret;
+
+       ret = miphy28lp_get_resource_byname(child, rname, &res);
+       if (!ret) {
+               *base = devm_ioremap(dev, res.start, resource_size(&res));
+               if (!*base) {
+                       dev_err(dev, "failed to ioremap %s address region\n"
+                                       , rname);
+                       return -ENOENT;
+               }
+       }
+
+       return 0;
+}
+
+/* MiPHY reset and sysconf setup */
+static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val)
+{
+       int err;
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+
+       if (!miphy_phy->syscfg_reg[SYSCFG_CTRL])
+               return -EINVAL;
+
+       err = reset_control_assert(miphy_phy->miphy_rst);
+       if (err) {
+               dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n");
+               return err;
+       }
+
+       if (miphy_phy->osc_force_ext)
+               miphy_val |= MIPHY_OSC_FORCE_EXT;
+
+       regmap_update_bits(miphy_dev->regmap,
+                          miphy_phy->syscfg_reg[SYSCFG_CTRL],
+                          MIPHY_CTRL_MASK, miphy_val);
+
+       err = reset_control_deassert(miphy_phy->miphy_rst);
+       if (err) {
+               dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n");
+               return err;
+       }
+
+       return miphy_osc_is_ready(miphy_phy);
+}
+
+static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy)
+{
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+       int err, sata_conf = SATA_CTRL_SELECT_SATA;
+
+       if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) ||
+                       (!miphy_phy->syscfg_reg[SYSCFG_PCI]) ||
+                       (!miphy_phy->base))
+               return -EINVAL;
+
+       dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base);
+
+       /* Configure the glue-logic */
+       sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE);
+
+       regmap_update_bits(miphy_dev->regmap,
+                          miphy_phy->syscfg_reg[SYSCFG_SATA],
+                          SATA_CTRL_MASK, sata_conf);
+
+       regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI],
+                          PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
+
+       /* MiPHY path and clocking init */
+       err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT);
+
+       if (err) {
+               dev_err(miphy_dev->dev, "SATA phy setup failed\n");
+               return err;
+       }
+
+       /* initialize miphy */
+       miphy28lp_configure_sata(miphy_phy);
+
+       return miphy_is_ready(miphy_phy);
+}
+
+static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy)
+{
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+       int err;
+
+       if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) ||
+                       (!miphy_phy->syscfg_reg[SYSCFG_PCI])
+               || (!miphy_phy->base) || (!miphy_phy->pipebase))
+               return -EINVAL;
+
+       dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base);
+
+       /* Configure the glue-logic */
+       regmap_update_bits(miphy_dev->regmap,
+                          miphy_phy->syscfg_reg[SYSCFG_SATA],
+                          SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
+
+       regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI],
+                          PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL);
+
+       /* MiPHY path and clocking init */
+       err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_DEFAULT);
+
+       if (err) {
+               dev_err(miphy_dev->dev, "PCIe phy setup failed\n");
+               return err;
+       }
+
+       /* initialize miphy */
+       err = miphy28lp_configure_pcie(miphy_phy);
+       if (err)
+               return err;
+
+       /* PIPE Wrapper Configuration */
+       writeb_relaxed(0x68, miphy_phy->pipebase + 0x104); /* Rise_0 */
+       writeb_relaxed(0x61, miphy_phy->pipebase + 0x105); /* Rise_1 */
+       writeb_relaxed(0x68, miphy_phy->pipebase + 0x108); /* Fall_0 */
+       writeb_relaxed(0x61, miphy_phy->pipebase + 0x109); /* Fall-1 */
+       writeb_relaxed(0x68, miphy_phy->pipebase + 0x10c); /* Threshold_0 */
+       writeb_relaxed(0x60, miphy_phy->pipebase + 0x10d); /* Threshold_1 */
+
+       /* Wait for phy_ready */
+       return miphy_is_ready(miphy_phy);
+}
+
+static int miphy28lp_init_usb3(struct miphy28lp_phy *miphy_phy)
+{
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+       int err;
+
+       if ((!miphy_phy->base) || (!miphy_phy->pipebase))
+               return -EINVAL;
+
+       dev_info(miphy_dev->dev, "usb3-up mode, addr 0x%p\n", miphy_phy->base);
+
+       /* MiPHY path and clocking init */
+       err = miphy28lp_setup(miphy_phy, MIPHY_CTRL_SYNC_D_EN);
+       if (err) {
+               dev_err(miphy_dev->dev, "USB3 phy setup failed\n");
+               return err;
+       }
+
+       /* initialize miphy */
+       miphy28lp_configure_usb3(miphy_phy);
+
+       /* PIPE Wrapper Configuration */
+       writeb_relaxed(0x68, miphy_phy->pipebase + 0x23);
+       writeb_relaxed(0x61, miphy_phy->pipebase + 0x24);
+       writeb_relaxed(0x68, miphy_phy->pipebase + 0x26);
+       writeb_relaxed(0x61, miphy_phy->pipebase + 0x27);
+       writeb_relaxed(0x18, miphy_phy->pipebase + 0x29);
+       writeb_relaxed(0x61, miphy_phy->pipebase + 0x2a);
+
+       /* pipe Wrapper usb3 TX swing de-emph margin PREEMPH[7:4], SWING[3:0] */
+       writeb_relaxed(0X67, miphy_phy->pipebase + 0x68);
+       writeb_relaxed(0x0d, miphy_phy->pipebase + 0x69);
+       writeb_relaxed(0X67, miphy_phy->pipebase + 0x6a);
+       writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6b);
+       writeb_relaxed(0X67, miphy_phy->pipebase + 0x6c);
+       writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6d);
+       writeb_relaxed(0X67, miphy_phy->pipebase + 0x6e);
+       writeb_relaxed(0X0d, miphy_phy->pipebase + 0x6f);
+
+       return miphy_is_ready(miphy_phy);
+}
+
+static int miphy28lp_init(struct phy *phy)
+{
+       struct miphy28lp_phy *miphy_phy = phy_get_drvdata(phy);
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+       int ret;
+
+       mutex_lock(&miphy_dev->miphy_mutex);
+
+       switch (miphy_phy->type) {
+
+       case PHY_TYPE_SATA:
+               ret = miphy28lp_init_sata(miphy_phy);
+               break;
+       case PHY_TYPE_PCIE:
+               ret = miphy28lp_init_pcie(miphy_phy);
+               break;
+       case PHY_TYPE_USB3:
+               ret = miphy28lp_init_usb3(miphy_phy);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       mutex_unlock(&miphy_dev->miphy_mutex);
+
+       return ret;
+}
+
+static int miphy28lp_get_addr(struct miphy28lp_phy *miphy_phy)
+{
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+       struct device_node *phynode = miphy_phy->phy->dev.of_node;
+       int err;
+
+       if ((miphy_phy->type != PHY_TYPE_SATA) &&
+           (miphy_phy->type != PHY_TYPE_PCIE) &&
+           (miphy_phy->type != PHY_TYPE_USB3)) {
+               return -EINVAL;
+       }
+
+       err = miphy28lp_get_one_addr(miphy_dev->dev, phynode,
+                       PHY_TYPE_name[miphy_phy->type - PHY_TYPE_SATA],
+                       &miphy_phy->base);
+       if (err)
+               return err;
+
+       if ((miphy_phy->type == PHY_TYPE_PCIE) ||
+           (miphy_phy->type == PHY_TYPE_USB3)) {
+               err = miphy28lp_get_one_addr(miphy_dev->dev, phynode, "pipew",
+                                            &miphy_phy->pipebase);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static struct phy *miphy28lp_xlate(struct device *dev,
+                                  struct of_phandle_args *args)
+{
+       struct miphy28lp_dev *miphy_dev = dev_get_drvdata(dev);
+       struct miphy28lp_phy *miphy_phy = NULL;
+       struct device_node *phynode = args->np;
+       int ret, index = 0;
+
+       if (!of_device_is_available(phynode)) {
+               dev_warn(dev, "Requested PHY is disabled\n");
+               return ERR_PTR(-ENODEV);
+       }
+
+       if (args->args_count != 1) {
+               dev_err(dev, "Invalid number of cells in 'phy' property\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       for (index = 0; index < of_get_child_count(dev->of_node); index++)
+               if (phynode == miphy_dev->phys[index]->phy->dev.of_node) {
+                       miphy_phy = miphy_dev->phys[index];
+                       break;
+               }
+
+       if (!miphy_phy) {
+               dev_err(dev, "Failed to find appropriate phy\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       miphy_phy->type = args->args[0];
+
+       ret = miphy28lp_get_addr(miphy_phy);
+       if (ret < 0)
+               return ERR_PTR(ret);
+
+       return miphy_phy->phy;
+}
+
+static struct phy_ops miphy28lp_ops = {
+       .init = miphy28lp_init,
+};
+
+static int miphy28lp_probe_resets(struct device_node *node,
+                                 struct miphy28lp_phy *miphy_phy)
+{
+       struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
+       int err;
+
+       miphy_phy->miphy_rst = of_reset_control_get(node, "miphy-sw-rst");
+
+       if (IS_ERR(miphy_phy->miphy_rst)) {
+               dev_err(miphy_dev->dev,
+                               "miphy soft reset control not defined\n");
+               return PTR_ERR(miphy_phy->miphy_rst);
+       }
+
+       err = reset_control_deassert(miphy_phy->miphy_rst);
+       if (err) {
+               dev_err(miphy_dev->dev, "unable to bring out of miphy reset\n");
+               return err;
+       }
+
+       return 0;
+}
+
+static int miphy28lp_of_probe(struct device_node *np,
+                             struct miphy28lp_phy *miphy_phy)
+{
+       int i;
+       u32 ctrlreg;
+
+       miphy_phy->osc_force_ext =
+               of_property_read_bool(np, "st,osc-force-ext");
+
+       miphy_phy->osc_rdy = of_property_read_bool(np, "st,osc-rdy");
+
+       miphy_phy->px_rx_pol_inv =
+               of_property_read_bool(np, "st,px_rx_pol_inv");
+
+       miphy_phy->ssc = of_property_read_bool(np, "st,ssc-on");
+
+       miphy_phy->tx_impedance =
+               of_property_read_bool(np, "st,tx-impedance-comp");
+
+       of_property_read_u32(np, "st,sata-gen", &miphy_phy->sata_gen);
+       if (!miphy_phy->sata_gen)
+               miphy_phy->sata_gen = SATA_GEN1;
+
+       for (i = 0; i < SYSCFG_REG_MAX; i++) {
+               if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg))
+                       miphy_phy->syscfg_reg[i] = ctrlreg;
+       }
+
+       return 0;
+}
+
+static int miphy28lp_probe(struct platform_device *pdev)
+{
+       struct device_node *child, *np = pdev->dev.of_node;
+       struct miphy28lp_dev *miphy_dev;
+       struct phy_provider *provider;
+       struct phy *phy;
+       int chancount, port = 0;
+       int ret;
+
+       miphy_dev = devm_kzalloc(&pdev->dev, sizeof(*miphy_dev), GFP_KERNEL);
+       if (!miphy_dev)
+               return -ENOMEM;
+
+       chancount = of_get_child_count(np);
+       miphy_dev->phys = devm_kzalloc(&pdev->dev, sizeof(phy) * chancount,
+                                      GFP_KERNEL);
+       if (!miphy_dev->phys)
+               return -ENOMEM;
+
+       miphy_dev->regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+       if (IS_ERR(miphy_dev->regmap)) {
+               dev_err(miphy_dev->dev, "No syscfg phandle specified\n");
+               return PTR_ERR(miphy_dev->regmap);
+       }
+
+       miphy_dev->dev = &pdev->dev;
+
+       dev_set_drvdata(&pdev->dev, miphy_dev);
+
+       mutex_init(&miphy_dev->miphy_mutex);
+
+       for_each_child_of_node(np, child) {
+               struct miphy28lp_phy *miphy_phy;
+
+               miphy_phy = devm_kzalloc(&pdev->dev, sizeof(*miphy_phy),
+                                        GFP_KERNEL);
+               if (!miphy_phy)
+                       return -ENOMEM;
+
+               miphy_dev->phys[port] = miphy_phy;
+
+               phy = devm_phy_create(&pdev->dev, child, &miphy28lp_ops);
+               if (IS_ERR(phy)) {
+                       dev_err(&pdev->dev, "failed to create PHY\n");
+                       return PTR_ERR(phy);
+               }
+
+               miphy_dev->phys[port]->phy = phy;
+               miphy_dev->phys[port]->phydev = miphy_dev;
+
+               ret = miphy28lp_of_probe(child, miphy_phy);
+               if (ret)
+                       return ret;
+
+               ret = miphy28lp_probe_resets(child, miphy_dev->phys[port]);
+               if (ret)
+                       return ret;
+
+               phy_set_drvdata(phy, miphy_dev->phys[port]);
+               port++;
+
+       }
+
+       provider = devm_of_phy_provider_register(&pdev->dev, miphy28lp_xlate);
+       return PTR_ERR_OR_ZERO(provider);
+}
+
+static const struct of_device_id miphy28lp_of_match[] = {
+       {.compatible = "st,miphy28lp-phy", },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, miphy28lp_of_match);
+
+static struct platform_driver miphy28lp_driver = {
+       .probe = miphy28lp_probe,
+       .driver = {
+               .name = "miphy28lp-phy",
+               .owner = THIS_MODULE,
+               .of_match_table = miphy28lp_of_match,
+       }
+};
+
+module_platform_driver(miphy28lp_driver);
+
+MODULE_AUTHOR("Alexandre Torgue <alexandre.torgue@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics miphy28lp driver");
+MODULE_LICENSE("GPL v2");
index 801afaf2d449655bcc6acc45dac5d8655a850008..98bffbc0f74dad1c4b6cf4892d92c3424dfadaad 100644 (file)
@@ -25,7 +25,7 @@
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 
 #include <linux/mfd/syscon.h>
 #include <linux/regmap.h>
 
-#include <dt-bindings/phy/phy-miphy365x.h>
+#include <dt-bindings/phy/phy.h>
 
 #define HFC_TIMEOUT            100
 
 
 #define HFC_TIMEOUT            100
 
@@ -141,7 +141,7 @@ struct miphy365x_phy {
        bool pcie_tx_pol_inv;
        bool sata_tx_pol_inv;
        u32 sata_gen;
        bool pcie_tx_pol_inv;
        bool sata_tx_pol_inv;
        u32 sata_gen;
-       u64 ctrlreg;
+       u32 ctrlreg;
        u8 type;
 };
 
        u8 type;
 };
 
@@ -176,10 +176,10 @@ static u8 rx_tx_spd[] = {
 static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
                              struct miphy365x_dev *miphy_dev)
 {
 static int miphy365x_set_path(struct miphy365x_phy *miphy_phy,
                              struct miphy365x_dev *miphy_dev)
 {
-       bool sata = (miphy_phy->type == MIPHY_TYPE_SATA);
+       bool sata = (miphy_phy->type == PHY_TYPE_SATA);
 
        return regmap_update_bits(miphy_dev->regmap,
 
        return regmap_update_bits(miphy_dev->regmap,
-                                 (unsigned int)miphy_phy->ctrlreg,
+                                 miphy_phy->ctrlreg,
                                  SYSCFG_SELECT_SATA_MASK,
                                  sata << SYSCFG_SELECT_SATA_POS);
 }
                                  SYSCFG_SELECT_SATA_MASK,
                                  sata << SYSCFG_SELECT_SATA_POS);
 }
@@ -430,7 +430,7 @@ static int miphy365x_init(struct phy *phy)
        }
 
        /* Initialise Miphy for PCIe or SATA */
        }
 
        /* Initialise Miphy for PCIe or SATA */
-       if (miphy_phy->type == MIPHY_TYPE_PCIE)
+       if (miphy_phy->type == PHY_TYPE_PCIE)
                ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
        else
                ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
                ret = miphy365x_init_pcie_port(miphy_phy, miphy_dev);
        else
                ret = miphy365x_init_sata_port(miphy_phy, miphy_dev);
@@ -445,7 +445,6 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
 {
        struct device_node *phynode = miphy_phy->phy->dev.of_node;
        const char *name;
 {
        struct device_node *phynode = miphy_phy->phy->dev.of_node;
        const char *name;
-       const __be32 *taddr;
        int type = miphy_phy->type;
        int ret;
 
        int type = miphy_phy->type;
        int ret;
 
@@ -455,24 +454,8 @@ int miphy365x_get_addr(struct device *dev, struct miphy365x_phy *miphy_phy,
                return ret;
        }
 
                return ret;
        }
 
-       if (!strncmp(name, "syscfg", 6)) {
-               taddr = of_get_address(phynode, index, NULL, NULL);
-               if (!taddr) {
-                       dev_err(dev, "failed to fetch syscfg address\n");
-                       return -EINVAL;
-               }
-
-               miphy_phy->ctrlreg = of_translate_address(phynode, taddr);
-               if (miphy_phy->ctrlreg == OF_BAD_ADDR) {
-                       dev_err(dev, "failed to translate syscfg address\n");
-                       return -EINVAL;
-               }
-
-               return 0;
-       }
-
-       if (!((!strncmp(name, "sata", 4) && type == MIPHY_TYPE_SATA) ||
-             (!strncmp(name, "pcie", 4) && type == MIPHY_TYPE_PCIE)))
+       if (!((!strncmp(name, "sata", 4) && type == PHY_TYPE_SATA) ||
+             (!strncmp(name, "pcie", 4) && type == PHY_TYPE_PCIE)))
                return 0;
 
        miphy_phy->base = of_iomap(phynode, index);
                return 0;
 
        miphy_phy->base = of_iomap(phynode, index);
@@ -515,8 +498,8 @@ static struct phy *miphy365x_xlate(struct device *dev,
 
        miphy_phy->type = args->args[0];
 
 
        miphy_phy->type = args->args[0];
 
-       if (!(miphy_phy->type == MIPHY_TYPE_SATA ||
-             miphy_phy->type == MIPHY_TYPE_PCIE)) {
+       if (!(miphy_phy->type == PHY_TYPE_SATA ||
+             miphy_phy->type == PHY_TYPE_PCIE)) {
                dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
                return ERR_PTR(-EINVAL);
        }
                dev_err(dev, "Unsupported device type: %d\n", miphy_phy->type);
                return ERR_PTR(-EINVAL);
        }
@@ -593,7 +576,7 @@ static int miphy365x_probe(struct platform_device *pdev)
 
                miphy_dev->phys[port] = miphy_phy;
 
 
                miphy_dev->phys[port] = miphy_phy;
 
-               phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops, NULL);
+               phy = devm_phy_create(&pdev->dev, child, &miphy365x_ops);
                if (IS_ERR(phy)) {
                        dev_err(&pdev->dev, "failed to create PHY\n");
                        return PTR_ERR(phy);
                if (IS_ERR(phy)) {
                        dev_err(&pdev->dev, "failed to create PHY\n");
                        return PTR_ERR(phy);
@@ -606,14 +589,19 @@ static int miphy365x_probe(struct platform_device *pdev)
                        return ret;
 
                phy_set_drvdata(phy, miphy_dev->phys[port]);
                        return ret;
 
                phy_set_drvdata(phy, miphy_dev->phys[port]);
+
                port++;
                port++;
+               /* sysconfig offsets are indexed from 1 */
+               ret = of_property_read_u32_index(np, "st,syscfg", port,
+                                       &miphy_phy->ctrlreg);
+               if (ret) {
+                       dev_err(&pdev->dev, "No sysconfig offset found\n");
+                       return ret;
+               }
        }
 
        provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
        }
 
        provider = devm_of_phy_provider_register(&pdev->dev, miphy365x_xlate);
-       if (IS_ERR(provider))
-               return PTR_ERR(provider);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(provider);
 }
 
 static const struct of_device_id miphy365x_of_match[] = {
 }
 
 static const struct of_device_id miphy365x_of_match[] = {
index d395558cb12e327fd1bc42d6f9f8c3a4d59fd5d9..03b94f92e6f17b319c68a58f03ac8cf91ef0be75 100644 (file)
@@ -101,7 +101,7 @@ static int phy_mvebu_sata_probe(struct platform_device *pdev)
        if (IS_ERR(priv->clk))
                return PTR_ERR(priv->clk);
 
        if (IS_ERR(priv->clk))
                return PTR_ERR(priv->clk);
 
-       phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops, NULL);
+       phy = devm_phy_create(&pdev->dev, NULL, &phy_mvebu_sata_ops);
        if (IS_ERR(phy))
                return PTR_ERR(phy);
 
        if (IS_ERR(phy))
                return PTR_ERR(phy);
 
index c96e8183a8ffe061e017976daa40632ac5a22c56..a7653d930e6bf492aae1d80c7978f69c0c038f4d 100644 (file)
 /**
  * omap_control_pcie_pcs - set the PCS delay count
  * @dev: the control module device
 /**
  * omap_control_pcie_pcs - set the PCS delay count
  * @dev: the control module device
- * @id: index of the pcie PHY (should be 1 or 2)
  * @delay: 8 bit delay value
  */
  * @delay: 8 bit delay value
  */
-void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+void omap_control_pcie_pcs(struct device *dev, u8 delay)
 {
        u32 val;
        struct omap_control_phy *control_phy;
 {
        u32 val;
        struct omap_control_phy *control_phy;
@@ -55,8 +54,8 @@ void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
 
        val = readl(control_phy->pcie_pcs);
        val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<
 
        val = readl(control_phy->pcie_pcs);
        val &= ~(OMAP_CTRL_PCIE_PCS_MASK <<
-               (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT));
-       val |= delay << (id * OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
+               OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
+       val |= (delay << OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT);
        writel(val, control_phy->pcie_pcs);
 }
 EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);
        writel(val, control_phy->pcie_pcs);
 }
 EXPORT_SYMBOL_GPL(omap_control_pcie_pcs);
@@ -217,7 +216,6 @@ void omap_control_usb_set_mode(struct device *dev,
                return;
 
        ctrl_phy = dev_get_drvdata(dev);
                return;
 
        ctrl_phy = dev_get_drvdata(dev);
-
        if (!ctrl_phy) {
                dev_err(dev, "Invalid control phy device\n");
                return;
        if (!ctrl_phy) {
                dev_err(dev, "Invalid control phy device\n");
                return;
@@ -242,8 +240,6 @@ void omap_control_usb_set_mode(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
 
 }
 EXPORT_SYMBOL_GPL(omap_control_usb_set_mode);
 
-#ifdef CONFIG_OF
-
 static const enum omap_control_phy_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
 static const enum omap_control_phy_type usb2_data = OMAP_CTRL_TYPE_USB2;
 static const enum omap_control_phy_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
 static const enum omap_control_phy_type 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;
@@ -279,8 +275,6 @@ static const struct of_device_id omap_control_phy_id_table[] = {
        {},
 };
 MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
        {},
 };
 MODULE_DEVICE_TABLE(of, omap_control_phy_id_table);
-#endif
-
 
 static int omap_control_phy_probe(struct platform_device *pdev)
 {
 
 static int omap_control_phy_probe(struct platform_device *pdev)
 {
@@ -288,8 +282,7 @@ static int omap_control_phy_probe(struct platform_device *pdev)
        const struct of_device_id *of_id;
        struct omap_control_phy *control_phy;
 
        const struct of_device_id *of_id;
        struct omap_control_phy *control_phy;
 
-       of_id = of_match_device(of_match_ptr(omap_control_phy_id_table),
-                               &pdev->dev);
+       of_id = of_match_device(omap_control_phy_id_table, &pdev->dev);
        if (!of_id)
                return -EINVAL;
 
        if (!of_id)
                return -EINVAL;
 
@@ -345,7 +338,7 @@ static struct platform_driver omap_control_phy_driver = {
        .probe          = omap_control_phy_probe,
        .driver         = {
                .name   = "omap-control-phy",
        .probe          = omap_control_phy_probe,
        .driver         = {
                .name   = "omap-control-phy",
-               .of_match_table = of_match_ptr(omap_control_phy_id_table),
+               .of_match_table = omap_control_phy_id_table,
        },
 };
 
        },
 };
 
index 08f2da27083779fef174c5844680e57a64718c34..18b33cedadba4334a03b43089c1aa06ba6d8017f 100644 (file)
@@ -144,7 +144,6 @@ static struct phy_ops ops = {
        .owner          = THIS_MODULE,
 };
 
        .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 omap_usb2_data = {
        .label = "omap_usb2",
        .flags = OMAP_USB2_HAS_START_SRP | OMAP_USB2_HAS_SET_VBUS,
@@ -185,7 +184,6 @@ static const struct of_device_id omap_usb2_id_table[] = {
        {},
 };
 MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
        {},
 };
 MODULE_DEVICE_TABLE(of, omap_usb2_id_table);
-#endif
 
 static int omap_usb2_probe(struct platform_device *pdev)
 {
 
 static int omap_usb2_probe(struct platform_device *pdev)
 {
@@ -200,7 +198,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
        const struct of_device_id *of_id;
        struct usb_phy_data *phy_data;
 
        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);
+       of_id = of_match_device(omap_usb2_id_table, &pdev->dev);
 
        if (!of_id)
                return -EINVAL;
 
        if (!of_id)
                return -EINVAL;
@@ -256,7 +254,7 @@ static int omap_usb2_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, phy);
        pm_runtime_enable(phy->dev);
 
        platform_set_drvdata(pdev, phy);
        pm_runtime_enable(phy->dev);
 
-       generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
+       generic_phy = devm_phy_create(phy->dev, NULL, &ops);
        if (IS_ERR(generic_phy)) {
                pm_runtime_disable(phy->dev);
                return PTR_ERR(generic_phy);
        if (IS_ERR(generic_phy)) {
                pm_runtime_disable(phy->dev);
                return PTR_ERR(generic_phy);
@@ -318,7 +316,7 @@ static int omap_usb2_remove(struct platform_device *pdev)
        return 0;
 }
 
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
+#ifdef CONFIG_PM
 
 static int omap_usb2_runtime_suspend(struct device *dev)
 {
 
 static int omap_usb2_runtime_suspend(struct device *dev)
 {
@@ -377,7 +375,7 @@ static struct platform_driver omap_usb2_driver = {
        .driver         = {
                .name   = "omap-usb2",
                .pm     = DEV_PM_OPS,
        .driver         = {
                .name   = "omap-usb2",
                .pm     = DEV_PM_OPS,
-               .of_match_table = of_match_ptr(omap_usb2_id_table),
+               .of_match_table = omap_usb2_id_table,
        },
 };
 
        },
 };
 
index 7b3ddfb6589802e5862a81d9f97a0854b4093c66..4b243f7a10e4a9bb3e9e237e1a25dcb5f80b7b52 100644 (file)
@@ -228,8 +228,7 @@ static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
        if (IS_ERR(phy->mmio))
                return PTR_ERR(phy->mmio);
 
        if (IS_ERR(phy->mmio))
                return PTR_ERR(phy->mmio);
 
-       generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops,
-                                     NULL);
+       generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops);
        if (IS_ERR(generic_phy)) {
                dev_err(dev, "%s: failed to create phy\n", __func__);
                return PTR_ERR(generic_phy);
        if (IS_ERR(generic_phy)) {
                dev_err(dev, "%s: failed to create phy\n", __func__);
                return PTR_ERR(generic_phy);
index 759b0bf5b6b3ed8efe0de9cdafe8a4adaecb2227..6f2fe26279165ff1b8e26821cfd243d0ab79a214 100644 (file)
@@ -150,8 +150,7 @@ static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
        if (IS_ERR(phy->mmio))
                return PTR_ERR(phy->mmio);
 
        if (IS_ERR(phy->mmio))
                return PTR_ERR(phy->mmio);
 
-       generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops,
-                                     NULL);
+       generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops);
        if (IS_ERR(generic_phy)) {
                dev_err(dev, "%s: failed to create phy\n", __func__);
                return PTR_ERR(generic_phy);
        if (IS_ERR(generic_phy)) {
                dev_err(dev, "%s: failed to create phy\n", __func__);
                return PTR_ERR(generic_phy);
diff --git a/drivers/phy/phy-qcom-ufs-i.h b/drivers/phy/phy-qcom-ufs-i.h
new file mode 100644 (file)
index 0000000..591a391
--- /dev/null
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef UFS_QCOM_PHY_I_H_
+#define UFS_QCOM_PHY_I_H_
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/phy/phy-qcom-ufs.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#define readl_poll_timeout(addr, val, cond, sleep_us, timeout_us) \
+({ \
+       ktime_t timeout = ktime_add_us(ktime_get(), timeout_us); \
+       might_sleep_if(timeout_us); \
+       for (;;) { \
+               (val) = readl(addr); \
+               if (cond) \
+                       break; \
+               if (timeout_us && ktime_compare(ktime_get(), timeout) > 0) { \
+                       (val) = readl(addr); \
+                       break; \
+               } \
+               if (sleep_us) \
+                       usleep_range(DIV_ROUND_UP(sleep_us, 4), sleep_us); \
+       } \
+       (cond) ? 0 : -ETIMEDOUT; \
+})
+
+#define UFS_QCOM_PHY_CAL_ENTRY(reg, val)       \
+       {                               \
+               .reg_offset = reg,      \
+               .cfg_value = val,       \
+       }
+
+#define UFS_QCOM_PHY_NAME_LEN  30
+
+enum {
+       MASK_SERDES_START       = 0x1,
+       MASK_PCS_READY          = 0x1,
+};
+
+enum {
+       OFFSET_SERDES_START     = 0x0,
+};
+
+struct ufs_qcom_phy_stored_attributes {
+       u32 att;
+       u32 value;
+};
+
+
+struct ufs_qcom_phy_calibration {
+       u32 reg_offset;
+       u32 cfg_value;
+};
+
+struct ufs_qcom_phy_vreg {
+       const char *name;
+       struct regulator *reg;
+       int max_uA;
+       int min_uV;
+       int max_uV;
+       bool enabled;
+       bool is_always_on;
+};
+
+struct ufs_qcom_phy {
+       struct list_head list;
+       struct device *dev;
+       void __iomem *mmio;
+       void __iomem *dev_ref_clk_ctrl_mmio;
+       struct clk *tx_iface_clk;
+       struct clk *rx_iface_clk;
+       bool is_iface_clk_enabled;
+       struct clk *ref_clk_src;
+       struct clk *ref_clk_parent;
+       struct clk *ref_clk;
+       bool is_ref_clk_enabled;
+       bool is_dev_ref_clk_enabled;
+       struct ufs_qcom_phy_vreg vdda_pll;
+       struct ufs_qcom_phy_vreg vdda_phy;
+       struct ufs_qcom_phy_vreg vddp_ref_clk;
+       unsigned int quirks;
+
+       /*
+       * If UFS link is put into Hibern8 and if UFS PHY analog hardware is
+       * power collapsed (by clearing UFS_PHY_POWER_DOWN_CONTROL), Hibern8
+       * exit might fail even after powering on UFS PHY analog hardware.
+       * Enabling this quirk will help to solve above issue by doing
+       * custom PHY settings just before PHY analog power collapse.
+       */
+       #define UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE  BIT(0)
+
+       u8 host_ctrl_rev_major;
+       u16 host_ctrl_rev_minor;
+       u16 host_ctrl_rev_step;
+
+       char name[UFS_QCOM_PHY_NAME_LEN];
+       struct ufs_qcom_phy_calibration *cached_regs;
+       int cached_regs_table_size;
+       bool is_powered_on;
+       struct ufs_qcom_phy_specific_ops *phy_spec_ops;
+};
+
+/**
+ * struct ufs_qcom_phy_specific_ops - set of pointers to functions which have a
+ * specific implementation per phy. Each UFS phy, should implement
+ * those functions according to its spec and requirements
+ * @calibrate_phy: pointer to a function that calibrate the phy
+ * @start_serdes: pointer to a function that starts the serdes
+ * @is_physical_coding_sublayer_ready: pointer to a function that
+ * checks pcs readiness. returns 0 for success and non-zero for error.
+ * @set_tx_lane_enable: pointer to a function that enable tx lanes
+ * @power_control: pointer to a function that controls analog rail of phy
+ * and writes to QSERDES_RX_SIGDET_CNTRL attribute
+ */
+struct ufs_qcom_phy_specific_ops {
+       int (*calibrate_phy)(struct ufs_qcom_phy *phy, bool is_rate_B);
+       void (*start_serdes)(struct ufs_qcom_phy *phy);
+       int (*is_physical_coding_sublayer_ready)(struct ufs_qcom_phy *phy);
+       void (*set_tx_lane_enable)(struct ufs_qcom_phy *phy, u32 val);
+       void (*power_control)(struct ufs_qcom_phy *phy, bool val);
+};
+
+struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy);
+int ufs_qcom_phy_power_on(struct phy *generic_phy);
+int ufs_qcom_phy_power_off(struct phy *generic_phy);
+int ufs_qcom_phy_exit(struct phy *generic_phy);
+int ufs_qcom_phy_init_clks(struct phy *generic_phy,
+                       struct ufs_qcom_phy *phy_common);
+int ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
+                       struct ufs_qcom_phy *phy_common);
+int ufs_qcom_phy_remove(struct phy *generic_phy,
+                      struct ufs_qcom_phy *ufs_qcom_phy);
+struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
+                       struct ufs_qcom_phy *common_cfg,
+                       struct phy_ops *ufs_qcom_phy_gen_ops,
+                       struct ufs_qcom_phy_specific_ops *phy_spec_ops);
+int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
+                       struct ufs_qcom_phy_calibration *tbl_A, int tbl_size_A,
+                       struct ufs_qcom_phy_calibration *tbl_B, int tbl_size_B,
+                       bool is_rate_B);
+#endif
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.c b/drivers/phy/phy-qcom-ufs-qmp-14nm.c
new file mode 100644 (file)
index 0000000..f5fc50a
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "phy-qcom-ufs-qmp-14nm.h"
+
+#define UFS_PHY_NAME "ufs_phy_qmp_14nm"
+#define UFS_PHY_VDDA_PHY_UV    (925000)
+
+static
+int ufs_qcom_phy_qmp_14nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
+                                       bool is_rate_B)
+{
+       int tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A);
+       int tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
+       int err;
+
+       err = ufs_qcom_phy_calibrate(ufs_qcom_phy, phy_cal_table_rate_A,
+               tbl_size_A, phy_cal_table_rate_B, tbl_size_B, is_rate_B);
+
+       if (err)
+               dev_err(ufs_qcom_phy->dev,
+                       "%s: ufs_qcom_phy_calibrate() failed %d\n",
+                       __func__, err);
+       return err;
+}
+
+static
+void ufs_qcom_phy_qmp_14nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
+{
+       phy_common->quirks =
+               UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
+}
+
+static int ufs_qcom_phy_qmp_14nm_init(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy_qmp_14nm *phy = phy_get_drvdata(generic_phy);
+       struct ufs_qcom_phy *phy_common = &phy->common_cfg;
+       int err;
+
+       err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
+       if (err) {
+               dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
+       if (err) {
+               dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
+                       __func__, err);
+               goto out;
+       }
+       phy_common->vdda_phy.max_uV = UFS_PHY_VDDA_PHY_UV;
+       phy_common->vdda_phy.min_uV = UFS_PHY_VDDA_PHY_UV;
+
+       ufs_qcom_phy_qmp_14nm_advertise_quirks(phy_common);
+
+out:
+       return err;
+}
+
+static
+void ufs_qcom_phy_qmp_14nm_power_control(struct ufs_qcom_phy *phy, bool val)
+{
+       writel_relaxed(val ? 0x1 : 0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+       /*
+        * Before any transactions involving PHY, ensure PHY knows
+        * that it's analog rail is powered ON (or OFF).
+        */
+       mb();
+}
+
+static inline
+void ufs_qcom_phy_qmp_14nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
+{
+       /*
+        * 14nm PHY does not have TX_LANE_ENABLE register.
+        * Implement this function so as not to propagate error to caller.
+        */
+}
+
+static inline void ufs_qcom_phy_qmp_14nm_start_serdes(struct ufs_qcom_phy *phy)
+{
+       u32 tmp;
+
+       tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
+       tmp &= ~MASK_SERDES_START;
+       tmp |= (1 << OFFSET_SERDES_START);
+       writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
+       /* Ensure register value is committed */
+       mb();
+}
+
+static int ufs_qcom_phy_qmp_14nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
+{
+       int err = 0;
+       u32 val;
+
+       err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
+               val, (val & MASK_PCS_READY), 10, 1000000);
+       if (err)
+               dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
+                       __func__, err);
+       return err;
+}
+
+static struct phy_ops ufs_qcom_phy_qmp_14nm_phy_ops = {
+       .init           = ufs_qcom_phy_qmp_14nm_init,
+       .exit           = ufs_qcom_phy_exit,
+       .power_on       = ufs_qcom_phy_power_on,
+       .power_off      = ufs_qcom_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static struct ufs_qcom_phy_specific_ops phy_14nm_ops = {
+       .calibrate_phy          = ufs_qcom_phy_qmp_14nm_phy_calibrate,
+       .start_serdes           = ufs_qcom_phy_qmp_14nm_start_serdes,
+       .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_14nm_is_pcs_ready,
+       .set_tx_lane_enable     = ufs_qcom_phy_qmp_14nm_set_tx_lane_enable,
+       .power_control          = ufs_qcom_phy_qmp_14nm_power_control,
+};
+
+static int ufs_qcom_phy_qmp_14nm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy;
+       struct ufs_qcom_phy_qmp_14nm *phy;
+       int err = 0;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(dev, "%s: failed to allocate phy\n", __func__);
+               err = -ENOMEM;
+               goto out;
+       }
+
+       generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
+                               &ufs_qcom_phy_qmp_14nm_phy_ops, &phy_14nm_ops);
+
+       if (!generic_phy) {
+               dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
+                       __func__);
+               err = -EIO;
+               goto out;
+       }
+
+       phy_set_drvdata(generic_phy, phy);
+
+       strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
+               sizeof(phy->common_cfg.name));
+
+out:
+       return err;
+}
+
+static int ufs_qcom_phy_qmp_14nm_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy = to_phy(dev);
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+       int err = 0;
+
+       err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
+       if (err)
+               dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+static const struct of_device_id ufs_qcom_phy_qmp_14nm_of_match[] = {
+       {.compatible = "qcom,ufs-phy-qmp-14nm"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_14nm_of_match);
+
+static struct platform_driver ufs_qcom_phy_qmp_14nm_driver = {
+       .probe = ufs_qcom_phy_qmp_14nm_probe,
+       .remove = ufs_qcom_phy_qmp_14nm_remove,
+       .driver = {
+               .of_match_table = ufs_qcom_phy_qmp_14nm_of_match,
+               .name = "ufs_qcom_phy_qmp_14nm",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(ufs_qcom_phy_qmp_14nm_driver);
+
+MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 14nm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-qcom-ufs-qmp-14nm.h b/drivers/phy/phy-qcom-ufs-qmp-14nm.h
new file mode 100644 (file)
index 0000000..3aefdba
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef UFS_QCOM_PHY_QMP_14NM_H_
+#define UFS_QCOM_PHY_QMP_14NM_H_
+
+#include "phy-qcom-ufs-i.h"
+
+/* QCOM UFS PHY control registers */
+#define COM_OFF(x)     (0x000 + x)
+#define PHY_OFF(x)     (0xC00 + x)
+#define TX_OFF(n, x)   (0x400 + (0x400 * n) + x)
+#define RX_OFF(n, x)   (0x600 + (0x400 * n) + x)
+
+/* UFS PHY QSERDES COM registers */
+#define QSERDES_COM_BG_TIMER                   COM_OFF(0x0C)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN                COM_OFF(0x34)
+#define QSERDES_COM_SYS_CLK_CTRL               COM_OFF(0x3C)
+#define QSERDES_COM_LOCK_CMP1_MODE0            COM_OFF(0x4C)
+#define QSERDES_COM_LOCK_CMP2_MODE0            COM_OFF(0x50)
+#define QSERDES_COM_LOCK_CMP3_MODE0            COM_OFF(0x54)
+#define QSERDES_COM_LOCK_CMP1_MODE1            COM_OFF(0x58)
+#define QSERDES_COM_LOCK_CMP2_MODE1            COM_OFF(0x5C)
+#define QSERDES_COM_LOCK_CMP3_MODE1            COM_OFF(0x60)
+#define QSERDES_COM_CP_CTRL_MODE0              COM_OFF(0x78)
+#define QSERDES_COM_CP_CTRL_MODE1              COM_OFF(0x7C)
+#define QSERDES_COM_PLL_RCTRL_MODE0            COM_OFF(0x84)
+#define QSERDES_COM_PLL_RCTRL_MODE1            COM_OFF(0x88)
+#define QSERDES_COM_PLL_CCTRL_MODE0            COM_OFF(0x90)
+#define QSERDES_COM_PLL_CCTRL_MODE1            COM_OFF(0x94)
+#define QSERDES_COM_SYSCLK_EN_SEL              COM_OFF(0xAC)
+#define QSERDES_COM_RESETSM_CNTRL              COM_OFF(0xB4)
+#define QSERDES_COM_LOCK_CMP_EN                        COM_OFF(0xC8)
+#define QSERDES_COM_LOCK_CMP_CFG               COM_OFF(0xCC)
+#define QSERDES_COM_DEC_START_MODE0            COM_OFF(0xD0)
+#define QSERDES_COM_DEC_START_MODE1            COM_OFF(0xD4)
+#define QSERDES_COM_DIV_FRAC_START1_MODE0      COM_OFF(0xDC)
+#define QSERDES_COM_DIV_FRAC_START2_MODE0      COM_OFF(0xE0)
+#define QSERDES_COM_DIV_FRAC_START3_MODE0      COM_OFF(0xE4)
+#define QSERDES_COM_DIV_FRAC_START1_MODE1      COM_OFF(0xE8)
+#define QSERDES_COM_DIV_FRAC_START2_MODE1      COM_OFF(0xEC)
+#define QSERDES_COM_DIV_FRAC_START3_MODE1      COM_OFF(0xF0)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE0      COM_OFF(0x108)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE0      COM_OFF(0x10C)
+#define QSERDES_COM_INTEGLOOP_GAIN0_MODE1      COM_OFF(0x110)
+#define QSERDES_COM_INTEGLOOP_GAIN1_MODE1      COM_OFF(0x114)
+#define QSERDES_COM_VCO_TUNE_CTRL              COM_OFF(0x124)
+#define QSERDES_COM_VCO_TUNE_MAP               COM_OFF(0x128)
+#define QSERDES_COM_VCO_TUNE1_MODE0            COM_OFF(0x12C)
+#define QSERDES_COM_VCO_TUNE2_MODE0            COM_OFF(0x130)
+#define QSERDES_COM_VCO_TUNE1_MODE1            COM_OFF(0x134)
+#define QSERDES_COM_VCO_TUNE2_MODE1            COM_OFF(0x138)
+#define QSERDES_COM_VCO_TUNE_TIMER1            COM_OFF(0x144)
+#define QSERDES_COM_VCO_TUNE_TIMER2            COM_OFF(0x148)
+#define QSERDES_COM_CLK_SELECT                 COM_OFF(0x174)
+#define QSERDES_COM_HSCLK_SEL                  COM_OFF(0x178)
+#define QSERDES_COM_CORECLK_DIV                        COM_OFF(0x184)
+#define QSERDES_COM_CORE_CLK_EN                        COM_OFF(0x18C)
+#define QSERDES_COM_CMN_CONFIG                 COM_OFF(0x194)
+#define QSERDES_COM_SVS_MODE_CLK_SEL           COM_OFF(0x19C)
+#define QSERDES_COM_CORECLK_DIV_MODE1          COM_OFF(0x1BC)
+
+/* UFS PHY registers */
+#define UFS_PHY_PHY_START                      PHY_OFF(0x00)
+#define UFS_PHY_POWER_DOWN_CONTROL             PHY_OFF(0x04)
+#define UFS_PHY_PCS_READY_STATUS               PHY_OFF(0x168)
+
+/* UFS PHY TX registers */
+#define QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN      TX_OFF(0, 0x68)
+#define QSERDES_TX_LANE_MODE                           TX_OFF(0, 0x94)
+
+/* UFS PHY RX registers */
+#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN       RX_OFF(0, 0x40)
+#define QSERDES_RX_RX_TERM_BW                  RX_OFF(0, 0x90)
+#define QSERDES_RX_RX_EQ_GAIN1_LSB             RX_OFF(0, 0xC4)
+#define QSERDES_RX_RX_EQ_GAIN1_MSB             RX_OFF(0, 0xC8)
+#define QSERDES_RX_RX_EQ_GAIN2_LSB             RX_OFF(0, 0xCC)
+#define QSERDES_RX_RX_EQ_GAIN2_MSB             RX_OFF(0, 0xD0)
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2       RX_OFF(0, 0xD8)
+#define QSERDES_RX_SIGDET_CNTRL                        RX_OFF(0, 0x114)
+#define QSERDES_RX_SIGDET_LVL                  RX_OFF(0, 0x118)
+#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL       RX_OFF(0, 0x11C)
+#define QSERDES_RX_RX_INTERFACE_MODE           RX_OFF(0, 0x12C)
+
+/*
+ * This structure represents the 14nm specific phy.
+ * common_cfg MUST remain the first field in this structure
+ * in case extra fields are added. This way, when calling
+ * get_ufs_qcom_phy() of generic phy, we can extract the
+ * common phy structure (struct ufs_qcom_phy) out of it
+ * regardless of the relevant specific phy.
+ */
+struct ufs_qcom_phy_qmp_14nm {
+       struct ufs_qcom_phy common_cfg;
+};
+
+static struct ufs_qcom_phy_calibration phy_cal_table_rate_A[] = {
+       UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CMN_CONFIG, 0x0e),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL, 0xd7),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CLK_SELECT, 0x30),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYS_CLK_CTRL, 0x06),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BG_TIMER, 0x0a),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_HSCLK_SEL, 0x05),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV, 0x0a),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORECLK_DIV_MODE1, 0x0a),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_EN, 0x01),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_CTRL, 0x10),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x20),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CORE_CLK_EN, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP_CFG, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER1, 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_TIMER2, 0x3f),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x14),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SVS_MODE_CLK_SEL, 0x05),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE0, 0x82),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE0, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE0, 0x0b),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE0, 0x16),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE0, 0x28),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0x80),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE0, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE0, 0x28),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE0, 0x02),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE0, 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE0, 0x0c),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE0, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START_MODE1, 0x98),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1_MODE1, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2_MODE1, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3_MODE1, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_CP_CTRL_MODE1, 0x0b),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RCTRL_MODE1, 0x16),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CCTRL_MODE1, 0x28),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN0_MODE1, 0x80),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_INTEGLOOP_GAIN1_MODE1, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE1_MODE1, 0xd6),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE2_MODE1, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP1_MODE1, 0x32),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP2_MODE1, 0x0f),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_LOCK_CMP3_MODE1, 0x00),
+
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_HIGHZ_TRANSCEIVER_BIAS_DRVR_EN, 0x45),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE, 0x02),
+
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_LVL, 0x24),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_CNTRL, 0x02),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_INTERFACE_MODE, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x18),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0B),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_TERM_BW, 0x5B),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB, 0xFF),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB, 0x3F),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB, 0xFF),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB, 0x0F),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x0E),
+};
+
+static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_VCO_TUNE_MAP, 0x54),
+};
+
+#endif
diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.c b/drivers/phy/phy-qcom-ufs-qmp-20nm.c
new file mode 100644 (file)
index 0000000..8332f96
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "phy-qcom-ufs-qmp-20nm.h"
+
+#define UFS_PHY_NAME "ufs_phy_qmp_20nm"
+
+static
+int ufs_qcom_phy_qmp_20nm_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
+                                       bool is_rate_B)
+{
+       struct ufs_qcom_phy_calibration *tbl_A, *tbl_B;
+       int tbl_size_A, tbl_size_B;
+       u8 major = ufs_qcom_phy->host_ctrl_rev_major;
+       u16 minor = ufs_qcom_phy->host_ctrl_rev_minor;
+       u16 step = ufs_qcom_phy->host_ctrl_rev_step;
+       int err;
+
+       if ((major == 0x1) && (minor == 0x002) && (step == 0x0000)) {
+               tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_2_0);
+               tbl_A = phy_cal_table_rate_A_1_2_0;
+       } else if ((major == 0x1) && (minor == 0x003) && (step == 0x0000)) {
+               tbl_size_A = ARRAY_SIZE(phy_cal_table_rate_A_1_3_0);
+               tbl_A = phy_cal_table_rate_A_1_3_0;
+       } else {
+               dev_err(ufs_qcom_phy->dev, "%s: Unknown UFS-PHY version, no calibration values\n",
+                       __func__);
+               err = -ENODEV;
+               goto out;
+       }
+
+       tbl_size_B = ARRAY_SIZE(phy_cal_table_rate_B);
+       tbl_B = phy_cal_table_rate_B;
+
+       err = ufs_qcom_phy_calibrate(ufs_qcom_phy, tbl_A, tbl_size_A,
+                                               tbl_B, tbl_size_B, is_rate_B);
+
+       if (err)
+               dev_err(ufs_qcom_phy->dev, "%s: ufs_qcom_phy_calibrate() failed %d\n",
+                       __func__, err);
+
+out:
+       return err;
+}
+
+static
+void ufs_qcom_phy_qmp_20nm_advertise_quirks(struct ufs_qcom_phy *phy_common)
+{
+       phy_common->quirks =
+               UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
+}
+
+static int ufs_qcom_phy_qmp_20nm_init(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy_qmp_20nm *phy = phy_get_drvdata(generic_phy);
+       struct ufs_qcom_phy *phy_common = &phy->common_cfg;
+       int err = 0;
+
+       err = ufs_qcom_phy_init_clks(generic_phy, phy_common);
+       if (err) {
+               dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_clks() failed %d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       err = ufs_qcom_phy_init_vregulators(generic_phy, phy_common);
+       if (err) {
+               dev_err(phy_common->dev, "%s: ufs_qcom_phy_init_vregulators() failed %d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       ufs_qcom_phy_qmp_20nm_advertise_quirks(phy_common);
+
+out:
+       return err;
+}
+
+static
+void ufs_qcom_phy_qmp_20nm_power_control(struct ufs_qcom_phy *phy, bool val)
+{
+       bool hibern8_exit_after_pwr_collapse = phy->quirks &
+               UFS_QCOM_PHY_QUIRK_HIBERN8_EXIT_AFTER_PHY_PWR_COLLAPSE;
+
+       if (val) {
+               writel_relaxed(0x1, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+               /*
+                * Before any transactions involving PHY, ensure PHY knows
+                * that it's analog rail is powered ON.
+                */
+               mb();
+
+               if (hibern8_exit_after_pwr_collapse) {
+                       /*
+                        * Give atleast 1us delay after restoring PHY analog
+                        * power.
+                        */
+                       usleep_range(1, 2);
+                       writel_relaxed(0x0A, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       writel_relaxed(0x08, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       /*
+                        * Make sure workaround is deactivated before proceeding
+                        * with normal PHY operations.
+                        */
+                       mb();
+               }
+       } else {
+               if (hibern8_exit_after_pwr_collapse) {
+                       writel_relaxed(0x0A, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       writel_relaxed(0x02, phy->mmio +
+                                      QSERDES_COM_SYSCLK_EN_SEL_TXBAND);
+                       /*
+                        * Make sure that above workaround is activated before
+                        * PHY analog power collapse.
+                        */
+                       mb();
+               }
+
+               writel_relaxed(0x0, phy->mmio + UFS_PHY_POWER_DOWN_CONTROL);
+               /*
+                * ensure that PHY knows its PHY analog rail is going
+                * to be powered down
+                */
+               mb();
+       }
+}
+
+static
+void ufs_qcom_phy_qmp_20nm_set_tx_lane_enable(struct ufs_qcom_phy *phy, u32 val)
+{
+       writel_relaxed(val & UFS_PHY_TX_LANE_ENABLE_MASK,
+                       phy->mmio + UFS_PHY_TX_LANE_ENABLE);
+       mb();
+}
+
+static inline void ufs_qcom_phy_qmp_20nm_start_serdes(struct ufs_qcom_phy *phy)
+{
+       u32 tmp;
+
+       tmp = readl_relaxed(phy->mmio + UFS_PHY_PHY_START);
+       tmp &= ~MASK_SERDES_START;
+       tmp |= (1 << OFFSET_SERDES_START);
+       writel_relaxed(tmp, phy->mmio + UFS_PHY_PHY_START);
+       mb();
+}
+
+static int ufs_qcom_phy_qmp_20nm_is_pcs_ready(struct ufs_qcom_phy *phy_common)
+{
+       int err = 0;
+       u32 val;
+
+       err = readl_poll_timeout(phy_common->mmio + UFS_PHY_PCS_READY_STATUS,
+                       val, (val & MASK_PCS_READY), 10, 1000000);
+       if (err)
+               dev_err(phy_common->dev, "%s: poll for pcs failed err = %d\n",
+                       __func__, err);
+       return err;
+}
+
+static struct phy_ops ufs_qcom_phy_qmp_20nm_phy_ops = {
+       .init           = ufs_qcom_phy_qmp_20nm_init,
+       .exit           = ufs_qcom_phy_exit,
+       .power_on       = ufs_qcom_phy_power_on,
+       .power_off      = ufs_qcom_phy_power_off,
+       .owner          = THIS_MODULE,
+};
+
+static struct ufs_qcom_phy_specific_ops phy_20nm_ops = {
+       .calibrate_phy          = ufs_qcom_phy_qmp_20nm_phy_calibrate,
+       .start_serdes           = ufs_qcom_phy_qmp_20nm_start_serdes,
+       .is_physical_coding_sublayer_ready = ufs_qcom_phy_qmp_20nm_is_pcs_ready,
+       .set_tx_lane_enable     = ufs_qcom_phy_qmp_20nm_set_tx_lane_enable,
+       .power_control          = ufs_qcom_phy_qmp_20nm_power_control,
+};
+
+static int ufs_qcom_phy_qmp_20nm_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy;
+       struct ufs_qcom_phy_qmp_20nm *phy;
+       int err = 0;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy) {
+               dev_err(dev, "%s: failed to allocate phy\n", __func__);
+               err = -ENOMEM;
+               goto out;
+       }
+
+       generic_phy = ufs_qcom_phy_generic_probe(pdev, &phy->common_cfg,
+                               &ufs_qcom_phy_qmp_20nm_phy_ops, &phy_20nm_ops);
+
+       if (!generic_phy) {
+               dev_err(dev, "%s: ufs_qcom_phy_generic_probe() failed\n",
+                       __func__);
+               err = -EIO;
+               goto out;
+       }
+
+       phy_set_drvdata(generic_phy, phy);
+
+       strlcpy(phy->common_cfg.name, UFS_PHY_NAME,
+                       sizeof(phy->common_cfg.name));
+
+out:
+       return err;
+}
+
+static int ufs_qcom_phy_qmp_20nm_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy = to_phy(dev);
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+       int err = 0;
+
+       err = ufs_qcom_phy_remove(generic_phy, ufs_qcom_phy);
+       if (err)
+               dev_err(dev, "%s: ufs_qcom_phy_remove failed = %d\n",
+                       __func__, err);
+
+       return err;
+}
+
+static const struct of_device_id ufs_qcom_phy_qmp_20nm_of_match[] = {
+       {.compatible = "qcom,ufs-phy-qmp-20nm"},
+       {},
+};
+MODULE_DEVICE_TABLE(of, ufs_qcom_phy_qmp_20nm_of_match);
+
+static struct platform_driver ufs_qcom_phy_qmp_20nm_driver = {
+       .probe = ufs_qcom_phy_qmp_20nm_probe,
+       .remove = ufs_qcom_phy_qmp_20nm_remove,
+       .driver = {
+               .of_match_table = ufs_qcom_phy_qmp_20nm_of_match,
+               .name = "ufs_qcom_phy_qmp_20nm",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(ufs_qcom_phy_qmp_20nm_driver);
+
+MODULE_DESCRIPTION("Universal Flash Storage (UFS) QCOM PHY QMP 20nm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-qcom-ufs-qmp-20nm.h b/drivers/phy/phy-qcom-ufs-qmp-20nm.h
new file mode 100644 (file)
index 0000000..4f3076b
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef UFS_QCOM_PHY_QMP_20NM_H_
+#define UFS_QCOM_PHY_QMP_20NM_H_
+
+#include "phy-qcom-ufs-i.h"
+
+/* QCOM UFS PHY control registers */
+
+#define COM_OFF(x)     (0x000 + x)
+#define PHY_OFF(x)     (0xC00 + x)
+#define TX_OFF(n, x)   (0x400 + (0x400 * n) + x)
+#define RX_OFF(n, x)   (0x600 + (0x400 * n) + x)
+
+/* UFS PHY PLL block registers */
+#define QSERDES_COM_SYS_CLK_CTRL               COM_OFF(0x0)
+#define QSERDES_COM_PLL_VCOTAIL_EN             COM_OFF(0x04)
+#define QSERDES_COM_PLL_CNTRL                  COM_OFF(0x14)
+#define QSERDES_COM_PLL_IP_SETI                        COM_OFF(0x24)
+#define QSERDES_COM_CORE_CLK_IN_SYNC_SEL       COM_OFF(0x28)
+#define QSERDES_COM_BIAS_EN_CLKBUFLR_EN                COM_OFF(0x30)
+#define QSERDES_COM_PLL_CP_SETI                        COM_OFF(0x34)
+#define QSERDES_COM_PLL_IP_SETP                        COM_OFF(0x38)
+#define QSERDES_COM_PLL_CP_SETP                        COM_OFF(0x3C)
+#define QSERDES_COM_SYSCLK_EN_SEL_TXBAND       COM_OFF(0x48)
+#define QSERDES_COM_RESETSM_CNTRL              COM_OFF(0x4C)
+#define QSERDES_COM_RESETSM_CNTRL2             COM_OFF(0x50)
+#define QSERDES_COM_PLLLOCK_CMP1               COM_OFF(0x90)
+#define QSERDES_COM_PLLLOCK_CMP2               COM_OFF(0x94)
+#define QSERDES_COM_PLLLOCK_CMP3               COM_OFF(0x98)
+#define QSERDES_COM_PLLLOCK_CMP_EN             COM_OFF(0x9C)
+#define QSERDES_COM_BGTC                       COM_OFF(0xA0)
+#define QSERDES_COM_DEC_START1                 COM_OFF(0xAC)
+#define QSERDES_COM_PLL_AMP_OS                 COM_OFF(0xB0)
+#define QSERDES_COM_RES_CODE_UP_OFFSET         COM_OFF(0xD8)
+#define QSERDES_COM_RES_CODE_DN_OFFSET         COM_OFF(0xDC)
+#define QSERDES_COM_DIV_FRAC_START1            COM_OFF(0x100)
+#define QSERDES_COM_DIV_FRAC_START2            COM_OFF(0x104)
+#define QSERDES_COM_DIV_FRAC_START3            COM_OFF(0x108)
+#define QSERDES_COM_DEC_START2                 COM_OFF(0x10C)
+#define QSERDES_COM_PLL_RXTXEPCLK_EN           COM_OFF(0x110)
+#define QSERDES_COM_PLL_CRCTRL                 COM_OFF(0x114)
+#define QSERDES_COM_PLL_CLKEPDIV               COM_OFF(0x118)
+
+/* TX LANE n (0, 1) registers */
+#define QSERDES_TX_EMP_POST1_LVL(n)            TX_OFF(n, 0x08)
+#define QSERDES_TX_DRV_LVL(n)                  TX_OFF(n, 0x0C)
+#define QSERDES_TX_LANE_MODE(n)                        TX_OFF(n, 0x54)
+
+/* RX LANE n (0, 1) registers */
+#define QSERDES_RX_CDR_CONTROL1(n)             RX_OFF(n, 0x0)
+#define QSERDES_RX_CDR_CONTROL_HALF(n)         RX_OFF(n, 0x8)
+#define QSERDES_RX_RX_EQ_GAIN1_LSB(n)          RX_OFF(n, 0xA8)
+#define QSERDES_RX_RX_EQ_GAIN1_MSB(n)          RX_OFF(n, 0xAC)
+#define QSERDES_RX_RX_EQ_GAIN2_LSB(n)          RX_OFF(n, 0xB0)
+#define QSERDES_RX_RX_EQ_GAIN2_MSB(n)          RX_OFF(n, 0xB4)
+#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(n)    RX_OFF(n, 0xBC)
+#define QSERDES_RX_CDR_CONTROL_QUARTER(n)      RX_OFF(n, 0xC)
+#define QSERDES_RX_SIGDET_CNTRL(n)             RX_OFF(n, 0x100)
+
+/* UFS PHY registers */
+#define UFS_PHY_PHY_START                      PHY_OFF(0x00)
+#define UFS_PHY_POWER_DOWN_CONTROL             PHY_OFF(0x4)
+#define UFS_PHY_TX_LANE_ENABLE                 PHY_OFF(0x44)
+#define UFS_PHY_PWM_G1_CLK_DIVIDER             PHY_OFF(0x08)
+#define UFS_PHY_PWM_G2_CLK_DIVIDER             PHY_OFF(0x0C)
+#define UFS_PHY_PWM_G3_CLK_DIVIDER             PHY_OFF(0x10)
+#define UFS_PHY_PWM_G4_CLK_DIVIDER             PHY_OFF(0x14)
+#define UFS_PHY_CORECLK_PWM_G1_CLK_DIVIDER     PHY_OFF(0x34)
+#define UFS_PHY_CORECLK_PWM_G2_CLK_DIVIDER     PHY_OFF(0x38)
+#define UFS_PHY_CORECLK_PWM_G3_CLK_DIVIDER     PHY_OFF(0x3C)
+#define UFS_PHY_CORECLK_PWM_G4_CLK_DIVIDER     PHY_OFF(0x40)
+#define UFS_PHY_OMC_STATUS_RDVAL               PHY_OFF(0x68)
+#define UFS_PHY_LINE_RESET_TIME                        PHY_OFF(0x28)
+#define UFS_PHY_LINE_RESET_GRANULARITY         PHY_OFF(0x2C)
+#define UFS_PHY_TSYNC_RSYNC_CNTL               PHY_OFF(0x48)
+#define UFS_PHY_PLL_CNTL                       PHY_OFF(0x50)
+#define UFS_PHY_TX_LARGE_AMP_DRV_LVL           PHY_OFF(0x54)
+#define UFS_PHY_TX_SMALL_AMP_DRV_LVL           PHY_OFF(0x5C)
+#define UFS_PHY_TX_LARGE_AMP_POST_EMP_LVL      PHY_OFF(0x58)
+#define UFS_PHY_TX_SMALL_AMP_POST_EMP_LVL      PHY_OFF(0x60)
+#define UFS_PHY_CFG_CHANGE_CNT_VAL             PHY_OFF(0x64)
+#define UFS_PHY_RX_SYNC_WAIT_TIME              PHY_OFF(0x6C)
+#define UFS_PHY_TX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xB4)
+#define UFS_PHY_RX_MIN_SLEEP_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xE0)
+#define UFS_PHY_TX_MIN_STALL_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xB8)
+#define UFS_PHY_RX_MIN_STALL_NOCONFIG_TIME_CAPABILITY  PHY_OFF(0xE4)
+#define UFS_PHY_TX_MIN_SAVE_CONFIG_TIME_CAPABILITY     PHY_OFF(0xBC)
+#define UFS_PHY_RX_MIN_SAVE_CONFIG_TIME_CAPABILITY     PHY_OFF(0xE8)
+#define UFS_PHY_RX_PWM_BURST_CLOSURE_LENGTH_CAPABILITY PHY_OFF(0xFC)
+#define UFS_PHY_RX_MIN_ACTIVATETIME_CAPABILITY         PHY_OFF(0x100)
+#define UFS_PHY_RX_SIGDET_CTRL3                                PHY_OFF(0x14c)
+#define UFS_PHY_RMMI_ATTR_CTRL                 PHY_OFF(0x160)
+#define UFS_PHY_RMMI_RX_CFGUPDT_L1     (1 << 7)
+#define UFS_PHY_RMMI_TX_CFGUPDT_L1     (1 << 6)
+#define UFS_PHY_RMMI_CFGWR_L1          (1 << 5)
+#define UFS_PHY_RMMI_CFGRD_L1          (1 << 4)
+#define UFS_PHY_RMMI_RX_CFGUPDT_L0     (1 << 3)
+#define UFS_PHY_RMMI_TX_CFGUPDT_L0     (1 << 2)
+#define UFS_PHY_RMMI_CFGWR_L0          (1 << 1)
+#define UFS_PHY_RMMI_CFGRD_L0          (1 << 0)
+#define UFS_PHY_RMMI_ATTRID                    PHY_OFF(0x164)
+#define UFS_PHY_RMMI_ATTRWRVAL                 PHY_OFF(0x168)
+#define UFS_PHY_RMMI_ATTRRDVAL_L0_STATUS       PHY_OFF(0x16C)
+#define UFS_PHY_RMMI_ATTRRDVAL_L1_STATUS       PHY_OFF(0x170)
+#define UFS_PHY_PCS_READY_STATUS               PHY_OFF(0x174)
+
+#define UFS_PHY_TX_LANE_ENABLE_MASK            0x3
+
+/*
+ * This structure represents the 20nm specific phy.
+ * common_cfg MUST remain the first field in this structure
+ * in case extra fields are added. This way, when calling
+ * get_ufs_qcom_phy() of generic phy, we can extract the
+ * common phy structure (struct ufs_qcom_phy) out of it
+ * regardless of the relevant specific phy.
+ */
+struct ufs_qcom_phy_qmp_20nm {
+       struct ufs_qcom_phy common_cfg;
+};
+
+static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_2_0[] = {
+       UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
+       UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x3f),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x1b),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x0f),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(0), 0x2F),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(0), 0x20),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_EMP_POST1_LVL(1), 0x2F),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_DRV_LVL(1), 0x20),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
+};
+
+static struct ufs_qcom_phy_calibration phy_cal_table_rate_A_1_3_0[] = {
+       UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_POWER_DOWN_CONTROL, 0x01),
+       UFS_QCOM_PHY_CAL_ENTRY(UFS_PHY_RX_SIGDET_CTRL3, 0x0D),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_VCOTAIL_EN, 0xe1),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CRCTRL, 0xcc),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_SYSCLK_EN_SEL_TXBAND, 0x08),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CLKEPDIV, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_RXTXEPCLK_EN, 0x10),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x82),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START2, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START1, 0x80),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START2, 0x80),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DIV_FRAC_START3, 0x40),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x19),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP3, 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP_EN, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL, 0x90),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RESETSM_CNTRL2, 0x03),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(0), 0xf2),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(0), 0x0c),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(0), 0x12),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL1(1), 0xf2),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_HALF(1), 0x0c),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_CDR_CONTROL_QUARTER(1), 0x12),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(0), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(0), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(0), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(0), 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_LSB(1), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN1_MSB(1), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_LSB(1), 0xff),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQ_GAIN2_MSB(1), 0x00),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETI, 0x2b),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETP, 0x38),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CP_SETP, 0x3c),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_UP_OFFSET, 0x02),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_RES_CODE_DN_OFFSET, 0x02),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_IP_SETI, 0x01),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLL_CNTRL, 0x40),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(0), 0x68),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_TX_LANE_MODE(1), 0x68),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(1), 0xdc),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2(0), 0xdc),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x3),
+};
+
+static struct ufs_qcom_phy_calibration phy_cal_table_rate_B[] = {
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_DEC_START1, 0x98),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP1, 0x65),
+       UFS_QCOM_PHY_CAL_ENTRY(QSERDES_COM_PLLLOCK_CMP2, 0x1e),
+};
+
+#endif
diff --git a/drivers/phy/phy-qcom-ufs.c b/drivers/phy/phy-qcom-ufs.c
new file mode 100644 (file)
index 0000000..c4199e6
--- /dev/null
@@ -0,0 +1,738 @@
+/*
+ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include "phy-qcom-ufs-i.h"
+
+#define MAX_PROP_NAME              32
+#define VDDA_PHY_MIN_UV            1000000
+#define VDDA_PHY_MAX_UV            1000000
+#define VDDA_PLL_MIN_UV            1800000
+#define VDDA_PLL_MAX_UV            1800000
+#define VDDP_REF_CLK_MIN_UV        1200000
+#define VDDP_REF_CLK_MAX_UV        1200000
+
+static int __ufs_qcom_phy_init_vreg(struct phy *, struct ufs_qcom_phy_vreg *,
+                                   const char *, bool);
+static int ufs_qcom_phy_init_vreg(struct phy *, struct ufs_qcom_phy_vreg *,
+                                 const char *);
+static int ufs_qcom_phy_base_init(struct platform_device *pdev,
+                                 struct ufs_qcom_phy *phy_common);
+
+int ufs_qcom_phy_calibrate(struct ufs_qcom_phy *ufs_qcom_phy,
+                          struct ufs_qcom_phy_calibration *tbl_A,
+                          int tbl_size_A,
+                          struct ufs_qcom_phy_calibration *tbl_B,
+                          int tbl_size_B, bool is_rate_B)
+{
+       int i;
+       int ret = 0;
+
+       if (!tbl_A) {
+               dev_err(ufs_qcom_phy->dev, "%s: tbl_A is NULL", __func__);
+               ret = EINVAL;
+               goto out;
+       }
+
+       for (i = 0; i < tbl_size_A; i++)
+               writel_relaxed(tbl_A[i].cfg_value,
+                              ufs_qcom_phy->mmio + tbl_A[i].reg_offset);
+
+       /*
+        * In case we would like to work in rate B, we need
+        * to override a registers that were configured in rate A table
+        * with registers of rate B table.
+        * table.
+        */
+       if (is_rate_B) {
+               if (!tbl_B) {
+                       dev_err(ufs_qcom_phy->dev, "%s: tbl_B is NULL",
+                               __func__);
+                       ret = EINVAL;
+                       goto out;
+               }
+
+               for (i = 0; i < tbl_size_B; i++)
+                       writel_relaxed(tbl_B[i].cfg_value,
+                               ufs_qcom_phy->mmio + tbl_B[i].reg_offset);
+       }
+
+       /* flush buffered writes */
+       mb();
+
+out:
+       return ret;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_calibrate);
+
+struct phy *ufs_qcom_phy_generic_probe(struct platform_device *pdev,
+                               struct ufs_qcom_phy *common_cfg,
+                               struct phy_ops *ufs_qcom_phy_gen_ops,
+                               struct ufs_qcom_phy_specific_ops *phy_spec_ops)
+{
+       int err;
+       struct device *dev = &pdev->dev;
+       struct phy *generic_phy = NULL;
+       struct phy_provider *phy_provider;
+
+       err = ufs_qcom_phy_base_init(pdev, common_cfg);
+       if (err) {
+               dev_err(dev, "%s: phy base init failed %d\n", __func__, err);
+               goto out;
+       }
+
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (IS_ERR(phy_provider)) {
+               err = PTR_ERR(phy_provider);
+               dev_err(dev, "%s: failed to register phy %d\n", __func__, err);
+               goto out;
+       }
+
+       generic_phy = devm_phy_create(dev, NULL, ufs_qcom_phy_gen_ops);
+       if (IS_ERR(generic_phy)) {
+               err =  PTR_ERR(generic_phy);
+               dev_err(dev, "%s: failed to create phy %d\n", __func__, err);
+               generic_phy = NULL;
+               goto out;
+       }
+
+       common_cfg->phy_spec_ops = phy_spec_ops;
+       common_cfg->dev = dev;
+
+out:
+       return generic_phy;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_generic_probe);
+
+/*
+ * This assumes the embedded phy structure inside generic_phy is of type
+ * struct ufs_qcom_phy. In order to function properly it's crucial
+ * to keep the embedded struct "struct ufs_qcom_phy common_cfg"
+ * as the first inside generic_phy.
+ */
+struct ufs_qcom_phy *get_ufs_qcom_phy(struct phy *generic_phy)
+{
+       return (struct ufs_qcom_phy *)phy_get_drvdata(generic_phy);
+}
+EXPORT_SYMBOL_GPL(get_ufs_qcom_phy);
+
+static
+int ufs_qcom_phy_base_init(struct platform_device *pdev,
+                          struct ufs_qcom_phy *phy_common)
+{
+       struct device *dev = &pdev->dev;
+       struct resource *res;
+       int err = 0;
+
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy_mem");
+       phy_common->mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR((void const *)phy_common->mmio)) {
+               err = PTR_ERR((void const *)phy_common->mmio);
+               phy_common->mmio = NULL;
+               dev_err(dev, "%s: ioremap for phy_mem resource failed %d\n",
+                       __func__, err);
+               return err;
+       }
+
+       /* "dev_ref_clk_ctrl_mem" is optional resource */
+       res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+                                          "dev_ref_clk_ctrl_mem");
+       phy_common->dev_ref_clk_ctrl_mmio = devm_ioremap_resource(dev, res);
+       if (IS_ERR((void const *)phy_common->dev_ref_clk_ctrl_mmio))
+               phy_common->dev_ref_clk_ctrl_mmio = NULL;
+
+       return 0;
+}
+
+static int __ufs_qcom_phy_clk_get(struct phy *phy,
+                        const char *name, struct clk **clk_out, bool err_print)
+{
+       struct clk *clk;
+       int err = 0;
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
+       struct device *dev = ufs_qcom_phy->dev;
+
+       clk = devm_clk_get(dev, name);
+       if (IS_ERR(clk)) {
+               err = PTR_ERR(clk);
+               if (err_print)
+                       dev_err(dev, "failed to get %s err %d", name, err);
+       } else {
+               *clk_out = clk;
+       }
+
+       return err;
+}
+
+static
+int ufs_qcom_phy_clk_get(struct phy *phy,
+                        const char *name, struct clk **clk_out)
+{
+       return __ufs_qcom_phy_clk_get(phy, name, clk_out, true);
+}
+
+int
+ufs_qcom_phy_init_clks(struct phy *generic_phy,
+                      struct ufs_qcom_phy *phy_common)
+{
+       int err;
+
+       err = ufs_qcom_phy_clk_get(generic_phy, "tx_iface_clk",
+                                  &phy_common->tx_iface_clk);
+       if (err)
+               goto out;
+
+       err = ufs_qcom_phy_clk_get(generic_phy, "rx_iface_clk",
+                                  &phy_common->rx_iface_clk);
+       if (err)
+               goto out;
+
+       err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk_src",
+                                  &phy_common->ref_clk_src);
+       if (err)
+               goto out;
+
+       /*
+        * "ref_clk_parent" is optional hence don't abort init if it's not
+        * found.
+        */
+       __ufs_qcom_phy_clk_get(generic_phy, "ref_clk_parent",
+                                  &phy_common->ref_clk_parent, false);
+
+       err = ufs_qcom_phy_clk_get(generic_phy, "ref_clk",
+                                  &phy_common->ref_clk);
+
+out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_clks);
+
+int
+ufs_qcom_phy_init_vregulators(struct phy *generic_phy,
+                             struct ufs_qcom_phy *phy_common)
+{
+       int err;
+
+       err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_pll,
+               "vdda-pll");
+       if (err)
+               goto out;
+
+       err = ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vdda_phy,
+               "vdda-phy");
+
+       if (err)
+               goto out;
+
+       /* vddp-ref-clk-* properties are optional */
+       __ufs_qcom_phy_init_vreg(generic_phy, &phy_common->vddp_ref_clk,
+                                "vddp-ref-clk", true);
+out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_init_vregulators);
+
+static int __ufs_qcom_phy_init_vreg(struct phy *phy,
+               struct ufs_qcom_phy_vreg *vreg, const char *name, bool optional)
+{
+       int err = 0;
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
+       struct device *dev = ufs_qcom_phy->dev;
+
+       char prop_name[MAX_PROP_NAME];
+
+       vreg->name = kstrdup(name, GFP_KERNEL);
+       if (!vreg->name) {
+               err = -ENOMEM;
+               goto out;
+       }
+
+       vreg->reg = devm_regulator_get(dev, name);
+       if (IS_ERR(vreg->reg)) {
+               err = PTR_ERR(vreg->reg);
+               vreg->reg = NULL;
+               if (!optional)
+                       dev_err(dev, "failed to get %s, %d\n", name, err);
+               goto out;
+       }
+
+       if (dev->of_node) {
+               snprintf(prop_name, MAX_PROP_NAME, "%s-max-microamp", name);
+               err = of_property_read_u32(dev->of_node,
+                                       prop_name, &vreg->max_uA);
+               if (err && err != -EINVAL) {
+                       dev_err(dev, "%s: failed to read %s\n",
+                                       __func__, prop_name);
+                       goto out;
+               } else if (err == -EINVAL || !vreg->max_uA) {
+                       if (regulator_count_voltages(vreg->reg) > 0) {
+                               dev_err(dev, "%s: %s is mandatory\n",
+                                               __func__, prop_name);
+                               goto out;
+                       }
+                       err = 0;
+               }
+               snprintf(prop_name, MAX_PROP_NAME, "%s-always-on", name);
+               if (of_get_property(dev->of_node, prop_name, NULL))
+                       vreg->is_always_on = true;
+               else
+                       vreg->is_always_on = false;
+       }
+
+       if (!strcmp(name, "vdda-pll")) {
+               vreg->max_uV = VDDA_PLL_MAX_UV;
+               vreg->min_uV = VDDA_PLL_MIN_UV;
+       } else if (!strcmp(name, "vdda-phy")) {
+               vreg->max_uV = VDDA_PHY_MAX_UV;
+               vreg->min_uV = VDDA_PHY_MIN_UV;
+       } else if (!strcmp(name, "vddp-ref-clk")) {
+               vreg->max_uV = VDDP_REF_CLK_MAX_UV;
+               vreg->min_uV = VDDP_REF_CLK_MIN_UV;
+       }
+
+out:
+       if (err)
+               kfree(vreg->name);
+       return err;
+}
+
+static int ufs_qcom_phy_init_vreg(struct phy *phy,
+                       struct ufs_qcom_phy_vreg *vreg, const char *name)
+{
+       return __ufs_qcom_phy_init_vreg(phy, vreg, name, false);
+}
+
+static
+int ufs_qcom_phy_cfg_vreg(struct phy *phy,
+                         struct ufs_qcom_phy_vreg *vreg, bool on)
+{
+       int ret = 0;
+       struct regulator *reg = vreg->reg;
+       const char *name = vreg->name;
+       int min_uV;
+       int uA_load;
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
+       struct device *dev = ufs_qcom_phy->dev;
+
+       BUG_ON(!vreg);
+
+       if (regulator_count_voltages(reg) > 0) {
+               min_uV = on ? vreg->min_uV : 0;
+               ret = regulator_set_voltage(reg, min_uV, vreg->max_uV);
+               if (ret) {
+                       dev_err(dev, "%s: %s set voltage failed, err=%d\n",
+                                       __func__, name, ret);
+                       goto out;
+               }
+               uA_load = on ? vreg->max_uA : 0;
+               ret = regulator_set_optimum_mode(reg, uA_load);
+               if (ret >= 0) {
+                       /*
+                        * regulator_set_optimum_mode() returns new regulator
+                        * mode upon success.
+                        */
+                       ret = 0;
+               } else {
+                       dev_err(dev, "%s: %s set optimum mode(uA_load=%d) failed, err=%d\n",
+                                       __func__, name, uA_load, ret);
+                       goto out;
+               }
+       }
+out:
+       return ret;
+}
+
+static
+int ufs_qcom_phy_enable_vreg(struct phy *phy,
+                            struct ufs_qcom_phy_vreg *vreg)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
+       struct device *dev = ufs_qcom_phy->dev;
+       int ret = 0;
+
+       if (!vreg || vreg->enabled)
+               goto out;
+
+       ret = ufs_qcom_phy_cfg_vreg(phy, vreg, true);
+       if (ret) {
+               dev_err(dev, "%s: ufs_qcom_phy_cfg_vreg() failed, err=%d\n",
+                       __func__, ret);
+               goto out;
+       }
+
+       ret = regulator_enable(vreg->reg);
+       if (ret) {
+               dev_err(dev, "%s: enable failed, err=%d\n",
+                               __func__, ret);
+               goto out;
+       }
+
+       vreg->enabled = true;
+out:
+       return ret;
+}
+
+int ufs_qcom_phy_enable_ref_clk(struct phy *generic_phy)
+{
+       int ret = 0;
+       struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
+
+       if (phy->is_ref_clk_enabled)
+               goto out;
+
+       /*
+        * reference clock is propagated in a daisy-chained manner from
+        * source to phy, so ungate them at each stage.
+        */
+       ret = clk_prepare_enable(phy->ref_clk_src);
+       if (ret) {
+               dev_err(phy->dev, "%s: ref_clk_src enable failed %d\n",
+                               __func__, ret);
+               goto out;
+       }
+
+       /*
+        * "ref_clk_parent" is optional clock hence make sure that clk reference
+        * is available before trying to enable the clock.
+        */
+       if (phy->ref_clk_parent) {
+               ret = clk_prepare_enable(phy->ref_clk_parent);
+               if (ret) {
+                       dev_err(phy->dev, "%s: ref_clk_parent enable failed %d\n",
+                                       __func__, ret);
+                       goto out_disable_src;
+               }
+       }
+
+       ret = clk_prepare_enable(phy->ref_clk);
+       if (ret) {
+               dev_err(phy->dev, "%s: ref_clk enable failed %d\n",
+                               __func__, ret);
+               goto out_disable_parent;
+       }
+
+       phy->is_ref_clk_enabled = true;
+       goto out;
+
+out_disable_parent:
+       if (phy->ref_clk_parent)
+               clk_disable_unprepare(phy->ref_clk_parent);
+out_disable_src:
+       clk_disable_unprepare(phy->ref_clk_src);
+out:
+       return ret;
+}
+
+static
+int ufs_qcom_phy_disable_vreg(struct phy *phy,
+                             struct ufs_qcom_phy_vreg *vreg)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(phy);
+       struct device *dev = ufs_qcom_phy->dev;
+       int ret = 0;
+
+       if (!vreg || !vreg->enabled || vreg->is_always_on)
+               goto out;
+
+       ret = regulator_disable(vreg->reg);
+
+       if (!ret) {
+               /* ignore errors on applying disable config */
+               ufs_qcom_phy_cfg_vreg(phy, vreg, false);
+               vreg->enabled = false;
+       } else {
+               dev_err(dev, "%s: %s disable failed, err=%d\n",
+                               __func__, vreg->name, ret);
+       }
+out:
+       return ret;
+}
+
+void ufs_qcom_phy_disable_ref_clk(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
+
+       if (phy->is_ref_clk_enabled) {
+               clk_disable_unprepare(phy->ref_clk);
+               /*
+                * "ref_clk_parent" is optional clock hence make sure that clk
+                * reference is available before trying to disable the clock.
+                */
+               if (phy->ref_clk_parent)
+                       clk_disable_unprepare(phy->ref_clk_parent);
+               clk_disable_unprepare(phy->ref_clk_src);
+               phy->is_ref_clk_enabled = false;
+       }
+}
+
+#define UFS_REF_CLK_EN (1 << 5)
+
+static void ufs_qcom_phy_dev_ref_clk_ctrl(struct phy *generic_phy, bool enable)
+{
+       struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
+
+       if (phy->dev_ref_clk_ctrl_mmio &&
+           (enable ^ phy->is_dev_ref_clk_enabled)) {
+               u32 temp = readl_relaxed(phy->dev_ref_clk_ctrl_mmio);
+
+               if (enable)
+                       temp |= UFS_REF_CLK_EN;
+               else
+                       temp &= ~UFS_REF_CLK_EN;
+
+               /*
+                * If we are here to disable this clock immediately after
+                * entering into hibern8, we need to make sure that device
+                * ref_clk is active atleast 1us after the hibern8 enter.
+                */
+               if (!enable)
+                       udelay(1);
+
+               writel_relaxed(temp, phy->dev_ref_clk_ctrl_mmio);
+               /* ensure that ref_clk is enabled/disabled before we return */
+               wmb();
+               /*
+                * If we call hibern8 exit after this, we need to make sure that
+                * device ref_clk is stable for atleast 1us before the hibern8
+                * exit command.
+                */
+               if (enable)
+                       udelay(1);
+
+               phy->is_dev_ref_clk_enabled = enable;
+       }
+}
+
+void ufs_qcom_phy_enable_dev_ref_clk(struct phy *generic_phy)
+{
+       ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, true);
+}
+
+void ufs_qcom_phy_disable_dev_ref_clk(struct phy *generic_phy)
+{
+       ufs_qcom_phy_dev_ref_clk_ctrl(generic_phy, false);
+}
+
+/* Turn ON M-PHY RMMI interface clocks */
+int ufs_qcom_phy_enable_iface_clk(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
+       int ret = 0;
+
+       if (phy->is_iface_clk_enabled)
+               goto out;
+
+       ret = clk_prepare_enable(phy->tx_iface_clk);
+       if (ret) {
+               dev_err(phy->dev, "%s: tx_iface_clk enable failed %d\n",
+                               __func__, ret);
+               goto out;
+       }
+       ret = clk_prepare_enable(phy->rx_iface_clk);
+       if (ret) {
+               clk_disable_unprepare(phy->tx_iface_clk);
+               dev_err(phy->dev, "%s: rx_iface_clk enable failed %d. disabling also tx_iface_clk\n",
+                               __func__, ret);
+               goto out;
+       }
+       phy->is_iface_clk_enabled = true;
+
+out:
+       return ret;
+}
+
+/* Turn OFF M-PHY RMMI interface clocks */
+void ufs_qcom_phy_disable_iface_clk(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *phy = get_ufs_qcom_phy(generic_phy);
+
+       if (phy->is_iface_clk_enabled) {
+               clk_disable_unprepare(phy->tx_iface_clk);
+               clk_disable_unprepare(phy->rx_iface_clk);
+               phy->is_iface_clk_enabled = false;
+       }
+}
+
+int ufs_qcom_phy_start_serdes(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+       int ret = 0;
+
+       if (!ufs_qcom_phy->phy_spec_ops->start_serdes) {
+               dev_err(ufs_qcom_phy->dev, "%s: start_serdes() callback is not supported\n",
+                       __func__);
+               ret = -ENOTSUPP;
+       } else {
+               ufs_qcom_phy->phy_spec_ops->start_serdes(ufs_qcom_phy);
+       }
+
+       return ret;
+}
+
+int ufs_qcom_phy_set_tx_lane_enable(struct phy *generic_phy, u32 tx_lanes)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+       int ret = 0;
+
+       if (!ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable) {
+               dev_err(ufs_qcom_phy->dev, "%s: set_tx_lane_enable() callback is not supported\n",
+                       __func__);
+               ret = -ENOTSUPP;
+       } else {
+               ufs_qcom_phy->phy_spec_ops->set_tx_lane_enable(ufs_qcom_phy,
+                                                              tx_lanes);
+       }
+
+       return ret;
+}
+
+void ufs_qcom_phy_save_controller_version(struct phy *generic_phy,
+                                         u8 major, u16 minor, u16 step)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+
+       ufs_qcom_phy->host_ctrl_rev_major = major;
+       ufs_qcom_phy->host_ctrl_rev_minor = minor;
+       ufs_qcom_phy->host_ctrl_rev_step = step;
+}
+
+int ufs_qcom_phy_calibrate_phy(struct phy *generic_phy, bool is_rate_B)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+       int ret = 0;
+
+       if (!ufs_qcom_phy->phy_spec_ops->calibrate_phy) {
+               dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() callback is not supported\n",
+                       __func__);
+               ret = -ENOTSUPP;
+       } else {
+               ret = ufs_qcom_phy->phy_spec_ops->
+                               calibrate_phy(ufs_qcom_phy, is_rate_B);
+               if (ret)
+                       dev_err(ufs_qcom_phy->dev, "%s: calibrate_phy() failed %d\n",
+                               __func__, ret);
+       }
+
+       return ret;
+}
+
+int ufs_qcom_phy_remove(struct phy *generic_phy,
+                       struct ufs_qcom_phy *ufs_qcom_phy)
+{
+       phy_power_off(generic_phy);
+
+       kfree(ufs_qcom_phy->vdda_pll.name);
+       kfree(ufs_qcom_phy->vdda_phy.name);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_remove);
+
+int ufs_qcom_phy_exit(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+
+       if (ufs_qcom_phy->is_powered_on)
+               phy_power_off(generic_phy);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_exit);
+
+int ufs_qcom_phy_is_pcs_ready(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *ufs_qcom_phy = get_ufs_qcom_phy(generic_phy);
+
+       if (!ufs_qcom_phy->phy_spec_ops->is_physical_coding_sublayer_ready) {
+               dev_err(ufs_qcom_phy->dev, "%s: is_physical_coding_sublayer_ready() callback is not supported\n",
+                       __func__);
+               return -ENOTSUPP;
+       }
+
+       return ufs_qcom_phy->phy_spec_ops->
+                       is_physical_coding_sublayer_ready(ufs_qcom_phy);
+}
+
+int ufs_qcom_phy_power_on(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
+       struct device *dev = phy_common->dev;
+       int err;
+
+       err = ufs_qcom_phy_enable_vreg(generic_phy, &phy_common->vdda_phy);
+       if (err) {
+               dev_err(dev, "%s enable vdda_phy failed, err=%d\n",
+                       __func__, err);
+               goto out;
+       }
+
+       phy_common->phy_spec_ops->power_control(phy_common, true);
+
+       /* vdda_pll also enables ref clock LDOs so enable it first */
+       err = ufs_qcom_phy_enable_vreg(generic_phy, &phy_common->vdda_pll);
+       if (err) {
+               dev_err(dev, "%s enable vdda_pll failed, err=%d\n",
+                       __func__, err);
+               goto out_disable_phy;
+       }
+
+       err = ufs_qcom_phy_enable_ref_clk(generic_phy);
+       if (err) {
+               dev_err(dev, "%s enable phy ref clock failed, err=%d\n",
+                       __func__, err);
+               goto out_disable_pll;
+       }
+
+       /* enable device PHY ref_clk pad rail */
+       if (phy_common->vddp_ref_clk.reg) {
+               err = ufs_qcom_phy_enable_vreg(generic_phy,
+                                              &phy_common->vddp_ref_clk);
+               if (err) {
+                       dev_err(dev, "%s enable vddp_ref_clk failed, err=%d\n",
+                               __func__, err);
+                       goto out_disable_ref_clk;
+               }
+       }
+
+       phy_common->is_powered_on = true;
+       goto out;
+
+out_disable_ref_clk:
+       ufs_qcom_phy_disable_ref_clk(generic_phy);
+out_disable_pll:
+       ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
+out_disable_phy:
+       ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
+out:
+       return err;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_on);
+
+int ufs_qcom_phy_power_off(struct phy *generic_phy)
+{
+       struct ufs_qcom_phy *phy_common = get_ufs_qcom_phy(generic_phy);
+
+       phy_common->phy_spec_ops->power_control(phy_common, false);
+
+       if (phy_common->vddp_ref_clk.reg)
+               ufs_qcom_phy_disable_vreg(generic_phy,
+                                         &phy_common->vddp_ref_clk);
+       ufs_qcom_phy_disable_ref_clk(generic_phy);
+
+       ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_pll);
+       ufs_qcom_phy_disable_vreg(generic_phy, &phy_common->vdda_phy);
+       phy_common->is_powered_on = false;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ufs_qcom_phy_power_off);
index 2793af17799f6a519b34ef479ff36f0a395d8673..778276aba3aa0092d8e8e7bc2de15eae4f5a5a15 100644 (file)
@@ -304,7 +304,7 @@ static int rcar_gen2_phy_probe(struct platform_device *pdev)
                        phy->select_value = select_value[channel_num][n];
 
                        phy->phy = devm_phy_create(dev, NULL,
                        phy->select_value = select_value[channel_num][n];
 
                        phy->phy = devm_phy_create(dev, NULL,
-                                                  &rcar_gen2_phy_ops, NULL);
+                                                  &rcar_gen2_phy_ops);
                        if (IS_ERR(phy->phy)) {
                                dev_err(dev, "Failed to create PHY\n");
                                return PTR_ERR(phy->phy);
                        if (IS_ERR(phy->phy)) {
                                dev_err(dev, "Failed to create PHY\n");
                                return PTR_ERR(phy->phy);
index 2586b767bcb20420830f1d139a2ecb89b727a55f..c1d94dcc0bab92407fb6e97c1e5f8a9c05649b67 100644 (file)
@@ -1,7 +1,7 @@
 /*
  * Rockchip usb PHY driver
  *
 /*
  * Rockchip usb PHY driver
  *
- * Copyright (C) 2014 Roy Li <lyz@rock-chips.com>
+ * Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com>
  * Copyright (C) 2014 ROCKCHIP, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * Copyright (C) 2014 ROCKCHIP, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 
-#define ROCKCHIP_RK3288_UOC(n) (0x320 + n * 0x14)
-
-#define SIDDQ_MSK              (1 << (13 + 16))
-#define SIDDQ_ON               (1 << 13)
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(13 + 16) set to 1 the BIT(13) can be written.
+ */
+#define SIDDQ_WRITE_ENA        BIT(29)
+#define SIDDQ_ON               BIT(13)
 #define SIDDQ_OFF              (0 << 13)
 
 #define SIDDQ_OFF              (0 << 13)
 
-enum rk3288_phy_id {
-       RK3288_OTG,
-       RK3288_HOST0,
-       RK3288_HOST1,
-       RK3288_NUM_PHYS,
-};
-
 struct rockchip_usb_phy {
 struct rockchip_usb_phy {
-       struct regmap *reg_base;
-       unsigned int reg_offset;
-       struct clk *clk;
-       struct phy *phy;
+       unsigned int    reg_offset;
+       struct regmap   *reg_base;
+       struct clk      *clk;
+       struct phy      *phy;
 };
 
 static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
                                           bool siddq)
 {
        return regmap_write(phy->reg_base, phy->reg_offset,
 };
 
 static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
                                           bool siddq)
 {
        return regmap_write(phy->reg_base, phy->reg_offset,
-                           SIDDQ_MSK | (siddq ? SIDDQ_ON : SIDDQ_OFF));
+                           SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
 }
 
 static int rockchip_usb_phy_power_off(struct phy *_phy)
 }
 
 static int rockchip_usb_phy_power_off(struct phy *_phy)
@@ -60,7 +55,7 @@ static int rockchip_usb_phy_power_off(struct phy *_phy)
        struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
        int ret = 0;
 
        struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
        int ret = 0;
 
-       /* Power down usb phy analog blocks by set siddq 1*/
+       /* Power down usb phy analog blocks by set siddq 1 */
        ret = rockchip_usb_phy_power(phy, 1);
        if (ret)
                return ret;
        ret = rockchip_usb_phy_power(phy, 1);
        if (ret)
                return ret;
@@ -81,7 +76,7 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
        if (ret)
                return ret;
 
        if (ret)
                return ret;
 
-       /* Power up usb phy analog blocks by set siddq 0*/
+       /* Power up usb phy analog blocks by set siddq 0 */
        ret = rockchip_usb_phy_power(phy, 0);
        if (ret)
                return ret;
        ret = rockchip_usb_phy_power(phy, 0);
        if (ret)
                return ret;
@@ -89,17 +84,6 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
        return 0;
 }
 
        return 0;
 }
 
-static struct phy *rockchip_usb_phy_xlate(struct device *dev,
-                                       struct of_phandle_args *args)
-{
-       struct rockchip_usb_phy *phy_array = dev_get_drvdata(dev);
-
-       if (WARN_ON(args->args[0] == 0 || args->args[0] >= RK3288_NUM_PHYS))
-               return ERR_PTR(-ENODEV);
-
-       return (phy_array + args->args[0])->phy;
-}
-
 static struct phy_ops ops = {
        .power_on       = rockchip_usb_phy_power_on,
        .power_off      = rockchip_usb_phy_power_off,
 static struct phy_ops ops = {
        .power_on       = rockchip_usb_phy_power_on,
        .power_off      = rockchip_usb_phy_power_off,
@@ -110,11 +94,10 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct rockchip_usb_phy *rk_phy;
 {
        struct device *dev = &pdev->dev;
        struct rockchip_usb_phy *rk_phy;
-       struct rockchip_usb_phy *phy_array;
        struct phy_provider *phy_provider;
        struct phy_provider *phy_provider;
+       struct device_node *child;
        struct regmap *grf;
        struct regmap *grf;
-       char clk_name[16];
-       int i;
+       unsigned int reg_offset;
 
        grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
        if (IS_ERR(grf)) {
 
        grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
        if (IS_ERR(grf)) {
@@ -122,38 +105,37 @@ static int rockchip_usb_phy_probe(struct platform_device *pdev)
                return PTR_ERR(grf);
        }
 
                return PTR_ERR(grf);
        }
 
-       phy_array = devm_kzalloc(dev, RK3288_NUM_PHYS * sizeof(*rk_phy),
-                                GFP_KERNEL);
-       if (!phy_array)
-               return -ENOMEM;
+       for_each_available_child_of_node(dev->of_node, child) {
+               rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
+               if (!rk_phy)
+                       return -ENOMEM;
 
 
-       for (i = 0; i < RK3288_NUM_PHYS; i++) {
-               rk_phy = &phy_array[i];
+               if (of_property_read_u32(child, "reg", &reg_offset)) {
+                       dev_err(dev, "missing reg property in node %s\n",
+                               child->name);
+                       return -EINVAL;
+               }
 
 
+               rk_phy->reg_offset = reg_offset;
                rk_phy->reg_base = grf;
 
                rk_phy->reg_base = grf;
 
-               rk_phy->reg_offset = ROCKCHIP_RK3288_UOC(i);
-
-               snprintf(clk_name, sizeof(clk_name), "usbphy%d", i);
-               rk_phy->clk = devm_clk_get(dev, clk_name);
-               if (IS_ERR(rk_phy->clk)) {
-                       dev_warn(dev, "failed to get clock %s\n", clk_name);
+               rk_phy->clk = of_clk_get_by_name(child, "phyclk");
+               if (IS_ERR(rk_phy->clk))
                        rk_phy->clk = NULL;
                        rk_phy->clk = NULL;
-               }
 
 
-               rk_phy->phy = devm_phy_create(dev, NULL, &ops, NULL);
+               rk_phy->phy = devm_phy_create(dev, child, &ops);
                if (IS_ERR(rk_phy->phy)) {
                if (IS_ERR(rk_phy->phy)) {
-                       dev_err(dev, "failed to create PHY %d\n", i);
+                       dev_err(dev, "failed to create PHY\n");
                        return PTR_ERR(rk_phy->phy);
                }
                phy_set_drvdata(rk_phy->phy, rk_phy);
        }
 
                        return PTR_ERR(rk_phy->phy);
                }
                phy_set_drvdata(rk_phy->phy, rk_phy);
        }
 
-       platform_set_drvdata(pdev, phy_array);
-
-       phy_provider = devm_of_phy_provider_register(dev,
-                                                    rockchip_usb_phy_xlate);
-       return PTR_ERR_OR_ZERO(phy_provider);
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+       if (PTR_ERR(phy_provider))
+               return (long)phy_provider;
+       else
+               return 0;
 }
 
 static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
 }
 
 static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
@@ -174,6 +156,6 @@ static struct platform_driver rockchip_usb_driver = {
 
 module_platform_driver(rockchip_usb_driver);
 
 
 module_platform_driver(rockchip_usb_driver);
 
-MODULE_AUTHOR("Roy Li <lyz@rock-chips.com>");
+MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
 MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
 MODULE_LICENSE("GPL v2");
index 908949dfb4dbec93e22c55c27b293c2cb5bf6c9f..55b6994932e37f0b3d5d14c72cb1ff7df4807f85 100644 (file)
@@ -37,10 +37,14 @@ static int samsung_usb2_phy_power_on(struct phy *phy)
                spin_lock(&drv->lock);
                ret = inst->cfg->power_on(inst);
                spin_unlock(&drv->lock);
                spin_lock(&drv->lock);
                ret = inst->cfg->power_on(inst);
                spin_unlock(&drv->lock);
+               if (ret)
+                       goto err_power_on;
        }
 
        return 0;
 
        }
 
        return 0;
 
+err_power_on:
+       clk_disable_unprepare(drv->ref_clk);
 err_instance_clk:
        clk_disable_unprepare(drv->clk);
 err_main_clk:
 err_instance_clk:
        clk_disable_unprepare(drv->clk);
 err_main_clk:
@@ -51,7 +55,7 @@ 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;
 {
        struct samsung_usb2_phy_instance *inst = phy_get_drvdata(phy);
        struct samsung_usb2_phy_driver *drv = inst->drv;
-       int ret = 0;
+       int ret;
 
        dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
                inst->cfg->label);
 
        dev_dbg(drv->dev, "Request to power_off \"%s\" usb phy\n",
                inst->cfg->label);
@@ -59,10 +63,12 @@ static int samsung_usb2_phy_power_off(struct phy *phy)
                spin_lock(&drv->lock);
                ret = inst->cfg->power_off(inst);
                spin_unlock(&drv->lock);
                spin_lock(&drv->lock);
                ret = inst->cfg->power_off(inst);
                spin_unlock(&drv->lock);
+               if (ret)
+                       return ret;
        }
        clk_disable_unprepare(drv->ref_clk);
        clk_disable_unprepare(drv->clk);
        }
        clk_disable_unprepare(drv->ref_clk);
        clk_disable_unprepare(drv->clk);
-       return ret;
+       return 0;
 }
 
 static struct phy_ops samsung_usb2_phy_ops = {
 }
 
 static struct phy_ops samsung_usb2_phy_ops = {
@@ -202,8 +208,7 @@ static int samsung_usb2_phy_probe(struct platform_device *pdev)
                struct samsung_usb2_phy_instance *p = &drv->instances[i];
 
                dev_dbg(dev, "Creating phy \"%s\"\n", label);
                struct samsung_usb2_phy_instance *p = &drv->instances[i];
 
                dev_dbg(dev, "Creating phy \"%s\"\n", label);
-               p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops,
-                                        NULL);
+               p->phy = devm_phy_create(dev, NULL, &samsung_usb2_phy_ops);
                if (IS_ERR(p->phy)) {
                        dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
                                label);
                if (IS_ERR(p->phy)) {
                        dev_err(drv->dev, "Failed to create usb2_phy \"%s\"\n",
                                label);
index 5f4c586ee951ddd95776f92d6303987f2714fb3b..65ae640cfbd1cf533b036597b045043483240429 100644 (file)
@@ -192,14 +192,14 @@ static struct phy *spear1310_miphy_xlate(struct device *dev,
 
        if (args->args_count < 1) {
                dev_err(dev, "DT did not pass correct no of args\n");
 
        if (args->args_count < 1) {
                dev_err(dev, "DT did not pass correct no of args\n");
-               return NULL;
+               return ERR_PTR(-ENODEV);
        }
 
        priv->mode = args->args[0];
 
        if (priv->mode != SATA && priv->mode != PCIE) {
                dev_err(dev, "DT did not pass correct phy mode\n");
        }
 
        priv->mode = args->args[0];
 
        if (priv->mode != SATA && priv->mode != PCIE) {
                dev_err(dev, "DT did not pass correct phy mode\n");
-               return NULL;
+               return ERR_PTR(-ENODEV);
        }
 
        return priv->phy;
        }
 
        return priv->phy;
@@ -227,7 +227,7 @@ static int spear1310_miphy_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
                return -EINVAL;
        }
 
-       priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops, NULL);
+       priv->phy = devm_phy_create(dev, NULL, &spear1310_miphy_ops);
        if (IS_ERR(priv->phy)) {
                dev_err(dev, "failed to create SATA PCIe PHY\n");
                return PTR_ERR(priv->phy);
        if (IS_ERR(priv->phy)) {
                dev_err(dev, "failed to create SATA PCIe PHY\n");
                return PTR_ERR(priv->phy);
index 1ecd0945bad31ba2fabd77cf42f16d11a6d19fc0..1a00c2817f34f8a7c5837ef8f701b09192702116 100644 (file)
@@ -229,14 +229,14 @@ static struct phy *spear1340_miphy_xlate(struct device *dev,
 
        if (args->args_count < 1) {
                dev_err(dev, "DT did not pass correct no of args\n");
 
        if (args->args_count < 1) {
                dev_err(dev, "DT did not pass correct no of args\n");
-               return NULL;
+               return ERR_PTR(-ENODEV);
        }
 
        priv->mode = args->args[0];
 
        if (priv->mode != SATA && priv->mode != PCIE) {
                dev_err(dev, "DT did not pass correct phy mode\n");
        }
 
        priv->mode = args->args[0];
 
        if (priv->mode != SATA && priv->mode != PCIE) {
                dev_err(dev, "DT did not pass correct phy mode\n");
-               return NULL;
+               return ERR_PTR(-ENODEV);
        }
 
        return priv->phy;
        }
 
        return priv->phy;
@@ -259,7 +259,7 @@ static int spear1340_miphy_probe(struct platform_device *pdev)
                return PTR_ERR(priv->misc);
        }
 
                return PTR_ERR(priv->misc);
        }
 
-       priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops, NULL);
+       priv->phy = devm_phy_create(dev, NULL, &spear1340_miphy_ops);
        if (IS_ERR(priv->phy)) {
                dev_err(dev, "failed to create SATA PCIe PHY\n");
                return PTR_ERR(priv->phy);
        if (IS_ERR(priv->phy)) {
                dev_err(dev, "failed to create SATA PCIe PHY\n");
                return PTR_ERR(priv->phy);
index 42428d4181ea3b39d8e593b7cf8a6cd41976cb3e..1d5ae5f8ef694cf9ca389dd3a0742bec189a6955 100644 (file)
@@ -22,6 +22,9 @@
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
 
 #include <linux/mfd/syscon.h>
 #include <linux/phy/phy.h>
 
+#define PHYPARAM_REG   1
+#define PHYCTRL_REG    2
+
 /* Default PHY_SEL and REFCLKSEL configuration */
 #define STIH407_USB_PICOPHY_CTRL_PORT_CONF     0x6
 #define STIH407_USB_PICOPHY_CTRL_PORT_MASK     0x1f
 /* Default PHY_SEL and REFCLKSEL configuration */
 #define STIH407_USB_PICOPHY_CTRL_PORT_CONF     0x6
 #define STIH407_USB_PICOPHY_CTRL_PORT_MASK     0x1f
@@ -93,7 +96,7 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev)
        struct device_node *np = dev->of_node;
        struct phy_provider *phy_provider;
        struct phy *phy;
        struct device_node *np = dev->of_node;
        struct phy_provider *phy_provider;
        struct phy *phy;
-       struct resource *res;
+       int ret;
 
        phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
        if (!phy_dev)
 
        phy_dev = devm_kzalloc(dev, sizeof(*phy_dev), GFP_KERNEL);
        if (!phy_dev)
@@ -123,21 +126,21 @@ static int stih407_usb2_picophy_probe(struct platform_device *pdev)
                return PTR_ERR(phy_dev->regmap);
        }
 
                return PTR_ERR(phy_dev->regmap);
        }
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ctrl");
-       if (!res) {
-               dev_err(dev, "No ctrl reg found\n");
-               return -ENXIO;
+       ret = of_property_read_u32_index(np, "st,syscfg", PHYPARAM_REG,
+                                       &phy_dev->param);
+       if (ret) {
+               dev_err(dev, "can't get phyparam offset (%d)\n", ret);
+               return ret;
        }
        }
-       phy_dev->ctrl = res->start;
 
 
-       res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "param");
-       if (!res) {
-               dev_err(dev, "No param reg found\n");
-               return -ENXIO;
+       ret = of_property_read_u32_index(np, "st,syscfg", PHYCTRL_REG,
+                                       &phy_dev->ctrl);
+       if (ret) {
+               dev_err(dev, "can't get phyctrl offset (%d)\n", ret);
+               return ret;
        }
        }
-       phy_dev->param = res->start;
 
 
-       phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data, NULL);
+       phy = devm_phy_create(dev, NULL, &stih407_usb2_picophy_data);
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create Display Port PHY\n");
                return PTR_ERR(phy);
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create Display Port PHY\n");
                return PTR_ERR(phy);
index 9f16cb8e01f47e0e4af7dbb0053f8b34d9421c38..c093b472b57dadee94b832a989f5495a07dc98f2 100644 (file)
@@ -87,8 +87,12 @@ static int stih41x_usb_phy_power_on(struct phy *phy)
                return ret;
        }
 
                return ret;
        }
 
-       return regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
-                       phy_dev->cfg->oscok, phy_dev->cfg->oscok);
+       ret = regmap_update_bits(phy_dev->regmap, phy_dev->cfg->syscfg,
+                                phy_dev->cfg->oscok, phy_dev->cfg->oscok);
+       if (ret)
+               clk_disable_unprepare(phy_dev->clk);
+
+       return ret;
 }
 
 static int stih41x_usb_phy_power_off(struct phy *phy)
 }
 
 static int stih41x_usb_phy_power_off(struct phy *phy)
@@ -148,7 +152,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev)
                return PTR_ERR(phy_dev->clk);
        }
 
                return PTR_ERR(phy_dev->clk);
        }
 
-       phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops, NULL);
+       phy = devm_phy_create(dev, NULL, &stih41x_usb_phy_ops);
 
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create phy\n");
 
        if (IS_ERR(phy)) {
                dev_err(dev, "failed to create phy\n");
@@ -160,10 +164,7 @@ static int stih41x_usb_phy_probe(struct platform_device *pdev)
        phy_set_drvdata(phy, phy_dev);
 
        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
        phy_set_drvdata(phy, phy_dev);
 
        phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
-       if (IS_ERR(phy_provider))
-               return PTR_ERR(phy_provider);
-
-       return 0;
+       return PTR_ERR_OR_ZERO(phy_provider);
 }
 
 static const struct of_device_id stih41x_usb_phy_of_match[] = {
 }
 
 static const struct of_device_id stih41x_usb_phy_of_match[] = {
index 0baf5efc8a40baff827bfb9aaf6e3cd0e8c094b8..a2b08f3ccb031cbf3a484cb0afab5665c0fb3ad7 100644 (file)
@@ -157,6 +157,10 @@ static int sun4i_usb_phy_init(struct phy *_phy)
                return ret;
        }
 
                return ret;
        }
 
+       /* Enable USB 45 Ohm resistor calibration */
+       if (phy->index == 0)
+               sun4i_usb_phy_write(phy, PHY_RES45_CAL_EN, 0x01, 1);
+
        /* Adjust PHY's magnitude and rate */
        sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
 
        /* Adjust PHY's magnitude and rate */
        sun4i_usb_phy_write(phy, PHY_TX_AMPLITUDE_TUNE, 0x14, 5);
 
@@ -213,7 +217,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev,
 {
        struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
 
 {
        struct sun4i_usb_phy_data *data = dev_get_drvdata(dev);
 
-       if (WARN_ON(args->args[0] == 0 || args->args[0] >= data->num_phys))
+       if (args->args[0] >= data->num_phys)
                return ERR_PTR(-ENODEV);
 
        return data->phys[args->args[0]].phy;
                return ERR_PTR(-ENODEV);
 
        return data->phys[args->args[0]].phy;
@@ -240,7 +244,8 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
        else
                data->num_phys = 3;
 
        else
                data->num_phys = 3;
 
-       if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy"))
+       if (of_device_is_compatible(np, "allwinner,sun4i-a10-usb-phy") ||
+           of_device_is_compatible(np, "allwinner,sun6i-a31-usb-phy"))
                data->disc_thresh = 3;
        else
                data->disc_thresh = 2;
                data->disc_thresh = 3;
        else
                data->disc_thresh = 2;
@@ -255,8 +260,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
        if (IS_ERR(data->base))
                return PTR_ERR(data->base);
 
        if (IS_ERR(data->base))
                return PTR_ERR(data->base);
 
-       /* Skip 0, 0 is the phy for otg which is not yet supported. */
-       for (i = 1; i < data->num_phys; i++) {
+       for (i = 0; i < data->num_phys; i++) {
                struct sun4i_usb_phy *phy = data->phys + i;
                char name[16];
 
                struct sun4i_usb_phy *phy = data->phys + i;
                char name[16];
 
@@ -295,7 +299,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev)
                                return PTR_ERR(phy->pmu);
                }
 
                                return PTR_ERR(phy->pmu);
                }
 
-               phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops, NULL);
+               phy->phy = devm_phy_create(dev, NULL, &sun4i_usb_phy_ops);
                if (IS_ERR(phy->phy)) {
                        dev_err(dev, "failed to create PHY %d\n", i);
                        return PTR_ERR(phy->phy);
                if (IS_ERR(phy->phy)) {
                        dev_err(dev, "failed to create PHY %d\n", i);
                        return PTR_ERR(phy->phy);
diff --git a/drivers/phy/phy-sun9i-usb.c b/drivers/phy/phy-sun9i-usb.c
new file mode 100644 (file)
index 0000000..0095914
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * Allwinner sun9i USB phy driver
+ *
+ * Copyright (C) 2014-2015 Chen-Yu Tsai <wens@csie.org>
+ *
+ * Based on phy-sun4i-usb.c from
+ * Hans de Goede <hdegoede@redhat.com>
+ *
+ * and code from
+ * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/phy/phy.h>
+#include <linux/usb/of.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+
+#define SUNXI_AHB_INCR16_BURST_EN      BIT(11)
+#define SUNXI_AHB_INCR8_BURST_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)
+
+/* usb1 HSIC specific bits */
+#define SUNXI_EHCI_HS_FORCE            BIT(20)
+#define SUNXI_HSIC_CONNECT_DET         BIT(17)
+#define SUNXI_HSIC_CONNECT_INT         BIT(16)
+#define SUNXI_HSIC                     BIT(1)
+
+struct sun9i_usb_phy {
+       struct phy *phy;
+       void __iomem *pmu;
+       struct reset_control *reset;
+       struct clk *clk;
+       struct clk *hsic_clk;
+       enum usb_phy_interface type;
+};
+
+static void sun9i_usb_phy_passby(struct sun9i_usb_phy *phy, int enable)
+{
+       u32 bits, reg_value;
+
+       bits = SUNXI_AHB_INCR16_BURST_EN | SUNXI_AHB_INCR8_BURST_EN |
+               SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN |
+               SUNXI_ULPI_BYPASS_EN;
+
+       if (phy->type == USBPHY_INTERFACE_MODE_HSIC)
+               bits |= SUNXI_HSIC | SUNXI_EHCI_HS_FORCE |
+                       SUNXI_HSIC_CONNECT_DET | SUNXI_HSIC_CONNECT_INT;
+
+       reg_value = readl(phy->pmu);
+
+       if (enable)
+               reg_value |= bits;
+       else
+               reg_value &= ~bits;
+
+       writel(reg_value, phy->pmu);
+}
+
+static int sun9i_usb_phy_init(struct phy *_phy)
+{
+       struct sun9i_usb_phy *phy = phy_get_drvdata(_phy);
+       int ret;
+
+       ret = clk_prepare_enable(phy->clk);
+       if (ret)
+               goto err_clk;
+
+       ret = clk_prepare_enable(phy->hsic_clk);
+       if (ret)
+               goto err_hsic_clk;
+
+       ret = reset_control_deassert(phy->reset);
+       if (ret)
+               goto err_reset;
+
+       sun9i_usb_phy_passby(phy, 1);
+       return 0;
+
+err_reset:
+       clk_disable_unprepare(phy->hsic_clk);
+
+err_hsic_clk:
+       clk_disable_unprepare(phy->clk);
+
+err_clk:
+       return ret;
+}
+
+static int sun9i_usb_phy_exit(struct phy *_phy)
+{
+       struct sun9i_usb_phy *phy = phy_get_drvdata(_phy);
+
+       sun9i_usb_phy_passby(phy, 0);
+       reset_control_assert(phy->reset);
+       clk_disable_unprepare(phy->hsic_clk);
+       clk_disable_unprepare(phy->clk);
+
+       return 0;
+}
+
+static struct phy_ops sun9i_usb_phy_ops = {
+       .init           = sun9i_usb_phy_init,
+       .exit           = sun9i_usb_phy_exit,
+       .owner          = THIS_MODULE,
+};
+
+static int sun9i_usb_phy_probe(struct platform_device *pdev)
+{
+       struct sun9i_usb_phy *phy;
+       struct device *dev = &pdev->dev;
+       struct device_node *np = dev->of_node;
+       struct phy_provider *phy_provider;
+       struct resource *res;
+
+       phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
+       if (!phy)
+               return -ENOMEM;
+
+       phy->type = of_usb_get_phy_mode(np);
+       if (phy->type == USBPHY_INTERFACE_MODE_HSIC) {
+               phy->clk = devm_clk_get(dev, "hsic_480M");
+               if (IS_ERR(phy->clk)) {
+                       dev_err(dev, "failed to get hsic_480M clock\n");
+                       return PTR_ERR(phy->clk);
+               }
+
+               phy->hsic_clk = devm_clk_get(dev, "hsic_12M");
+               if (IS_ERR(phy->clk)) {
+                       dev_err(dev, "failed to get hsic_12M clock\n");
+                       return PTR_ERR(phy->clk);
+               }
+
+               phy->reset = devm_reset_control_get(dev, "hsic");
+               if (IS_ERR(phy->reset)) {
+                       dev_err(dev, "failed to get reset control\n");
+                       return PTR_ERR(phy->reset);
+               }
+       } else {
+               phy->clk = devm_clk_get(dev, "phy");
+               if (IS_ERR(phy->clk)) {
+                       dev_err(dev, "failed to get phy clock\n");
+                       return PTR_ERR(phy->clk);
+               }
+
+               phy->reset = devm_reset_control_get(dev, "phy");
+               if (IS_ERR(phy->reset)) {
+                       dev_err(dev, "failed to get reset control\n");
+                       return PTR_ERR(phy->reset);
+               }
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       phy->pmu = devm_ioremap_resource(dev, res);
+       if (IS_ERR(phy->pmu))
+               return PTR_ERR(phy->pmu);
+
+       phy->phy = devm_phy_create(dev, NULL, &sun9i_usb_phy_ops);
+       if (IS_ERR(phy->phy)) {
+               dev_err(dev, "failed to create PHY\n");
+               return PTR_ERR(phy->phy);
+       }
+
+       phy_set_drvdata(phy->phy, phy);
+       phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+
+       return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id sun9i_usb_phy_of_match[] = {
+       { .compatible = "allwinner,sun9i-a80-usb-phy" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, sun9i_usb_phy_of_match);
+
+static struct platform_driver sun9i_usb_phy_driver = {
+       .probe  = sun9i_usb_phy_probe,
+       .driver = {
+               .of_match_table = sun9i_usb_phy_of_match,
+               .name  = "sun9i-usb-phy",
+       }
+};
+module_platform_driver(sun9i_usb_phy_driver);
+
+MODULE_DESCRIPTION("Allwinner sun9i USB phy driver");
+MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
+MODULE_LICENSE("GPL");
index ab1e22d9a1e8ac4ef786d184d925cd6b25c1ea8c..ad3fbc80e044e68e6b907d0837f6d1d7460be7b9 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/delay.h>
 #include <linux/phy/omap_control_phy.h>
 #include <linux/of_platform.h>
 #include <linux/delay.h>
 #include <linux/phy/omap_control_phy.h>
 #include <linux/of_platform.h>
+#include <linux/spinlock.h>
 
 #define        PLL_STATUS              0x00000004
 #define        PLL_GO                  0x00000008
 
 #define        PLL_STATUS              0x00000004
 #define        PLL_GO                  0x00000008
@@ -82,7 +83,10 @@ struct ti_pipe3 {
        struct clk              *refclk;
        struct clk              *div_clk;
        struct pipe3_dpll_map   *dpll_map;
        struct clk              *refclk;
        struct clk              *div_clk;
        struct pipe3_dpll_map   *dpll_map;
-       u8                      id;
+       bool                    enabled;
+       spinlock_t              lock;   /* serialize clock enable/disable */
+       /* the below flag is needed specifically for SATA */
+       bool                    refclk_enabled;
 };
 
 static struct pipe3_dpll_map dpll_map_usb[] = {
 };
 
 static struct pipe3_dpll_map dpll_map_usb[] = {
@@ -217,8 +221,13 @@ static int ti_pipe3_init(struct phy *x)
        u32 val;
        int ret = 0;
 
        u32 val;
        int ret = 0;
 
+       /*
+        * Set pcie_pcs register to 0x96 for proper functioning of phy
+        * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table
+        * 18-1804.
+        */
        if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
        if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
-               omap_control_pcie_pcs(phy->control_dev, phy->id, 0xF1);
+               omap_control_pcie_pcs(phy->control_dev, 0x96);
                return 0;
        }
 
                return 0;
        }
 
@@ -282,9 +291,7 @@ static struct phy_ops ops = {
        .owner          = THIS_MODULE,
 };
 
        .owner          = THIS_MODULE,
 };
 
-#ifdef CONFIG_OF
 static const struct of_device_id ti_pipe3_id_table[];
 static const struct of_device_id ti_pipe3_id_table[];
-#endif
 
 static int ti_pipe3_probe(struct platform_device *pdev)
 {
 
 static int ti_pipe3_probe(struct platform_device *pdev)
 {
@@ -303,10 +310,10 @@ static int ti_pipe3_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        phy->dev                = &pdev->dev;
                return -ENOMEM;
 
        phy->dev                = &pdev->dev;
+       spin_lock_init(&phy->lock);
 
        if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
 
        if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
-               match = of_match_device(of_match_ptr(ti_pipe3_id_table),
-                                       &pdev->dev);
+               match = of_match_device(ti_pipe3_id_table, &pdev->dev);
                if (!match)
                        return -EINVAL;
 
                if (!match)
                        return -EINVAL;
 
@@ -329,26 +336,27 @@ static int ti_pipe3_probe(struct platform_device *pdev)
                }
        }
 
                }
        }
 
+       phy->refclk = devm_clk_get(phy->dev, "refclk");
+       if (IS_ERR(phy->refclk)) {
+               dev_err(&pdev->dev, "unable to get refclk\n");
+               /* older DTBs have missing refclk in SATA PHY
+                * so don't bail out in case of SATA PHY.
+                */
+               if (!of_device_is_compatible(node, "ti,phy-pipe3-sata"))
+                       return PTR_ERR(phy->refclk);
+       }
+
        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);
                }
        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);
        } else {
                phy->wkupclk = ERR_PTR(-ENODEV);
-               phy->refclk = ERR_PTR(-ENODEV);
        }
 
        if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
        }
 
        if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
-               if (of_property_read_u8(node, "id", &phy->id) < 0)
-                       phy->id = 1;
 
                clk = devm_clk_get(phy->dev, "dpll_ref");
                if (IS_ERR(clk)) {
 
                clk = devm_clk_get(phy->dev, "dpll_ref");
                if (IS_ERR(clk)) {
@@ -399,7 +407,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, phy);
        pm_runtime_enable(phy->dev);
 
        platform_set_drvdata(pdev, phy);
        pm_runtime_enable(phy->dev);
 
-       generic_phy = devm_phy_create(phy->dev, NULL, &ops, NULL);
+       generic_phy = devm_phy_create(phy->dev, NULL, &ops);
        if (IS_ERR(generic_phy))
                return PTR_ERR(generic_phy);
 
        if (IS_ERR(generic_phy))
                return PTR_ERR(generic_phy);
 
@@ -423,34 +431,43 @@ static int ti_pipe3_remove(struct platform_device *pdev)
        return 0;
 }
 
        return 0;
 }
 
-#ifdef CONFIG_PM_RUNTIME
-
-static int ti_pipe3_runtime_suspend(struct device *dev)
+#ifdef CONFIG_PM
+static int ti_pipe3_enable_refclk(struct ti_pipe3 *phy)
 {
 {
-       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+       if (!IS_ERR(phy->refclk) && !phy->refclk_enabled) {
+               int ret;
 
 
-       if (!IS_ERR(phy->wkupclk))
-               clk_disable_unprepare(phy->wkupclk);
+               ret = clk_prepare_enable(phy->refclk);
+               if (ret) {
+                       dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
+                       return ret;
+               }
+               phy->refclk_enabled = true;
+       }
+
+       return 0;
+}
+
+static void ti_pipe3_disable_refclk(struct ti_pipe3 *phy)
+{
        if (!IS_ERR(phy->refclk))
                clk_disable_unprepare(phy->refclk);
        if (!IS_ERR(phy->refclk))
                clk_disable_unprepare(phy->refclk);
-       if (!IS_ERR(phy->div_clk))
-               clk_disable_unprepare(phy->div_clk);
 
 
-       return 0;
+       phy->refclk_enabled = false;
 }
 
 }
 
-static int ti_pipe3_runtime_resume(struct device *dev)
+static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy)
 {
 {
-       u32 ret = 0;
-       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+       int ret = 0;
+       unsigned long flags;
 
 
-       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;
-               }
-       }
+       spin_lock_irqsave(&phy->lock, flags);
+       if (phy->enabled)
+               goto err1;
+
+       ret = ti_pipe3_enable_refclk(phy);
+       if (ret)
+               goto err1;
 
        if (!IS_ERR(phy->wkupclk)) {
                ret = clk_prepare_enable(phy->wkupclk);
 
        if (!IS_ERR(phy->wkupclk)) {
                ret = clk_prepare_enable(phy->wkupclk);
@@ -467,6 +484,9 @@ static int ti_pipe3_runtime_resume(struct device *dev)
                        goto err3;
                }
        }
                        goto err3;
                }
        }
+
+       phy->enabled = true;
+       spin_unlock_irqrestore(&phy->lock, flags);
        return 0;
 
 err3:
        return 0;
 
 err3:
@@ -477,21 +497,80 @@ err2:
        if (!IS_ERR(phy->refclk))
                clk_disable_unprepare(phy->refclk);
 
        if (!IS_ERR(phy->refclk))
                clk_disable_unprepare(phy->refclk);
 
+       ti_pipe3_disable_refclk(phy);
 err1:
 err1:
+       spin_unlock_irqrestore(&phy->lock, flags);
+       return ret;
+}
+
+static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&phy->lock, flags);
+       if (!phy->enabled) {
+               spin_unlock_irqrestore(&phy->lock, flags);
+               return;
+       }
+
+       if (!IS_ERR(phy->wkupclk))
+               clk_disable_unprepare(phy->wkupclk);
+       /* Don't disable refclk for SATA PHY due to Errata i783 */
+       if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
+               ti_pipe3_disable_refclk(phy);
+       if (!IS_ERR(phy->div_clk))
+               clk_disable_unprepare(phy->div_clk);
+       phy->enabled = false;
+       spin_unlock_irqrestore(&phy->lock, flags);
+}
+
+static int ti_pipe3_runtime_suspend(struct device *dev)
+{
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+       ti_pipe3_disable_clocks(phy);
+       return 0;
+}
+
+static int ti_pipe3_runtime_resume(struct device *dev)
+{
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+       int ret = 0;
+
+       ret = ti_pipe3_enable_clocks(phy);
        return ret;
 }
 
        return ret;
 }
 
+static int ti_pipe3_suspend(struct device *dev)
+{
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+
+       ti_pipe3_disable_clocks(phy);
+       return 0;
+}
+
+static int ti_pipe3_resume(struct device *dev)
+{
+       struct ti_pipe3 *phy = dev_get_drvdata(dev);
+       int ret;
+
+       ret = ti_pipe3_enable_clocks(phy);
+       if (ret)
+               return ret;
+
+       pm_runtime_disable(dev);
+       pm_runtime_set_active(dev);
+       pm_runtime_enable(dev);
+       return 0;
+}
+#endif
+
 static const struct dev_pm_ops ti_pipe3_pm_ops = {
        SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
                           ti_pipe3_runtime_resume, NULL)
 static const struct dev_pm_ops ti_pipe3_pm_ops = {
        SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
                           ti_pipe3_runtime_resume, NULL)
+       SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume)
 };
 
 };
 
-#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",
 static const struct of_device_id ti_pipe3_id_table[] = {
        {
                .compatible = "ti,phy-usb3",
@@ -511,15 +590,14 @@ static const struct of_device_id ti_pipe3_id_table[] = {
        {}
 };
 MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
        {}
 };
 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",
 
 static struct platform_driver ti_pipe3_driver = {
        .probe          = ti_pipe3_probe,
        .remove         = ti_pipe3_remove,
        .driver         = {
                .name   = "ti-pipe3",
-               .pm     = DEV_PM_OPS,
-               .of_match_table = of_match_ptr(ti_pipe3_id_table),
+               .pm     = &ti_pipe3_pm_ops,
+               .of_match_table = ti_pipe3_id_table,
        },
 };
 
        },
 };
 
index e2698d29f436848ef7b50a009af0df2a242060b5..8e87f54671f32de11915a7921824fc4a8929ab84 100644 (file)
@@ -644,7 +644,6 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        struct usb_otg          *otg;
        struct device_node      *np = pdev->dev.of_node;
        struct phy_provider     *phy_provider;
        struct usb_otg          *otg;
        struct device_node      *np = pdev->dev.of_node;
        struct phy_provider     *phy_provider;
-       struct phy_init_data    *init_data = NULL;
 
        twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
        if (!twl)
 
        twl = devm_kzalloc(&pdev->dev, sizeof(*twl), GFP_KERNEL);
        if (!twl)
@@ -655,7 +654,6 @@ static int twl4030_usb_probe(struct platform_device *pdev)
                                (enum twl4030_usb_mode *)&twl->usb_mode);
        else if (pdata) {
                twl->usb_mode = pdata->usb_mode;
                                (enum twl4030_usb_mode *)&twl->usb_mode);
        else if (pdata) {
                twl->usb_mode = pdata->usb_mode;
-               init_data = pdata->init_data;
        } else {
                dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
                return -EINVAL;
        } else {
                dev_err(&pdev->dev, "twl4030 initialized without pdata\n");
                return -EINVAL;
@@ -680,7 +678,7 @@ static int twl4030_usb_probe(struct platform_device *pdev)
        otg->set_host           = twl4030_set_host;
        otg->set_peripheral     = twl4030_set_peripheral;
 
        otg->set_host           = twl4030_set_host;
        otg->set_peripheral     = twl4030_set_peripheral;
 
-       phy = devm_phy_create(twl->dev, NULL, &ops, init_data);
+       phy = devm_phy_create(twl->dev, NULL, &ops);
        if (IS_ERR(phy)) {
                dev_dbg(&pdev->dev, "Failed to create PHY\n");
                return PTR_ERR(phy);
        if (IS_ERR(phy)) {
                dev_dbg(&pdev->dev, "Failed to create PHY\n");
                return PTR_ERR(phy);
@@ -733,6 +731,11 @@ static int twl4030_usb_probe(struct platform_device *pdev)
                return status;
        }
 
                return status;
        }
 
+       if (pdata)
+               err = phy_create_lookup(phy, "usb", "musb-hdrc.0");
+       if (err)
+               return err;
+
        pm_runtime_mark_last_busy(&pdev->dev);
        pm_runtime_put_autosuspend(twl->dev);
 
        pm_runtime_mark_last_busy(&pdev->dev);
        pm_runtime_put_autosuspend(twl->dev);
 
index f8a51b16ade8efc279be7d72340b42b6c955fdef..bae9cccc08f08390ceccc0177caf8d5d4ff9ff46 100644 (file)
@@ -1657,7 +1657,6 @@ static int xgene_phy_probe(struct platform_device *pdev)
        struct phy_provider *phy_provider;
        struct xgene_phy_ctx *ctx;
        struct resource *res;
        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_spd[] = DEFAULT_SATA_SPD_SEL;
        u32 default_txboost_gain[] = DEFAULT_SATA_TXBOOST_GAIN;
        u32 default_txeye_direction[] = DEFAULT_SATA_TXEYEDIRECTION;
@@ -1676,10 +1675,8 @@ static int xgene_phy_probe(struct platform_device *pdev)
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        ctx->sds_base = devm_ioremap_resource(&pdev->dev, res);
 
        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;
-       }
+       if (IS_ERR(ctx->sds_base))
+               return PTR_ERR(ctx->sds_base);
 
        /* Retrieve optional clock */
        ctx->clk = clk_get(&pdev->dev, NULL);
 
        /* Retrieve optional clock */
        ctx->clk = clk_get(&pdev->dev, NULL);
@@ -1707,25 +1704,15 @@ static int xgene_phy_probe(struct platform_device *pdev)
        ctx->dev = &pdev->dev;
        platform_set_drvdata(pdev, ctx);
 
        ctx->dev = &pdev->dev;
        platform_set_drvdata(pdev, ctx);
 
-       ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops, NULL);
+       ctx->phy = devm_phy_create(ctx->dev, NULL, &xgene_phy_ops);
        if (IS_ERR(ctx->phy)) {
                dev_dbg(&pdev->dev, "Failed to create PHY\n");
        if (IS_ERR(ctx->phy)) {
                dev_dbg(&pdev->dev, "Failed to create PHY\n");
-               rc = PTR_ERR(ctx->phy);
-               goto error;
+               return PTR_ERR(ctx->phy);
        }
        phy_set_drvdata(ctx->phy, ctx);
 
        }
        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;
+       phy_provider = devm_of_phy_provider_register(ctx->dev, xgene_phy_xlate);
+       return PTR_ERR_OR_ZERO(phy_provider);
 }
 
 static const struct of_device_id xgene_phy_of_match[] = {
 }
 
 static const struct of_device_id xgene_phy_of_match[] = {
index e9e6cfbfbb589d0393060e2fed0422ec402dd612..eb7d4a135a9ea71364105c0bade762b5f06b67da 100644 (file)
@@ -66,7 +66,7 @@ enum omap_control_usb_mode {
 #define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
 
 #define        OMAP_CTRL_PCIE_PCS_MASK                 0xff
 #define        OMAP_CTRL_PIPE3_PHY_TX_RX_POWEROFF      0x0
 
 #define        OMAP_CTRL_PCIE_PCS_MASK                 0xff
-#define        OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT    0x8
+#define        OMAP_CTRL_PCIE_PCS_DELAY_COUNT_SHIFT    16
 
 #define OMAP_CTRL_USB2_PHY_PD          BIT(28)
 
 
 #define OMAP_CTRL_USB2_PHY_PD          BIT(28)
 
@@ -79,7 +79,7 @@ enum omap_control_usb_mode {
 void omap_control_phy_power(struct device *dev, int on);
 void omap_control_usb_set_mode(struct device *dev,
                               enum omap_control_usb_mode mode);
 void omap_control_phy_power(struct device *dev, int on);
 void omap_control_usb_set_mode(struct device *dev,
                               enum omap_control_usb_mode mode);
-void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay);
+void omap_control_pcie_pcs(struct device *dev, u8 delay);
 #else
 
 static inline void omap_control_phy_power(struct device *dev, int on)
 #else
 
 static inline void omap_control_phy_power(struct device *dev, int on)
@@ -91,7 +91,7 @@ static inline void omap_control_usb_set_mode(struct device *dev,
 {
 }
 
 {
 }
 
-static inline void omap_control_pcie_pcs(struct device *dev, u8 id, u8 delay)
+static inline void omap_control_pcie_pcs(struct device *dev, u8 delay)
 {
 }
 #endif
 {
 }
 #endif
diff --git a/include/linux/phy/phy-qcom-ufs.h b/include/linux/phy/phy-qcom-ufs.h
new file mode 100644 (file)
index 0000000..9d18e9f
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2013-2015, Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifndef PHY_QCOM_UFS_H_
+#define PHY_QCOM_UFS_H_
+
+#include "phy.h"
+
+/**
+ * ufs_qcom_phy_enable_ref_clk() - Enable the phy
+ * ref clock.
+ * @phy: reference to a generic phy
+ *
+ * returns 0 for success, and non-zero for error.
+ */
+int ufs_qcom_phy_enable_ref_clk(struct phy *phy);
+
+/**
+ * ufs_qcom_phy_disable_ref_clk() - Disable the phy
+ * ref clock.
+ * @phy: reference to a generic phy.
+ */
+void ufs_qcom_phy_disable_ref_clk(struct phy *phy);
+
+/**
+ * ufs_qcom_phy_enable_dev_ref_clk() - Enable the device
+ * ref clock.
+ * @phy: reference to a generic phy.
+ */
+void ufs_qcom_phy_enable_dev_ref_clk(struct phy *phy);
+
+/**
+ * ufs_qcom_phy_disable_dev_ref_clk() - Disable the device
+ * ref clock.
+ * @phy: reference to a generic phy.
+ */
+void ufs_qcom_phy_disable_dev_ref_clk(struct phy *phy);
+
+int ufs_qcom_phy_enable_iface_clk(struct phy *phy);
+void ufs_qcom_phy_disable_iface_clk(struct phy *phy);
+int ufs_qcom_phy_start_serdes(struct phy *phy);
+int ufs_qcom_phy_set_tx_lane_enable(struct phy *phy, u32 tx_lanes);
+int ufs_qcom_phy_calibrate_phy(struct phy *phy, bool is_rate_B);
+int ufs_qcom_phy_is_pcs_ready(struct phy *phy);
+void ufs_qcom_phy_save_controller_version(struct phy *phy,
+                       u8 major, u16 minor, u16 step);
+
+#endif /* PHY_QCOM_UFS_H_ */
index 8cb6f815475b0f5a237e6305986bc5e60ab08b8c..a0197fa1b1163b928c5562c7eb9fdcb5d1c68e61 100644 (file)
@@ -61,7 +61,6 @@ struct phy {
        struct device           dev;
        int                     id;
        const struct phy_ops    *ops;
        struct device           dev;
        int                     id;
        const struct phy_ops    *ops;
-       struct phy_init_data    *init_data;
        struct mutex            mutex;
        int                     init_count;
        int                     power_count;
        struct mutex            mutex;
        int                     init_count;
        int                     power_count;
@@ -84,33 +83,14 @@ struct phy_provider {
                struct of_phandle_args *args);
 };
 
                struct of_phandle_args *args);
 };
 
-/**
- * struct phy_consumer - represents the phy consumer
- * @dev_name: the device name of the controller that will use this PHY device
- * @port: name given to the consumer port
- */
-struct phy_consumer {
-       const char *dev_name;
-       const char *port;
-};
-
-/**
- * struct phy_init_data - contains the list of PHY consumers
- * @num_consumers: number of consumers for this PHY device
- * @consumers: list of PHY consumers
- */
-struct phy_init_data {
-       unsigned int num_consumers;
-       struct phy_consumer *consumers;
+struct phy_lookup {
+       struct list_head node;
+       const char *dev_id;
+       const char *con_id;
+       struct phy *phy;
 };
 
 };
 
-#define PHY_CONSUMER(_dev_name, _port)                         \
-{                                                              \
-       .dev_name       = _dev_name,                            \
-       .port           = _port,                                \
-}
-
-#define        to_phy(dev)     (container_of((dev), struct phy, dev))
+#define        to_phy(a)       (container_of((a), struct phy, dev))
 
 #define        of_phy_provider_register(dev, xlate)    \
        __of_phy_provider_register((dev), THIS_MODULE, (xlate))
 
 #define        of_phy_provider_register(dev, xlate)    \
        __of_phy_provider_register((dev), THIS_MODULE, (xlate))
@@ -159,10 +139,9 @@ struct phy *of_phy_get(struct device_node *np, const char *con_id);
 struct phy *of_phy_simple_xlate(struct device *dev,
        struct of_phandle_args *args);
 struct phy *phy_create(struct device *dev, struct device_node *node,
 struct phy *of_phy_simple_xlate(struct device *dev,
        struct of_phandle_args *args);
 struct phy *phy_create(struct device *dev, struct device_node *node,
-                      const struct phy_ops *ops,
-                      struct phy_init_data *init_data);
+                      const struct phy_ops *ops);
 struct phy *devm_phy_create(struct device *dev, struct device_node *node,
 struct phy *devm_phy_create(struct device *dev, struct device_node *node,
-       const struct phy_ops *ops, struct phy_init_data *init_data);
+                           const struct phy_ops *ops);
 void phy_destroy(struct phy *phy);
 void devm_phy_destroy(struct device *dev, struct phy *phy);
 struct phy_provider *__of_phy_provider_register(struct device *dev,
 void phy_destroy(struct phy *phy);
 void devm_phy_destroy(struct device *dev, struct phy *phy);
 struct phy_provider *__of_phy_provider_register(struct device *dev,
@@ -174,6 +153,8 @@ struct phy_provider *__devm_of_phy_provider_register(struct device *dev,
 void of_phy_provider_unregister(struct phy_provider *phy_provider);
 void devm_of_phy_provider_unregister(struct device *dev,
        struct phy_provider *phy_provider);
 void of_phy_provider_unregister(struct phy_provider *phy_provider);
 void devm_of_phy_provider_unregister(struct device *dev,
        struct phy_provider *phy_provider);
+int phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id);
+void phy_remove_lookup(struct phy *phy, const char *con_id, const char *dev_id);
 #else
 static inline int phy_pm_runtime_get(struct phy *phy)
 {
 #else
 static inline int phy_pm_runtime_get(struct phy *phy)
 {
@@ -301,16 +282,14 @@ static inline struct phy *of_phy_simple_xlate(struct device *dev,
 
 static inline struct phy *phy_create(struct device *dev,
                                     struct device_node *node,
 
 static inline struct phy *phy_create(struct device *dev,
                                     struct device_node *node,
-                                    const struct phy_ops *ops,
-                                    struct phy_init_data *init_data)
+                                    const struct phy_ops *ops)
 {
        return ERR_PTR(-ENOSYS);
 }
 
 static inline struct phy *devm_phy_create(struct device *dev,
                                          struct device_node *node,
 {
        return ERR_PTR(-ENOSYS);
 }
 
 static inline struct phy *devm_phy_create(struct device *dev,
                                          struct device_node *node,
-                                         const struct phy_ops *ops,
-                                         struct phy_init_data *init_data)
+                                         const struct phy_ops *ops)
 {
        return ERR_PTR(-ENOSYS);
 }
 {
        return ERR_PTR(-ENOSYS);
 }
@@ -345,6 +324,13 @@ static inline void devm_of_phy_provider_unregister(struct device *dev,
        struct phy_provider *phy_provider)
 {
 }
        struct phy_provider *phy_provider)
 {
 }
+static inline int
+phy_create_lookup(struct phy *phy, const char *con_id, const char *dev_id)
+{
+       return 0;
+}
+static inline void phy_remove_lookup(struct phy *phy, const char *con_id,
+                                    const char *dev_id) { }
 #endif
 
 #endif /* __DRIVERS_PHY_H */
 #endif
 
 #endif /* __DRIVERS_PHY_H */