]> Gitweb @ Texas Instruments - Open Source Git Repositories - git.TI.com/gitweb - rpmsg/rpmsg.git/commitdiff
remoteproc/pru: add support for parsing pru interrupt mapping from DT
authorTero Kristo <t-kristo@ti.com>
Wed, 20 Feb 2019 21:14:16 +0000 (15:14 -0600)
committerSuman Anna <s-anna@ti.com>
Sun, 24 Feb 2019 01:20:52 +0000 (19:20 -0600)
PRU interrupt mapping can now be parsed from devicetree also. The design
uses a single property "ti,pru-interrupt-map" in the PRU consumer nodes
to list all the interrupt mapping data associated with all the PRUs used
by the PRU consumer/application. This is an alternative configuration
method in addition to the legacy resource table config, and supercedes
any firmware provided intc configuration structure.

The design parses and configures the PRU INTC when a client driver
acquires a PRU core through pru_rproc_get(), and unconfigures the INTC
when it releases the PRU core through pru_rproc_put(). The design also
allows any referenced PRU cores with zero corresponding entries in the
"ti,pru-interrupt-map" property to fallback to use the interrupt mapping
data through the firmware intc vendor resource type, that is processed
during the loading/starting of the core through rproc_boot(). This
provides the maximum flexibility for PRU client drivers/applications.
If both are provided, the config in DT takes precedence. It is possible
that a client neither has the DT property nor uses the vendor resource
in its firmware, implying that particular PRU core doesn't use/need any
interrupts.

Signed-off-by: Tero Kristo <t-kristo@ti.com>
[rogerq@ti.com: refactor DT-parse logic into a separate function]
Signed-off-by: Roger Quadros <rogerq@ti.com>
[s-anna@ti.com: various fixes and cleanups, update patch description]
Signed-off-by: Suman Anna <s-anna@ti.com>
drivers/remoteproc/pru_rproc.c

