]> git.kernelconcepts.de Git - karo-tx-linux.git/blobdiff - drivers/usb/chipidea/ci_hdrc_msm.c
usb: chipidea: msm: Mux over secondary phy at the right time
[karo-tx-linux.git] / drivers / usb / chipidea / ci_hdrc_msm.c
index 7e870a253f555d5feb044f8c23cb412ce43b8dd2..4b0aadc2be2f5cc9b0354ca306f40ad69bf0e0ea 100644 (file)
@@ -8,29 +8,44 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
-#include <linux/usb/gadget.h>
 #include <linux/usb/chipidea.h>
 #include <linux/clk.h>
 #include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
 
 #include "ci.h"
 
 #define HS_PHY_AHB_MODE                        0x0098
 
+/* Vendor base starts at 0x200 beyond CI base */
+#define HS_PHY_SEC_CTRL                        0x0078
+#define HS_PHY_DIG_CLAMP_N             BIT(16)
+
 struct ci_hdrc_msm {
        struct platform_device *ci;
        struct clk *core_clk;
        struct clk *iface_clk;
        struct clk *fs_clk;
+       bool secondary_phy;
+       void __iomem *base;
 };
 
 static void ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
 {
-       struct device *dev = ci->gadget.dev.parent;
+       struct device *dev = ci->dev->parent;
+       struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
 
        switch (event) {
        case CI_HDRC_CONTROLLER_RESET_EVENT:
                dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
+               if (msm_ci->secondary_phy) {
+                       u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
+                       val |= HS_PHY_DIG_CLAMP_N;
+                       writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
+               }
+
                /* use AHB transactor, allow posted data writes */
                hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
                usb_phy_init(ci->usb_phy);
@@ -59,6 +74,39 @@ static struct ci_hdrc_platform_data ci_hdrc_msm_platdata = {
        .notify_event           = ci_hdrc_msm_notify_event,
 };
 
+static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
+                              struct platform_device *pdev)
+{
+       struct regmap *regmap;
+       struct device *dev = &pdev->dev;
+       struct of_phandle_args args;
+       u32 val;
+       int ret;
+
+       ret = of_parse_phandle_with_fixed_args(dev->of_node, "phy-select", 2, 0,
+                                              &args);
+       if (ret)
+               return 0;
+
+       regmap = syscon_node_to_regmap(args.np);
+       of_node_put(args.np);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       ret = regmap_write(regmap, args.args[0], args.args[1]);
+       if (ret)
+               return ret;
+
+       ci->secondary_phy = !!args.args[1];
+       if (ci->secondary_phy) {
+               val = readl_relaxed(ci->base + HS_PHY_SEC_CTRL);
+               val |= HS_PHY_DIG_CLAMP_N;
+               writel_relaxed(val, ci->base + HS_PHY_SEC_CTRL);
+       }
+
+       return 0;
+}
+
 static int ci_hdrc_msm_probe(struct platform_device *pdev)
 {
        struct ci_hdrc_msm *ci;
@@ -66,6 +114,7 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
        struct usb_phy *phy;
        struct clk *clk;
        struct reset_control *reset;
+       struct resource *res;
        int ret;
 
        dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
@@ -105,6 +154,11 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
                ci->fs_clk = NULL;
        }
 
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       ci->base = devm_ioremap_resource(&pdev->dev, res);
+       if (!ci->base)
+               return -ENOMEM;
+
        ret = clk_prepare_enable(ci->fs_clk);
        if (ret)
                return ret;
@@ -123,6 +177,10 @@ static int ci_hdrc_msm_probe(struct platform_device *pdev)
        if (ret)
                goto err_iface;
 
+       ret = ci_hdrc_msm_mux_phy(ci, pdev);
+       if (ret)
+               goto err_mux;
+
        plat_ci = ci_hdrc_add_device(&pdev->dev,
                                pdev->resource, pdev->num_resources,
                                &ci_hdrc_msm_platdata);