These changes are the raw update to linux-4.4.6-rt14. Kernel sources
[kvmfornfv.git] / kernel / drivers / phy / phy-ti-pipe3.c
index 53f295c..93bc112 100644 (file)
@@ -28,7 +28,8 @@
 #include <linux/delay.h>
 #include <linux/phy/omap_control_phy.h>
 #include <linux/of_platform.h>
-#include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
 
 #define        PLL_STATUS              0x00000004
 #define        PLL_GO                  0x00000008
@@ -53,6 +54,8 @@
 #define        PLL_LOCK                0x2
 #define        PLL_IDLE                0x1
 
+#define SATA_PLL_SOFT_RESET    BIT(18)
+
 /*
  * This is an Empirical value that works, need to confirm the actual
  * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
@@ -83,10 +86,9 @@ struct ti_pipe3 {
        struct clk              *refclk;
        struct clk              *div_clk;
        struct pipe3_dpll_map   *dpll_map;
-       bool                    enabled;
-       spinlock_t              lock;   /* serialize clock enable/disable */
-       /* the below flag is needed specifically for SATA */
-       bool                    refclk_enabled;
+       struct regmap           *dpll_reset_syscon; /* ctrl. reg. acces */
+       unsigned int            dpll_reset_reg; /* reg. index within syscon */
+       bool                    sata_refclk_enabled;
 };
 
 static struct pipe3_dpll_map dpll_map_usb[] = {
@@ -137,6 +139,9 @@ static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(struct ti_pipe3 *phy)
        return NULL;
 }
 
+static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy);
+static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy);
+
 static int ti_pipe3_power_off(struct phy *x)
 {
        struct ti_pipe3 *phy = phy_get_drvdata(x);
@@ -217,6 +222,7 @@ static int ti_pipe3_init(struct phy *x)
        u32 val;
        int ret = 0;
 
+       ti_pipe3_enable_clocks(phy);
        /*
         * Set pcie_pcs register to 0x96 for proper functioning of phy
         * as recommended in AM572x TRM SPRUHZ6, section 18.5.2.2, table
@@ -250,36 +256,49 @@ static int ti_pipe3_exit(struct phy *x)
        u32 val;
        unsigned long timeout;
 
-       /* SATA DPLL can't be powered down due to Errata i783 and PCIe
-        * does not have internal DPLL
+       /* If dpll_reset_syscon is not present we wont power down SATA DPLL
+        * due to Errata i783
         */
-       if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") ||
-           of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie"))
+       if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata") &&
+           !phy->dpll_reset_syscon)
                return 0;
 
-       /* Put DPLL in IDLE mode */
-       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
-       val |= PLL_IDLE;
-       ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
+       /* PCIe doesn't have internal DPLL */
+       if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-pcie")) {
+               /* Put DPLL in IDLE mode */
+               val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
+               val |= PLL_IDLE;
+               ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
 
-       /* wait for LDO and Oscillator to power down */
-       timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME);
-       do {
-               cpu_relax();
-               val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
-               if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
-                       break;
-       } while (!time_after(jiffies, timeout));
+               /* wait for LDO and Oscillator to power down */
+               timeout = jiffies + msecs_to_jiffies(PLL_IDLE_TIME);
+               do {
+                       cpu_relax();
+                       val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
+                       if ((val & PLL_TICOPWDN) && (val & PLL_LDOPWDN))
+                               break;
+               } while (!time_after(jiffies, timeout));
+
+               if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
+                       dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n",
+                               val);
+                       return -EBUSY;
+               }
+       }
 
-       if (!(val & PLL_TICOPWDN) || !(val & PLL_LDOPWDN)) {
-               dev_err(phy->dev, "Failed to power down: PLL_STATUS 0x%x\n",
-                       val);
-               return -EBUSY;
+       /* i783: SATA needs control bit toggle after PLL unlock */
+       if (of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata")) {
+               regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg,
+                                  SATA_PLL_SOFT_RESET, SATA_PLL_SOFT_RESET);
+               regmap_update_bits(phy->dpll_reset_syscon, phy->dpll_reset_reg,
+                                  SATA_PLL_SOFT_RESET, 0);
        }
 
+       ti_pipe3_disable_clocks(phy);
+
        return 0;
 }