index 90a50973763bf83e14678639c09bc22f5e30c8ad..1726c1266e8455985a2ca7df51d313cf9a737176 100644 (file)
@@ -75,9 +75,11 @@ enum pru_iomem {
  * @sdram_da: device address of secondary Data RAM for this PRU
  * @shrdram_da: device address of shared Data RAM
  * @fw_name: name of firmware image used during loading
+ * @dt_irqs: number of irqs configured from DT
  * @lock: mutex to protect client usage
  * @dbg_single_step: debug state variable to set PRU into single step mode
  * @dbg_continuous: debug state variable to restore PRU execution mode
+ * @fw_has_intc_rsc: boolean flag to indicate INTC config through firmware
  */
 struct pru_rproc {
        int id;
@@ -96,9 +98,11 @@ struct pru_rproc {
        u32 sdram_da;
        u32 shrdram_da;
        const char *fw_name;
+       int dt_irqs;
        struct mutex lock; /* client access lock */
        u32 dbg_single_step;
        u32 dbg_continuous;
+       unsigned int fw_has_intc_rsc : 1;
 };
 
 static void *pru_d_da_to_va(struct pru_rproc *pru, u32 da, int len);
@@ -146,6 +150,104 @@ static int pru_rproc_set_firmware(struct rproc *rproc, const char *fw_name)
        return rproc_set_firmware(rproc, fw_name);
 }
 
+static int pru_rproc_intc_dt_config(struct pru_rproc *pru, int index)
+{
+       struct device *dev = &pru->rproc->dev;
+       struct device_node *np = pru->client_np;
+       struct property *prop;
+       const char *prop_name = "ti,pru-interrupt-map";
+       int ret = 0, i;
+       int dt_irqs;
+       u32 *arr;
+       bool has_irqs = false;
+
+       prop = of_find_property(np, prop_name, NULL);
+       if (!prop)
+               return 0;
+
+       dt_irqs = of_property_count_u32_elems(np, prop_name);
+       if (dt_irqs <= 0 || dt_irqs % 4) {
+               dev_err(dev, "bad interrupt map data %d, expected multiple of 4\n",
+                       dt_irqs);
+               return -EINVAL;
+       }
+
+       arr = kmalloc_array(dt_irqs, sizeof(u32), GFP_KERNEL);
+       if (!arr)
+               return -ENOMEM;
+
+       ret = of_property_read_u32_array(np, prop_name, arr, dt_irqs);
+       if (ret) {
+               dev_err(dev, "failed to read pru irq map: %d\n", ret);
+               goto out;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(pru->intc_config.sysev_to_ch); i++)
+               pru->intc_config.sysev_to_ch[i] = -1;
+
+       for (i = 0; i < ARRAY_SIZE(pru->intc_config.ch_to_host); i++)
+               pru->intc_config.ch_to_host[i] = -1;
+
+       for (i = 0; i < dt_irqs; i += 4) {
+               if (arr[i] != index)
+                       continue;
+
+               if (arr[i + 1] < 0 ||
+                   arr[i + 1] >= MAX_PRU_SYS_EVENTS) {
+                       dev_err(dev, "bad sys event %d\n", arr[i + 1]);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (arr[i + 2] < 0 ||
+                   arr[i + 2] >= MAX_PRU_CHANNELS) {
+                       dev_err(dev, "bad channel %d\n", arr[i + 2]);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               if (arr[i + 3] < 0 ||
+                   arr[i + 3] >= MAX_PRU_HOST_INT) {
+                       dev_err(dev, "bad irq %d\n", arr[i + 3]);
+                       ret = -EINVAL;
+                       goto out;
+               }
+
+               pru->intc_config.sysev_to_ch[arr[i + 1]] = arr[i + 2];
+               dev_dbg(dev, "sysevt-to-ch[%d] -> %d\n", arr[i + 1],
+                       arr[i + 2]);
+
+               pru->intc_config.ch_to_host[arr[i + 2]] = arr[i + 3];
+               dev_dbg(dev, "chnl-to-host[%d] -> %d\n", arr[i + 2],
+                       arr[i + 3]);
+
+               has_irqs = true;
+       }
+
+       /*
+        * The property "ti,pru-interrupt-map" is used in a consumer node, but
+        * need not necessarily have data for all referenced PRUs. Provide a
+        * fallback to get the interrupt data from firmware for PRUs ith no
+        * interrupt data.
+        */
+       if (!has_irqs) {
+               dev_dbg(dev, "no DT irqs, falling back to firmware intc rsc mode\n");
+               goto out;
+       }
+
+       pru->dt_irqs = dt_irqs;
+       ret = pruss_intc_configure(pru->pruss, &pru->intc_config);
+       if (ret) {
+               dev_err(dev, "failed to configure intc %d\n", ret);
+               pru->dt_irqs = 0;
+       }
+
+out:
+       kfree(arr);
+
+       return ret;
+}
+
 static struct rproc *__pru_rproc_get(struct device_node *np, int index)
 {
        struct device_node *rproc_np = NULL;
@@ -234,6 +336,10 @@ struct rproc *pru_rproc_get(struct device_node *np, int index)
                }
        }
 
+       ret = pru_rproc_intc_dt_config(pru, index);
+       if (ret)
+               goto err;
+
        return rproc;
 
 err:
@@ -264,6 +370,9 @@ void pru_rproc_put(struct rproc *rproc)
        if (!pru->client_np)
                return;
 
+       if (pru->dt_irqs)
+               pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
+
        pru_rproc_set_firmware(rproc, NULL);
 
        mutex_lock(&pru->lock);
@@ -544,7 +653,8 @@ static int pru_rproc_start(struct rproc *rproc)
        return 0;
 
 fail:
-       pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
+       if (!pru->dt_irqs && pru->fw_has_intc_rsc)
+               pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
        return ret;
 }
 
@@ -566,7 +676,8 @@ static int pru_rproc_stop(struct rproc *rproc)
                free_irq(pru->irq_vring, pru);
 
        /* undo INTC config */
-       pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
+       if (!pru->dt_irqs && pru->fw_has_intc_rsc)
+               pruss_intc_unconfigure(pru->pruss, &pru->intc_config);
 
        return 0;
 }
@@ -657,6 +768,8 @@ static int pru_handle_vendor_intrmap(struct rproc *rproc,
                dev_dbg(dev, "chnl-to-host[%d] -> %d\n", i, intr_no);
        }
 
+       pru->fw_has_intc_rsc = 1;
+
        ret = pruss_intc_configure(pruss, &pru->intc_config);
        if (ret)
                dev_err(dev, "failed to configure pruss intc %d\n", ret);
@@ -669,15 +782,18 @@ static int pru_rproc_handle_vendor_rsc(struct rproc *rproc,
                                       struct fw_rsc_vendor *rsc)
 {
        struct device *dev = rproc->dev.parent;
-       int ret = -EINVAL;
+       struct pru_rproc *pru = rproc->priv;
+       int ret = 0;
 
        switch (rsc->u.st.st_type) {
        case PRUSS_RSC_INTRS:
-               ret = pru_handle_vendor_intrmap(rproc, rsc);
+               if (!pru->dt_irqs)
+                       ret = pru_handle_vendor_intrmap(rproc, rsc);
                break;
        default:
                dev_err(dev, "%s: cannot handle unknown type %d\n", __func__,
                        rsc->u.st.st_type);
+               ret = -EINVAL;
        }
 
        return ret;