summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/remoteproc/imx_rproc.c160
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);