phy: rockchip-dp: Add support for rk3368 Display Port PHY
[firefly-linux-kernel-4.4.55.git] / drivers / phy / phy-rockchip-dp.c
index 77e2d02e6bee228ac033a28cccea90047439ed86..79576e99ab3e421f5408eb5246646c72989084f1 100644 (file)
  */
 
 #include <linux/clk.h>
+#include <linux/delay.h>
 #include <linux/mfd/syscon.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
+#include <linux/reset.h>
 
-#define GRF_SOC_CON12                           0x0274
-
-#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK   BIT(20)
-#define GRF_EDP_REF_CLK_SEL_INTER               BIT(4)
-
-#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK           BIT(21)
-#define GRF_EDP_PHY_SIDDQ_ON                    0
-#define GRF_EDP_PHY_SIDDQ_OFF                   BIT(5)
+struct rockchip_dp_phy_drv_data {
+       u32 grf_reg_offset;
+       u32 ref_clk_sel_inter;
+       u32 siddq_on;
+       u32 siddq_off;
+};
 
 struct rockchip_dp_phy {
        struct device  *dev;
        struct regmap  *grf;
        struct clk     *phy_24m;
+       struct reset_control *rst_24m;
+
+       const struct rockchip_dp_phy_drv_data *drv_data;
 };
 
 static int rockchip_set_phy_state(struct phy *phy, bool enable)
 {
        struct rockchip_dp_phy *dp = phy_get_drvdata(phy);
+       const struct rockchip_dp_phy_drv_data *drv_data = dp->drv_data;
        int ret;
 
        if (enable) {
-               ret = regmap_write(dp->grf, GRF_SOC_CON12,
-                                  GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
-                                  GRF_EDP_PHY_SIDDQ_ON);
+               /* EDP 24m clock domain software reset request. */
+               reset_control_assert(dp->rst_24m);
+               usleep_range(20, 40);
+               reset_control_deassert(dp->rst_24m);
+               usleep_range(20, 40);
+
+               ret = regmap_write(dp->grf, drv_data->grf_reg_offset,
+                                  drv_data->siddq_on);
                if (ret < 0) {
                        dev_err(dp->dev, "Can't enable PHY power %d\n", ret);
                        return ret;
@@ -50,9 +60,8 @@ static int rockchip_set_phy_state(struct phy *phy, bool enable)
        } else {
                clk_disable_unprepare(dp->phy_24m);
 
-               ret = regmap_write(dp->grf, GRF_SOC_CON12,
-                                  GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
-                                  GRF_EDP_PHY_SIDDQ_OFF);
+               ret = regmap_write(dp->grf, drv_data->grf_reg_offset,
+                                  drv_data->siddq_off);
        }
 
        return ret;
@@ -80,17 +89,28 @@ static int rockchip_dp_phy_probe(struct platform_device *pdev)
        struct device_node *np = dev->of_node;
        struct phy_provider *phy_provider;
        struct rockchip_dp_phy *dp;
+       const struct rockchip_dp_phy_drv_data *drv_data;
        struct phy *phy;
        int ret;
 
        if (!np)
                return -ENODEV;
 
+       if (!dev->parent || !dev->parent->of_node)
+               return -ENODEV;
+
+       drv_data = of_device_get_match_data(dev);
+       if (!drv_data) {
+               dev_err(dev, "No OF match data provided\n");
+               return -EINVAL;
+       }
+
        dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
        if (IS_ERR(dp))
                return -ENOMEM;
 
        dp->dev = dev;
+       dp->drv_data = drv_data;
 
        dp->phy_24m = devm_clk_get(dev, "24m");
        if (IS_ERR(dp->phy_24m)) {
@@ -104,15 +124,22 @@ static int rockchip_dp_phy_probe(struct platform_device *pdev)
                return ret;
        }
 
-       dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+       /* optional */
+       dp->rst_24m = devm_reset_control_get_optional(&pdev->dev, "edp_24m");
+       if (IS_ERR(dp->rst_24m)) {
+               dev_info(dev, "No edp_24m reset control specified\n");
+               dp->rst_24m = NULL;
+       }
+
+       dp->grf = syscon_node_to_regmap(dev->parent->of_node);
        if (IS_ERR(dp->grf)) {
-               dev_err(dev, "rk3288-dp needs rockchip,grf property\n");
+               dev_err(dev, "rk3288-dp needs the General Register Files syscon\n");
                return PTR_ERR(dp->grf);
        }
 
-       ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER |
-                          GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK);
-       if (ret != 0) {
+       ret = regmap_write(dp->grf, drv_data->grf_reg_offset,
+                          drv_data->ref_clk_sel_inter);
+       if (ret) {
                dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret);
                return ret;
        }
@@ -129,8 +156,25 @@ static int rockchip_dp_phy_probe(struct platform_device *pdev)
        return PTR_ERR_OR_ZERO(phy_provider);
 }
 
+static const struct rockchip_dp_phy_drv_data rk3288_dp_phy_drv_data = {
+       .grf_reg_offset = 0x274,
+       .ref_clk_sel_inter = BIT(4) | BIT(20),
+       .siddq_on = 0 | BIT(21),
+       .siddq_off = BIT(5) | BIT(21),
+};
+
+static const struct rockchip_dp_phy_drv_data rk3368_dp_phy_drv_data = {
+       .grf_reg_offset = 0x410,
+       .ref_clk_sel_inter = BIT(0) | BIT(16),
+       .siddq_on = 0 | BIT(17),
+       .siddq_off = BIT(1) | BIT(17),
+};
+
 static const struct of_device_id rockchip_dp_phy_dt_ids[] = {
-       { .compatible = "rockchip,rk3288-dp-phy" },
+       { .compatible = "rockchip,rk3288-dp-phy",
+         .data = &rk3288_dp_phy_drv_data },
+       { .compatible = "rockchip,rk3368-dp-phy",
+         .data = &rk3368_dp_phy_drv_data },
        {}
 };