diff options
-rw-r--r-- | drivers/remoteproc/imx_rproc.c | 160 |
1 files changed, 160 insertions, 0 deletions
diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c index 45acc2ce930d..1999e50f5d4c 100644 --- a/drivers/remoteproc/imx_rproc.c +++ b/drivers/remoteproc/imx_rproc.c @@ -47,6 +47,8 @@ | IMX6SX_SW_M4C_RST) #define IMX7D_RPROC_MEM_MAX 8 +#define IMX7D_RPROC_RNODES_MAX 8 +#define IMX7D_RPROC_RNODE_CLKS_MAX 8 /** * struct imx_rproc_mem - slim internal memory structure @@ -81,6 +83,12 @@ struct imx_rproc_dcfg { size_t att_size; }; +struct imx_rproc_rnode { + struct device_node *node; + struct clk *clk[IMX7D_RPROC_RNODE_CLKS_MAX]; + unsigned int clks; +}; + struct imx_rproc { struct device *dev; struct regmap *regmap; @@ -88,6 +96,8 @@ struct imx_rproc { const struct imx_rproc_dcfg *dcfg; struct imx_rproc_mem mem[IMX7D_RPROC_MEM_MAX]; struct clk *clk; + struct imx_rproc_rnode rnode[IMX7D_RPROC_RNODES_MAX]; + unsigned int rnodes; }; static const struct imx_rproc_att imx_rproc_att_imx7d[] = { @@ -250,6 +260,142 @@ static const struct rproc_ops imx_rproc_ops = { .da_to_va = imx_rproc_da_to_va, }; +static int imx_rproc_set_clk_rates(struct device_node *node, bool clk_supplier) +{ + struct of_phandle_args clkspec; + struct property *prop; + const __be32 *cur; + int rc, index = 0; + struct clk *clk; + u32 rate; + + of_property_for_each_u32(node, "assigned-clock-rates", prop, cur, rate) { + if (rate) { + rc = of_parse_phandle_with_args(node, "clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) { + /* skip empty (null) phandles */ + if (rc == -ENOENT) + continue; + else + return rc; + } + if (clkspec.np == node && !clk_supplier) + return 0; + + clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + pr_warn("clk: couldn't get clock %d for %s\n", + index, node->full_name); + return PTR_ERR(clk); + } + + rc = clk_set_rate(clk, rate); + if (rc < 0) + pr_err("clk: couldn't set clk rate to %u (%d), current rate: %lu\n", + rate, rc, + clk_get_rate(clk)); + clk_put(clk); + } + index++; + } + return 0; +} + +static int imx_rproc_set_rclks(struct imx_rproc *priv, struct imx_rproc_rnode *rn) +{ + struct device_node *node = rn->node; + struct device *dev = priv->dev; + struct of_phandle_args clkspec; + int index, rc, ret, num_parents; + struct clk *clk; + + imx_rproc_set_clk_rates(node, 0); + num_parents = of_count_phandle_with_args(node, "clocks", "#clock-cells"); + if (num_parents == -EINVAL) { + dev_err(dev, "clk: invalid value of clocks property at %s\n", + node->full_name); + return -EINVAL; + } + + if (IMX7D_RPROC_RNODE_CLKS_MAX < num_parents) { + dev_err(dev, "unsupported count of remote clocks: %i, max: %i\n", + num_parents, IMX7D_RPROC_RNODE_CLKS_MAX); + return -EINVAL; + } + + for (index = 0; index < num_parents; index++) { + rc = of_parse_phandle_with_args(node, "clocks", + "#clock-cells", index, &clkspec); + if (rc < 0) + goto err; + + if (clkspec.np == node) { + rc = 0; + goto err; + } + + clk = of_clk_get_from_provider(&clkspec); + if (IS_ERR(clk)) { + if (PTR_ERR(clk) != -EPROBE_DEFER) + dev_warn(dev, "clk: couldn't get assigned clock %d for %s\n", + index, node->full_name); + rc = PTR_ERR(clk); + goto err; + } + + rn->clk[index] = clk; + rn->clks++; + /* + * clk for M4 block including memory. Should be + * enabled before .start for FW transfer. + */ + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(dev, "Failed to enable clock\n"); + return ret; + } + } + return 0; +err: + return rc; +} + +static int imx_rproc_rnodes_init(struct imx_rproc *priv) +{ + struct device *dev = priv->dev; + struct device_node *node = dev->of_node; + int a, err, num_parents; + + num_parents = of_count_phandle_with_args(node, "remote-nodes", NULL); + if (num_parents == -EINVAL) { + dev_err(dev, "rnote: invalid value of remote-node property at %s\n", + node->full_name); + return -EINVAL; + } + + if (IMX7D_RPROC_RNODES_MAX < num_parents) { + dev_err(dev, "unsupported count of remote node: %i, max: %i\n", + num_parents, IMX7D_RPROC_RNODES_MAX); + return -EINVAL; + } + + /* remap optional addresses */ + for (a = 0; a < num_parents; a++) { + struct device_node *rn; + + rn = of_parse_phandle(node, "remote-nodes", a); + priv->rnode[a].node = rn; + priv->rnodes++; + err = imx_rproc_set_rclks(priv, &priv->rnode[a]); + if (err) + break; + } + + return err; +} + static int imx_rproc_addr_init(struct imx_rproc *priv, struct platform_device *pdev) { @@ -374,6 +520,12 @@ static int imx_rproc_probe(struct platform_device *pdev) goto err_put_rproc; } + ret = imx_rproc_rnodes_init(priv); + if (ret) { + dev_err(dev, "filed on imx_rproc_rnodes_init\n"); + goto err_put_rproc; + } + ret = rproc_add(rproc); if (ret) { dev_err(dev, "rproc_add failed\n"); @@ -394,6 +546,14 @@ static int imx_rproc_remove(struct platform_device *pdev) { struct rproc *rproc = platform_get_drvdata(pdev); struct imx_rproc *priv = rproc->priv; + int a, b; + + for (a = 0; a < priv->rnodes; a++) { + struct imx_rproc_rnode *rn = &priv->rnode[a]; + + for (b = 0; b < rn->clks; b++) + clk_disable_unprepare(rn->clk[b]); + } clk_disable_unprepare(priv->clk); rproc_del(rproc); |