-static struct phy_ops ops = {
+static const struct phy_ops ops = {
        .init           = ti_pipe3_init,
        .exit           = ti_pipe3_exit,
        .power_on       = ti_pipe3_power_on,
@@ -306,7 +325,6 @@ static int ti_pipe3_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        phy->dev                = &pdev->dev;
-       spin_lock_init(&phy->lock);
 
        if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
                match = of_match_device(ti_pipe3_id_table, &pdev->dev);
@@ -350,6 +368,21 @@ static int ti_pipe3_probe(struct platform_device *pdev)
                }
        } else {
                phy->wkupclk = ERR_PTR(-ENODEV);
+               phy->dpll_reset_syscon = syscon_regmap_lookup_by_phandle(node,
+                                                       "syscon-pllreset");
+               if (IS_ERR(phy->dpll_reset_syscon)) {
+                       dev_info(&pdev->dev,
+                                "can't get syscon-pllreset, sata dpll won't idle\n");
+                       phy->dpll_reset_syscon = NULL;
+               } else {
+                       if (of_property_read_u32_index(node,
+                                                      "syscon-pllreset", 1,
+                                                      &phy->dpll_reset_reg)) {
+                               dev_err(&pdev->dev,
+                                       "couldn't get pllreset reg. offset\n");
+                               return -EINVAL;
+                       }
+               }
        }
 
        if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
@@ -403,6 +436,16 @@ static int ti_pipe3_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, phy);
        pm_runtime_enable(phy->dev);
 
+       /*
+        * Prevent auto-disable of refclk for SATA PHY due to Errata i783
+        */
+       if (of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
+               if (!IS_ERR(phy->refclk)) {
+                       clk_prepare_enable(phy->refclk);
+                       phy->sata_refclk_enabled = true;
+               }
+       }
+
        generic_phy = devm_phy_create(phy->dev, NULL, &ops);
        if (IS_ERR(generic_phy))
                return PTR_ERR(generic_phy);
@@ -413,63 +456,33 @@ static int ti_pipe3_probe(struct platform_device *pdev)
        if (IS_ERR(phy_provider))
                return PTR_ERR(phy_provider);
 
-       pm_runtime_get(&pdev->dev);
-
        return 0;
 }
 
 static int ti_pipe3_remove(struct platform_device *pdev)
 {
-       if (!pm_runtime_suspended(&pdev->dev))
-               pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int ti_pipe3_enable_refclk(struct ti_pipe3 *phy)
+static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy)
 {
-       if (!IS_ERR(phy->refclk) && !phy->refclk_enabled) {
-               int ret;
+       int ret = 0;
 
+       if (!IS_ERR(phy->refclk)) {
                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);
-
-       phy->refclk_enabled = false;
-}
-
-static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy)
-{
-       int ret = 0;
-       unsigned long flags;
-
-       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 (ret) {
                        dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
-                       goto err2;
+                       goto disable_refclk;
                }
        }
 
@@ -477,96 +490,43 @@ static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy)
                ret = clk_prepare_enable(phy->div_clk);
                if (ret) {
                        dev_err(phy->dev, "Failed to enable div_clk %d\n", ret);
-                       goto err3;
+                       goto disable_wkupclk;
                }
        }
 
-       phy->enabled = true;
-       spin_unlock_irqrestore(&phy->lock, flags);
        return 0;
 
-err3:
+disable_wkupclk:
        if (!IS_ERR(phy->wkupclk))
                clk_disable_unprepare(phy->wkupclk);
 
-err2:
+disable_refclk:
        if (!IS_ERR(phy->refclk))
                clk_disable_unprepare(phy->refclk);
 
-       ti_pipe3_disable_refclk(phy);
-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->refclk)) {
+               clk_disable_unprepare(phy->refclk);
+               /*
+                * SATA refclk needs an additional disable as we left it
+                * on in probe to avoid Errata i783
+                */
+               if (phy->sata_refclk_enabled) {
+                       clk_disable_unprepare(phy->refclk);
+                       phy->sata_refclk_enabled = false;
+               }
+       }
+
        if (!IS_ERR(phy->div_clk))
                clk_disable_unprepare(phy->div_clk);
-       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;
-}
-
-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)
-       SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume)
-};
-
 static const struct of_device_id ti_pipe3_id_table[] = {
        {
                .compatible = "ti,phy-usb3",
@@ -592,7 +552,6 @@ static struct platform_driver ti_pipe3_driver = {
        .remove         = ti_pipe3_remove,
        .driver         = {
                .name   = "ti-pipe3",
-               .pm     = &ti_pipe3_pm_ops,
                .of_match_table = ti_pipe3_id_table,
        },
 };