Merge tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 May 2013 21:04:56 +0000 (14:04 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 7 May 2013 21:04:56 +0000 (14:04 -0700)
Pull remoteproc update from Ohad Ben-Cohen:

 - Some refactoring, cleanups and small improvements from Sjur
   Brændeland.  The improvements are mainly about better supporting
   varios virtio properties (such as virtio's config space, status and
   features).  I now see that I messed up while commiting one of Sjur's
   patches and erroneously put myself as the author, as well as letting
   a nasty typo sneak in.  I will not fix this in order to avoid
   rebasing the patches.  Sjur - sorry!

 - A new remoteproc driver for OMAP-L13x (technically a DaVinci
   platform) from Robert Tivy.

 - Extend OMAP support to OMAP5 as well, from Vincent StehlĂ©.

 - Fix Kconfig VIRTUALIZATION dependency, from Suman Anna (a
   non-critical fix which arrived late during the rc cycle).

* tag 'remoteproc-3.10' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc:
  remoteproc: fix kconfig dependencies for VIRTIO
  remoteproc/davinci: add a remoteproc driver for OMAP-L13x DSP
  remoteproc: support default firmware name in rproc_alloc()
  remoteproc/omap: support OMAP5 too
  remoteproc: set vring addresses in resource table
  remoteproc: support virtio config space.
  remoteproc: perserve resource table data
  remoteproc: calculate max_notifyid by counting vrings
  remoteproc: code cleanup of resource parsing
  remoteproc: parse STE-firmware and find resource table address
  remoteproc: add find_loaded_rsc_table firmware ops
  remoteproc: refactor rproc_elf_find_rsc_table()

drivers/remoteproc/Kconfig
drivers/remoteproc/Makefile
drivers/remoteproc/da8xx_remoteproc.c [new file with mode: 0644]
drivers/remoteproc/remoteproc_core.c
drivers/remoteproc/remoteproc_elf_loader.c
drivers/remoteproc/remoteproc_internal.h
drivers/remoteproc/remoteproc_virtio.c
drivers/remoteproc/ste_modem_rproc.c
include/linux/remoteproc.h

index c6d77e20622c45f54e17e7063e3e498f781739c6..d4d377c40ec96585126f474238040d47c9e79c2e 100644 (file)
@@ -4,13 +4,15 @@ menu "Remoteproc drivers"
 config REMOTEPROC
        tristate
        depends on HAS_DMA
+       select CRC32
        select FW_LOADER
        select VIRTIO
+       select VIRTUALIZATION
 
 config OMAP_REMOTEPROC
        tristate "OMAP remoteproc support"
        depends on HAS_DMA
-       depends on ARCH_OMAP4
+       depends on ARCH_OMAP4 || SOC_OMAP5
        depends on OMAP_IOMMU
        depends on OMAP_MBOX_FWK
        select REMOTEPROC
@@ -38,4 +40,27 @@ config STE_MODEM_RPROC
          This can be either built-in or a loadable module.
          If unsure say N.
 
+config DA8XX_REMOTEPROC
+       tristate "DA8xx/OMAP-L13x remoteproc support"
+       depends on ARCH_DAVINCI_DA8XX
+       select CMA
+       select REMOTEPROC
+       select RPMSG
+       help
+         Say y here to support DA8xx/OMAP-L13x remote processors via the
+         remote processor framework.
+
+         You want to say y here in order to enable AMP
+         use-cases to run on your platform (multimedia codecs are
+         offloaded to remote DSP processors using this framework).
+
+         This module controls the name of the firmware file that gets
+         loaded on the DSP.  This file must reside in the /lib/firmware
+         directory.  It can be specified via the module parameter
+         da8xx_fw_name=<filename>, and if not specified will default to
+         "rproc-dsp-fw".
+
+         It's safe to say n here if you're not interested in multimedia
+         offloading.
+
 endmenu
index 391b65181c054c795a8e0e34b1092f4ddba969f4..ac2ff75686d20da708d77728aca94f23cbdf1764 100644 (file)
@@ -9,3 +9,4 @@ remoteproc-y                            += remoteproc_virtio.o
 remoteproc-y                           += remoteproc_elf_loader.o
 obj-$(CONFIG_OMAP_REMOTEPROC)          += omap_remoteproc.o
 obj-$(CONFIG_STE_MODEM_RPROC)          += ste_modem_rproc.o
+obj-$(CONFIG_DA8XX_REMOTEPROC)         += da8xx_remoteproc.o
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c
new file mode 100644 (file)
index 0000000..9b2e60a
--- /dev/null
@@ -0,0 +1,324 @@
+/*
+ * Remote processor machine-specific module for DA8XX
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+
+#include <mach/clock.h>   /* for davinci_clk_reset_assert/deassert() */
+
+#include "remoteproc_internal.h"
+
+static char *da8xx_fw_name;
+module_param(da8xx_fw_name, charp, S_IRUGO);
+MODULE_PARM_DESC(da8xx_fw_name,
+                "\n\t\tName of DSP firmware file in /lib/firmware"
+                " (if not specified defaults to 'rproc-dsp-fw')");
+
+/*
+ * OMAP-L138 Technical References:
+ * http://www.ti.com/product/omap-l138
+ */
+#define SYSCFG_CHIPSIG0 BIT(0)
+#define SYSCFG_CHIPSIG1 BIT(1)
+#define SYSCFG_CHIPSIG2 BIT(2)
+#define SYSCFG_CHIPSIG3 BIT(3)
+#define SYSCFG_CHIPSIG4 BIT(4)
+
+/**
+ * struct da8xx_rproc - da8xx remote processor instance state
+ * @rproc: rproc handle
+ * @dsp_clk: placeholder for platform's DSP clk
+ * @ack_fxn: chip-specific ack function for ack'ing irq
+ * @irq_data: ack_fxn function parameter
+ * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR)
+ * @bootreg: virt ptr to DSP boot address register (HOST1CFG)
+ * @irq: irq # used by this instance
+ */
+struct da8xx_rproc {
+       struct rproc *rproc;
+       struct clk *dsp_clk;
+       void (*ack_fxn)(struct irq_data *data);
+       struct irq_data *irq_data;
+       void __iomem *chipsig;
+       void __iomem *bootreg;
+       int irq;
+};
+
+/**
+ * handle_event() - inbound virtqueue message workqueue function
+ *
+ * This function is registered as a kernel thread and is scheduled by the
+ * kernel handler.
+ */
+static irqreturn_t handle_event(int irq, void *p)
+{
+       struct rproc *rproc = (struct rproc *)p;
+
+       /* Process incoming buffers on all our vrings */
+       rproc_vq_interrupt(rproc, 0);
+       rproc_vq_interrupt(rproc, 1);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * da8xx_rproc_callback() - inbound virtqueue message handler
+ *
+ * This handler is invoked directly by the kernel whenever the remote
+ * core (DSP) has modified the state of a virtqueue.  There is no
+ * "payload" message indicating the virtqueue index as is the case with
+ * mailbox-based implementations on OMAP4.  As such, this handler "polls"
+ * each known virtqueue index for every invocation.
+ */
+static irqreturn_t da8xx_rproc_callback(int irq, void *p)
+{
+       struct rproc *rproc = (struct rproc *)p;
+       struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+       u32 chipsig;
+
+       chipsig = readl(drproc->chipsig);
+       if (chipsig & SYSCFG_CHIPSIG0) {
+               /* Clear interrupt level source */
+               writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4);
+
+               /*
+                * ACK intr to AINTC.
+                *
+                * It has already been ack'ed by the kernel before calling
+                * this function, but since the ARM<->DSP interrupts in the
+                * CHIPSIG register are "level" instead of "pulse" variety,
+                * we need to ack it after taking down the level else we'll
+                * be called again immediately after returning.
+                */
+               drproc->ack_fxn(drproc->irq_data);
+
+               return IRQ_WAKE_THREAD;
+       }
+
+       return IRQ_HANDLED;
+}
+
+static int da8xx_rproc_start(struct rproc *rproc)
+{
+       struct device *dev = rproc->dev.parent;
+       struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+       struct clk *dsp_clk = drproc->dsp_clk;
+
+       /* hw requires the start (boot) address be on 1KB boundary */
+       if (rproc->bootaddr & 0x3ff) {
+               dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
+
+               return -EINVAL;
+       }
+
+       writel(rproc->bootaddr, drproc->bootreg);
+
+       clk_enable(dsp_clk);
+       davinci_clk_reset_deassert(dsp_clk);
+
+       return 0;
+}
+
+static int da8xx_rproc_stop(struct rproc *rproc)
+{
+       struct da8xx_rproc *drproc = rproc->priv;
+
+       clk_disable(drproc->dsp_clk);
+
+       return 0;
+}
+
+/* kick a virtqueue */
+static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
+{
+       struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+
+       /* Interupt remote proc */
+       writel(SYSCFG_CHIPSIG2, drproc->chipsig);
+}
+
+static struct rproc_ops da8xx_rproc_ops = {
+       .start = da8xx_rproc_start,
+       .stop = da8xx_rproc_stop,
+       .kick = da8xx_rproc_kick,
+};
+
+static int reset_assert(struct device *dev)
+{
+       struct clk *dsp_clk;
+
+       dsp_clk = clk_get(dev, NULL);
+       if (IS_ERR(dsp_clk)) {
+               dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
+               return PTR_RET(dsp_clk);
+       }
+
+       davinci_clk_reset_assert(dsp_clk);
+       clk_put(dsp_clk);
+
+       return 0;
+}
+
+static int da8xx_rproc_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct da8xx_rproc *drproc;
+       struct rproc *rproc;
+       struct irq_data *irq_data;
+       struct resource *bootreg_res;
+       struct resource *chipsig_res;
+       struct clk *dsp_clk;
+       void __iomem *chipsig;
+       void __iomem *bootreg;
+       int irq;
+       int ret;
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq);
+               return irq;
+       }
+
+       irq_data = irq_get_irq_data(irq);
+       if (!irq_data) {
+               dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
+               return -EINVAL;
+       }
+
+       bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!bootreg_res) {
+               dev_err(dev,
+                       "platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!chipsig_res) {
+               dev_err(dev,
+                       "platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
+               return -EADDRNOTAVAIL;
+       }
+
+       bootreg = devm_ioremap_resource(dev, bootreg_res);
+       if (IS_ERR(bootreg))
+               return PTR_ERR(bootreg);
+
+       chipsig = devm_ioremap_resource(dev, chipsig_res);
+       if (IS_ERR(chipsig))
+               return PTR_ERR(chipsig);
+
+       dsp_clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(dsp_clk)) {
+               dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
+
+               return PTR_ERR(dsp_clk);
+       }
+
+       rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
+               sizeof(*drproc));
+       if (!rproc)
+               return -ENOMEM;
+
+       drproc = rproc->priv;
+       drproc->rproc = rproc;
+
+       platform_set_drvdata(pdev, rproc);
+
+       /* everything the ISR needs is now setup, so hook it up */
+       ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
+                                       handle_event, 0, "da8xx-remoteproc",
+                                       rproc);
+       if (ret) {
+               dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
+               goto free_rproc;
+       }
+
+       /*
+        * rproc_add() can end up enabling the DSP's clk with the DSP
+        * *not* in reset, but da8xx_rproc_start() needs the DSP to be
+        * held in reset at the time it is called.
+        */
+       ret = reset_assert(dev);
+       if (ret)
+               goto free_rproc;
+
+       drproc->chipsig = chipsig;
+       drproc->bootreg = bootreg;
+       drproc->ack_fxn = irq_data->chip->irq_ack;
+       drproc->irq_data = irq_data;
+       drproc->irq = irq;
+       drproc->dsp_clk = dsp_clk;
+
+       ret = rproc_add(rproc);
+       if (ret) {
+               dev_err(dev, "rproc_add failed: %d\n", ret);
+               goto free_rproc;
+       }
+
+       return 0;
+
+free_rproc:
+       rproc_put(rproc);
+
+       return ret;
+}
+
+static int da8xx_rproc_remove(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct rproc *rproc = platform_get_drvdata(pdev);
+       struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+
+       /*
+        * It's important to place the DSP in reset before going away,
+        * since a subsequent insmod of this module may enable the DSP's
+        * clock before its program/boot-address has been loaded and
+        * before this module's probe has had a chance to reset the DSP.
+        * Without the reset, the DSP can lockup permanently when it
+        * begins executing garbage.
+        */
+       reset_assert(dev);
+
+       /*
+        * The devm subsystem might end up releasing things before
+        * freeing the irq, thus allowing an interrupt to sneak in while
+        * the device is being removed.  This should prevent that.
+        */
+       disable_irq(drproc->irq);
+
+       devm_clk_put(dev, drproc->dsp_clk);
+
+       rproc_del(rproc);
+       rproc_put(rproc);
+
+       return 0;
+}
+
+static struct platform_driver da8xx_rproc_driver = {
+       .probe = da8xx_rproc_probe,
+       .remove = da8xx_rproc_remove,
+       .driver = {
+               .name = "davinci-rproc",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(da8xx_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DA8XX Remote Processor control driver");
index 814af5ab8a72d2d5e979855bbffc86b487fe41f6..022dc635d01e4935ee84c0378e0d606e94028a39 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/iommu.h>
 #include <linux/idr.h>
 #include <linux/elf.h>
+#include <linux/crc32.h>
 #include <linux/virtio_ids.h>
 #include <linux/virtio_ring.h>
 #include <asm/byteorder.h>
@@ -45,7 +46,8 @@
 
 typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
                                struct resource_table *table, int len);
-typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail);
+typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
+                                void *, int offset, int avail);
 
 /* Unique indices for remoteproc devices */
 static DEFINE_IDA(rproc_dev_index);
@@ -192,6 +194,7 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        struct rproc *rproc = rvdev->rproc;
        struct device *dev = &rproc->dev;
        struct rproc_vring *rvring = &rvdev->vring[i];
+       struct fw_rsc_vdev *rsc;
        dma_addr_t dma;
        void *va;
        int ret, size, notifyid;
@@ -202,7 +205,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        /*
         * Allocate non-cacheable memory for the vring. In the future
         * this call will also configure the IOMMU for us
-        * TODO: let the rproc know the da of this vring
         */
        va = dma_alloc_coherent(dev->parent, size, &dma, GFP_KERNEL);
        if (!va) {
@@ -213,7 +215,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        /*
         * Assign an rproc-wide unique index for this vring
         * TODO: assign a notifyid for rvdev updates as well
-        * TODO: let the rproc know the notifyid of this vring
         * TODO: support predefined notifyids (via resource table)
         */
        ret = idr_alloc(&rproc->notifyids, rvring, 0, 0, GFP_KERNEL);
@@ -224,9 +225,6 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        }
        notifyid = ret;
 
-       /* Store largest notifyid */
-       rproc->max_notifyid = max(rproc->max_notifyid, notifyid);
-
        dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
                                (unsigned long long)dma, size, notifyid);
 
@@ -234,6 +232,15 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
        rvring->dma = dma;
        rvring->notifyid = notifyid;
 
+       /*
+        * Let the rproc know the notifyid and da of this vring.
+        * Not all platforms use dma_alloc_coherent to automatically
+        * set up the iommu. In this case the device address (da) will
+        * hold the physical address and not the device address.
+        */
+       rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
+       rsc->vring[i].da = dma;
+       rsc->vring[i].notifyid = notifyid;
        return 0;
 }
 
@@ -268,25 +275,20 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
        return 0;
 }
 
-static int rproc_max_notifyid(int id, void *p, void *data)
-{
-       int *maxid = data;
-       *maxid = max(*maxid, id);
-       return 0;
-}
-
 void rproc_free_vring(struct rproc_vring *rvring)
 {
        int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
        struct rproc *rproc = rvring->rvdev->rproc;
-       int maxid = 0;
+       int idx = rvring->rvdev->vring - rvring;
+       struct fw_rsc_vdev *rsc;
 
        dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma);
        idr_remove(&rproc->notifyids, rvring->notifyid);
 
-       /* Find the largest remaining notifyid */
-       idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid);
-       rproc->max_notifyid = maxid;
+       /* reset resource entry info */
+       rsc = (void *)rproc->table_ptr + rvring->rvdev->rsc_offset;
+       rsc->vring[idx].da = 0;
+       rsc->vring[idx].notifyid = -1;
 }
 
 /**
@@ -317,7 +319,7 @@ void rproc_free_vring(struct rproc_vring *rvring)
  * Returns 0 on success, or an appropriate error code otherwise
  */
 static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
 {
        struct device *dev = &rproc->dev;
        struct rproc_vdev *rvdev;
@@ -358,8 +360,8 @@ static int rproc_handle_vdev(struct rproc *rproc, struct fw_rsc_vdev *rsc,
                        goto free_rvdev;
        }
 
-       /* remember the device features */
-       rvdev->dfeatures = rsc->dfeatures;
+       /* remember the resource offset*/
+       rvdev->rsc_offset = offset;
 
        list_add_tail(&rvdev->node, &rproc->rvdevs);
 
@@ -394,7 +396,7 @@ free_rvdev:
  * Returns 0 on success, or an appropriate error code otherwise
  */
 static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
 {
        struct rproc_mem_entry *trace;
        struct device *dev = &rproc->dev;
@@ -476,7 +478,7 @@ static int rproc_handle_trace(struct rproc *rproc, struct fw_rsc_trace *rsc,
  * are outside those ranges.
  */
 static int rproc_handle_devmem(struct rproc *rproc, struct fw_rsc_devmem *rsc,
-                                                               int avail)
+                                                       int offset, int avail)
 {
        struct rproc_mem_entry *mapping;
        struct device *dev = &rproc->dev;
@@ -549,7 +551,9 @@ out:
  * pressure is important; it may have a substantial impact on performance.
  */
 static int rproc_handle_carveout(struct rproc *rproc,
-                               struct fw_rsc_carveout *rsc, int avail)
+                                               struct fw_rsc_carveout *rsc,
+                                               int offset, int avail)
+
 {
        struct rproc_mem_entry *carveout, *mapping;
        struct device *dev = &rproc->dev;
@@ -671,28 +675,45 @@ free_carv:
        return ret;
 }
 
+static int rproc_count_vrings(struct rproc *rproc, struct fw_rsc_vdev *rsc,
+                             int offset, int avail)
+{
+       /* Summarize the number of notification IDs */
+       rproc->max_notifyid += rsc->num_of_vrings;
+
+       return 0;
+}
+
 /*
  * A lookup table for resource handlers. The indices are defined in
  * enum fw_resource_type.
  */
-static rproc_handle_resource_t rproc_handle_rsc[] = {
+static rproc_handle_resource_t rproc_loading_handlers[RSC_LAST] = {
        [RSC_CARVEOUT] = (rproc_handle_resource_t)rproc_handle_carveout,
        [RSC_DEVMEM] = (rproc_handle_resource_t)rproc_handle_devmem,
        [RSC_TRACE] = (rproc_handle_resource_t)rproc_handle_trace,
        [RSC_VDEV] = NULL, /* VDEVs were handled upon registrarion */
 };
 
+static rproc_handle_resource_t rproc_vdev_handler[RSC_LAST] = {
+       [RSC_VDEV] = (rproc_handle_resource_t)rproc_handle_vdev,
+};
+
+static rproc_handle_resource_t rproc_count_vrings_handler[RSC_LAST] = {
+       [RSC_VDEV] = (rproc_handle_resource_t)rproc_count_vrings,
+};
+
 /* handle firmware resource entries before booting the remote processor */
-static int
-rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len)
+static int rproc_handle_resources(struct rproc *rproc, int len,
+                                 rproc_handle_resource_t handlers[RSC_LAST])
 {
        struct device *dev = &rproc->dev;
        rproc_handle_resource_t handler;
        int ret = 0, i;
 
-       for (i = 0; i < table->num; i++) {
-               int offset = table->offset[i];
-               struct fw_rsc_hdr *hdr = (void *)table + offset;
+       for (i = 0; i < rproc->table_ptr->num; i++) {
+               int offset = rproc->table_ptr->offset[i];
+               struct fw_rsc_hdr *hdr = (void *)rproc->table_ptr + offset;
                int avail = len - offset - sizeof(*hdr);
                void *rsc = (void *)hdr + sizeof(*hdr);
 
@@ -709,45 +730,11 @@ rproc_handle_boot_rsc(struct rproc *rproc, struct resource_table *table, int len
                        continue;
                }
 
-               handler = rproc_handle_rsc[hdr->type];
+               handler = handlers[hdr->type];
                if (!handler)
                        continue;
 
-               ret = handler(rproc, rsc, avail);
-               if (ret)
-                       break;
-       }
-
-       return ret;
-}
-
-/* handle firmware resource entries while registering the remote processor */
-static int
-rproc_handle_virtio_rsc(struct rproc *rproc, struct resource_table *table, int len)
-{
-       struct device *dev = &rproc->dev;
-       int ret = 0, i;
-
-       for (i = 0; i < table->num; i++) {
-               int offset = table->offset[i];
-               struct fw_rsc_hdr *hdr = (void *)table + offset;
-               int avail = len - offset - sizeof(*hdr);
-               struct fw_rsc_vdev *vrsc;
-
-               /* make sure table isn't truncated */
-               if (avail < 0) {
-                       dev_err(dev, "rsc table is truncated\n");
-                       return -EINVAL;
-               }
-
-               dev_dbg(dev, "%s: rsc type %d\n", __func__, hdr->type);
-
-               if (hdr->type != RSC_VDEV)
-                       continue;
-
-               vrsc = (struct fw_rsc_vdev *)hdr->data;
-
-               ret = rproc_handle_vdev(rproc, vrsc, avail);
+               ret = handler(rproc, rsc, offset + sizeof(*hdr), avail);
                if (ret)
                        break;
        }
@@ -805,9 +792,12 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
 {
        struct device *dev = &rproc->dev;
        const char *name = rproc->firmware;
-       struct resource_table *table;
+       struct resource_table *table, *loaded_table;
        int ret, tablesz;
 
+       if (!rproc->table_ptr)
+               return -ENOMEM;
+
        ret = rproc_fw_sanity_check(rproc, fw);
        if (ret)
                return ret;
@@ -833,8 +823,15 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
                goto clean_up;
        }
 
+       /* Verify that resource table in loaded fw is unchanged */
+       if (rproc->table_csum != crc32(0, table, tablesz)) {
+               dev_err(dev, "resource checksum failed, fw changed?\n");
+               ret = -EINVAL;
+               goto clean_up;
+       }
+
        /* handle fw resources which are required to boot rproc */
-       ret = rproc_handle_boot_rsc(rproc, table, tablesz);
+       ret = rproc_handle_resources(rproc, tablesz, rproc_loading_handlers);
        if (ret) {
                dev_err(dev, "Failed to process resources: %d\n", ret);
                goto clean_up;
@@ -847,6 +844,19 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
                goto clean_up;
        }
 
+       /*
+        * The starting device has been given the rproc->cached_table as the
+        * resource table. The address of the vring along with the other
+        * allocated resources (carveouts etc) is stored in cached_table.
+        * In order to pass this information to the remote device we must
+        * copy this information to device memory.
+        */
+       loaded_table = rproc_find_loaded_rsc_table(rproc, fw);
+       if (!loaded_table)
+               goto clean_up;
+
+       memcpy(loaded_table, rproc->cached_table, tablesz);
+
        /* power up the remote processor */
        ret = rproc->ops->start(rproc);
        if (ret) {
@@ -854,6 +864,13 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
                goto clean_up;
        }
 
+       /*
+        * Update table_ptr so that all subsequent vring allocations and
+        * virtio fields manipulation update the actual loaded resource table
+        * in device memory.
+        */
+       rproc->table_ptr = loaded_table;
+
        rproc->state = RPROC_RUNNING;
 
        dev_info(dev, "remote processor %s is now up\n", rproc->name);
@@ -888,11 +905,30 @@ static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
        if (!table)
                goto out;
 
-       /* look for virtio devices and register them */
-       ret = rproc_handle_virtio_rsc(rproc, table, tablesz);
+       rproc->table_csum = crc32(0, table, tablesz);
+
+       /*
+        * Create a copy of the resource table. When a virtio device starts
+        * and calls vring_new_virtqueue() the address of the allocated vring
+        * will be stored in the cached_table. Before the device is started,
+        * cached_table will be copied into devic memory.
+        */
+       rproc->cached_table = kmalloc(tablesz, GFP_KERNEL);
+       if (!rproc->cached_table)
+               goto out;
+
+       memcpy(rproc->cached_table, table, tablesz);
+       rproc->table_ptr = rproc->cached_table;
+
+       /* count the number of notify-ids */
+       rproc->max_notifyid = -1;
+       ret = rproc_handle_resources(rproc, tablesz, rproc_count_vrings_handler);
        if (ret)
                goto out;
 
+       /* look for virtio devices and register them */
+       ret = rproc_handle_resources(rproc, tablesz, rproc_vdev_handler);
+
 out:
        release_firmware(fw);
        /* allow rproc_del() contexts, if any, to proceed */
@@ -950,6 +986,9 @@ int rproc_trigger_recovery(struct rproc *rproc)
        /* wait until there is no more rproc users */
        wait_for_completion(&rproc->crash_comp);
 
+       /* Free the copy of the resource table */
+       kfree(rproc->cached_table);
+
        return rproc_add_virtio_devices(rproc);
 }
 
@@ -1105,6 +1144,9 @@ void rproc_shutdown(struct rproc *rproc)
 
        rproc_disable_iommu(rproc);
 
+       /* Give the next start a clean resource table */
+       rproc->table_ptr = rproc->cached_table;
+
        /* if in crash state, unlock crash handler */
        if (rproc->state == RPROC_CRASHED)
                complete_all(&rproc->crash_comp);
@@ -1196,11 +1238,11 @@ static struct device_type rproc_type = {
  * @dev: the underlying device
  * @name: name of this remote processor
  * @ops: platform-specific handlers (mainly start/stop)
- * @firmware: name of firmware file to load
+ * @firmware: name of firmware file to load, can be NULL
  * @len: length of private data needed by the rproc driver (in bytes)
  *
  * Allocates a new remote processor handle, but does not register
- * it yet.
+ * it yet. if @firmware is NULL, a default name is used.
  *
  * This function should be used by rproc implementations during initialization
  * of the remote processor.
@@ -1219,19 +1261,39 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
                                const char *firmware, int len)
 {
        struct rproc *rproc;
+       char *p, *template = "rproc-%s-fw";
+       int name_len = 0;
 
        if (!dev || !name || !ops)
                return NULL;
 
-       rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
+       if (!firmware)
+               /*
+                * Make room for default firmware name (minus %s plus '\0').
+                * If the caller didn't pass in a firmware name then
+                * construct a default name.  We're already glomming 'len'
+                * bytes onto the end of the struct rproc allocation, so do
+                * a few more for the default firmware name (but only if
+                * the caller doesn't pass one).
+                */
+               name_len = strlen(name) + strlen(template) - 2 + 1;
+
+       rproc = kzalloc(sizeof(struct rproc) + len + name_len, GFP_KERNEL);
        if (!rproc) {
                dev_err(dev, "%s: kzalloc failed\n", __func__);
                return NULL;
        }
 
+       if (!firmware) {
+               p = (char *)rproc + sizeof(struct rproc) + len;
+               snprintf(p, name_len, template, name);
+       } else {
+               p = (char *)firmware;
+       }
+
+       rproc->firmware = p;
        rproc->name = name;
        rproc->ops = ops;
-       rproc->firmware = firmware;
        rproc->priv = &rproc[1];
 
        device_initialize(&rproc->dev);
@@ -1315,6 +1377,9 @@ int rproc_del(struct rproc *rproc)
        list_for_each_entry_safe(rvdev, tmp, &rproc->rvdevs, node)
                rproc_remove_virtio_dev(rvdev);
 
+       /* Free the copy of the resource table */
+       kfree(rproc->cached_table);
+
        device_del(&rproc->dev);
 
        return 0;
index 0d36f94ab51defdd1adea3c1d42b58366ac02f21..ce283a5b42a1e2b677133b9ee2d6284fb80625a7 100644 (file)
@@ -208,41 +208,22 @@ rproc_elf_load_segments(struct rproc *rproc, const struct firmware *fw)
        return ret;
 }
 
-/**
- * rproc_elf_find_rsc_table() - find the resource table
- * @rproc: the rproc handle
- * @fw: the ELF firmware image
- * @tablesz: place holder for providing back the table size
- *
- * This function finds the resource table inside the remote processor's
- * firmware. It is used both upon the registration of @rproc (in order
- * to look for and register the supported virito devices), and when the
- * @rproc is booted.
- *
- * Returns the pointer to the resource table if it is found, and write its
- * size into @tablesz. If a valid table isn't found, NULL is returned
- * (and @tablesz isn't set).
- */
-static struct resource_table *
-rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
-                                                       int *tablesz)
+static struct elf32_shdr *
+find_table(struct device *dev, struct elf32_hdr *ehdr, size_t fw_size)
 {
-       struct elf32_hdr *ehdr;
        struct elf32_shdr *shdr;
+       int i;
        const char *name_table;
-       struct device *dev = &rproc->dev;
        struct resource_table *table = NULL;
-       int i;
-       const u8 *elf_data = fw->data;
+       const u8 *elf_data = (void *)ehdr;
 
-       ehdr = (struct elf32_hdr *)elf_data;
+       /* look for the resource table and handle it */
        shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
        name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
 
-       /* look for the resource table and handle it */
        for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
-               int size = shdr->sh_size;
-               int offset = shdr->sh_offset;
+               u32 size = shdr->sh_size;
+               u32 offset = shdr->sh_offset;
 
                if (strcmp(name_table + shdr->sh_name, ".resource_table"))
                        continue;
@@ -250,7 +231,7 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
                table = (struct resource_table *)(elf_data + offset);
 
                /* make sure we have the entire table */
-               if (offset + size > fw->size) {
+               if (offset + size > fw_size || offset + size < size) {
                        dev_err(dev, "resource table truncated\n");
                        return NULL;
                }
@@ -280,16 +261,77 @@ rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
                        return NULL;
                }
 
-               *tablesz = shdr->sh_size;
-               break;
+               return shdr;
        }
 
+       return NULL;
+}
+
+/**
+ * rproc_elf_find_rsc_table() - find the resource table
+ * @rproc: the rproc handle
+ * @fw: the ELF firmware image
+ * @tablesz: place holder for providing back the table size
+ *
+ * This function finds the resource table inside the remote processor's
+ * firmware. It is used both upon the registration of @rproc (in order
+ * to look for and register the supported virito devices), and when the
+ * @rproc is booted.
+ *
+ * Returns the pointer to the resource table if it is found, and write its
+ * size into @tablesz. If a valid table isn't found, NULL is returned
+ * (and @tablesz isn't set).
+ */
+static struct resource_table *
+rproc_elf_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
+                        int *tablesz)
+{
+       struct elf32_hdr *ehdr;
+       struct elf32_shdr *shdr;
+       struct device *dev = &rproc->dev;
+       struct resource_table *table = NULL;
+       const u8 *elf_data = fw->data;
+
+       ehdr = (struct elf32_hdr *)elf_data;
+
+       shdr = find_table(dev, ehdr, fw->size);
+       if (!shdr)
+               return NULL;
+
+       table = (struct resource_table *)(elf_data + shdr->sh_offset);
+       *tablesz = shdr->sh_size;
+
        return table;
 }
 
+/**
+ * rproc_elf_find_loaded_rsc_table() - find the loaded resource table
+ * @rproc: the rproc handle
+ * @fw: the ELF firmware image
+ *
+ * This function finds the location of the loaded resource table. Don't
+ * call this function if the table wasn't loaded yet - it's a bug if you do.
+ *
+ * Returns the pointer to the resource table if it is found or NULL otherwise.
+ * If the table wasn't loaded yet the result is unspecified.
+ */
+static struct resource_table *
+rproc_elf_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
+{
+       struct elf32_hdr *ehdr = (struct elf32_hdr *)fw->data;
+       struct elf32_shdr *shdr;
+
+       shdr = find_table(&rproc->dev, ehdr, fw->size);
+       if (!shdr)
+               return NULL;
+
+       return rproc_da_to_va(rproc, shdr->sh_addr, shdr->sh_size);
+}
+
 const struct rproc_fw_ops rproc_elf_fw_ops = {
        .load = rproc_elf_load_segments,
        .find_rsc_table = rproc_elf_find_rsc_table,
+       .find_loaded_rsc_table = rproc_elf_find_loaded_rsc_table,
        .sanity_check = rproc_elf_sanity_check,
        .get_boot_addr = rproc_elf_get_boot_addr
 };
index 7bb66482d061d33e41cd9f4e69fb97fe0fa91655..157e762c15714d3c03778edd6ae3df8f68a31bab 100644 (file)
@@ -27,7 +27,8 @@ struct rproc;
 
 /**
  * struct rproc_fw_ops - firmware format specific operations.
- * @find_rsc_table:    finds the resource table inside the firmware image
+ * @find_rsc_table:    find the resource table inside the firmware image
+ * @find_loaded_rsc_table: find the loaded resouce table
  * @load:              load firmeware to memory, where the remote processor
  *                     expects to find it
  * @sanity_check:      sanity check the fw image
@@ -37,6 +38,8 @@ struct rproc_fw_ops {
        struct resource_table *(*find_rsc_table) (struct rproc *rproc,
                                                const struct firmware *fw,
                                                int *tablesz);
+       struct resource_table *(*find_loaded_rsc_table)(struct rproc *rproc,
+                                               const struct firmware *fw);
        int (*load)(struct rproc *rproc, const struct firmware *fw);
        int (*sanity_check)(struct rproc *rproc, const struct firmware *fw);
        u32 (*get_boot_addr)(struct rproc *rproc, const struct firmware *fw);
@@ -102,6 +105,16 @@ struct resource_table *rproc_find_rsc_table(struct rproc *rproc,
        return NULL;
 }
 
+static inline
+struct resource_table *rproc_find_loaded_rsc_table(struct rproc *rproc,
+                                const struct firmware *fw)
+{
+       if (rproc->fw_ops->find_loaded_rsc_table)
+               return rproc->fw_ops->find_loaded_rsc_table(rproc, fw);
+
+        return NULL;
+}
+
 extern const struct rproc_fw_ops rproc_elf_fw_ops;
 
 #endif /* REMOTEPROC_INTERNAL_H */
index afed9b7731c456b43551e999d97f054e13b31032..b09c75c21b609c51569e4088d8aba51769bec8a4 100644 (file)
@@ -173,25 +173,35 @@ error:
        return ret;
 }
 
-/*
- * We don't support yet real virtio status semantics.
- *
- * The plan is to provide this via the VDEV resource entry
- * which is part of the firmware: this way the remote processor
- * will be able to access the status values as set by us.
- */
 static u8 rproc_virtio_get_status(struct virtio_device *vdev)
 {
-       return 0;
+       struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       struct fw_rsc_vdev *rsc;
+
+       rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+
+       return rsc->status;
 }
 
 static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
 {
+       struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       struct fw_rsc_vdev *rsc;
+
+       rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+
+       rsc->status = status;
        dev_dbg(&vdev->dev, "status: %d\n", status);
 }
 
 static void rproc_virtio_reset(struct virtio_device *vdev)
 {
+       struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       struct fw_rsc_vdev *rsc;
+
+       rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+
+       rsc->status = 0;
        dev_dbg(&vdev->dev, "reset !\n");
 }
 
@@ -199,13 +209,19 @@ static void rproc_virtio_reset(struct virtio_device *vdev)
 static u32 rproc_virtio_get_features(struct virtio_device *vdev)
 {
        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       struct fw_rsc_vdev *rsc;
+
+       rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 
-       return rvdev->dfeatures;
+       return rsc->dfeatures;
 }
 
 static void rproc_virtio_finalize_features(struct virtio_device *vdev)
 {
        struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       struct fw_rsc_vdev *rsc;
+
+       rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
 
        /* Give virtio_ring a chance to accept features */
        vring_transport_features(vdev);
@@ -213,13 +229,44 @@ static void rproc_virtio_finalize_features(struct virtio_device *vdev)
        /*
         * Remember the finalized features of our vdev, and provide it
         * to the remote processor once it is powered on.
-        *
-        * Similarly to the status field, we don't expose yet the negotiated
-        * features to the remote processors at this point. This will be
-        * fixed as part of a small resource table overhaul and then an
-        * extension of the virtio resource entries.
         */
-       rvdev->gfeatures = vdev->features[0];
+       rsc->gfeatures = vdev->features[0];
+}
+
+static void rproc_virtio_get(struct virtio_device *vdev, unsigned offset,
+                                                       void *buf, unsigned len)
+{
+       struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       struct fw_rsc_vdev *rsc;
+       void *cfg;
+
+       rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+       cfg = &rsc->vring[rsc->num_of_vrings];
+
+       if (offset + len > rsc->config_len || offset + len < len) {
+               dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
+               return;
+       }
+
+       memcpy(buf, cfg + offset, len);
+}
+
+static void rproc_virtio_set(struct virtio_device *vdev, unsigned offset,
+                     const void *buf, unsigned len)
+{
+       struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
+       struct fw_rsc_vdev *rsc;
+       void *cfg;
+
+       rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
+       cfg = &rsc->vring[rsc->num_of_vrings];
+
+       if (offset + len > rsc->config_len || offset + len < len) {
+               dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
+               return;
+       }
+
+       memcpy(cfg + offset, buf, len);
 }
 
 static const struct virtio_config_ops rproc_virtio_config_ops = {
@@ -230,6 +277,8 @@ static const struct virtio_config_ops rproc_virtio_config_ops = {
        .reset          = rproc_virtio_reset,
        .set_status     = rproc_virtio_set_status,
        .get_status     = rproc_virtio_get_status,
+       .get            = rproc_virtio_get,
+       .set            = rproc_virtio_set,
 };
 
 /*
index fb95c4220052decb09117075d3a798dbbb6235b7..1ec39a4c0b3edbd9a6c64ef95e22649ab6ca7de7 100644 (file)
@@ -64,26 +64,18 @@ static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw)
 }
 
 /* Find the entry for resource table in the Table of Content */
-static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw)
+static const struct ste_toc_entry *sproc_find_rsc_entry(const void *data)
 {
        int i;
-       struct ste_toc *toc;
-
-       if (!fw)
-               return NULL;
-
-       toc = (void *)fw->data;
+       const struct ste_toc *toc;
+       toc = data;
 
        /* Search the table for the resource table */
        for (i = 0; i < SPROC_MAX_TOC_ENTRIES &&
                    toc->table[i].start != 0xffffffff; i++) {
-
                if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME,
-                            sizeof(toc->table[i].name))) {
-                       if (toc->table[i].start > fw->size)
-                               return NULL;
+                            sizeof(toc->table[i].name)))
                        return &toc->table[i];
-               }
        }
 
        return NULL;
@@ -96,9 +88,12 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
 {
        struct sproc *sproc = rproc->priv;
        struct resource_table *table;
-       struct ste_toc_entry *entry;
+       const struct ste_toc_entry *entry;
 
-       entry = sproc_find_rsc_entry(fw);
+       if (!fw)
+               return NULL;
+
+       entry = sproc_find_rsc_entry(fw->data);
        if (!entry) {
                sproc_err(sproc, "resource table not found in fw\n");
                return NULL;
@@ -149,10 +144,30 @@ sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
        return table;
 }
 
+/* Find the resource table inside the remote processor's firmware. */
+static struct resource_table *
+sproc_find_loaded_rsc_table(struct rproc *rproc, const struct firmware *fw)
+{
+       struct sproc *sproc = rproc->priv;
+       const struct ste_toc_entry *entry;
+
+       if (!fw || !sproc->fw_addr)
+               return NULL;
+
+       entry = sproc_find_rsc_entry(sproc->fw_addr);
+       if (!entry) {
+               sproc_err(sproc, "resource table not found in fw\n");
+               return NULL;
+       }
+
+       return sproc->fw_addr + entry->start;
+}
+
 /* STE modem firmware handler operations */
 const struct rproc_fw_ops sproc_fw_ops = {
        .load = sproc_load_segments,
        .find_rsc_table = sproc_find_rsc_table,
+       .find_loaded_rsc_table = sproc_find_loaded_rsc_table,
 };
 
 /* Kick the modem with specified notification id */
@@ -198,7 +213,7 @@ static int sproc_start(struct rproc *rproc)
        }
 
        /* Subscribe to notifications */
-       for (i = 0; i < rproc->max_notifyid; i++) {
+       for (i = 0; i <= rproc->max_notifyid; i++) {
                err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i);
                if (err) {
                        sproc_err(sproc,
index faf33324c78f9613526a66474670303ae5661aab..9e7e745dac55a64ce513d2e4807bc045aa9a8b14 100644 (file)
@@ -401,6 +401,9 @@ enum rproc_crash_type {
  * @crash_comp: completion used to sync crash handler and the rproc reload
  * @recovery_disabled: flag that state if recovery was disabled
  * @max_notifyid: largest allocated notify id.
+ * @table_ptr: pointer to the resource table in effect
+ * @cached_table: copy of the resource table
+ * @table_csum: checksum of the resource table
  */
 struct rproc {
        struct klist_node node;
@@ -429,9 +432,13 @@ struct rproc {
        struct completion crash_comp;
        bool recovery_disabled;
        int max_notifyid;
+       struct resource_table *table_ptr;
+       struct resource_table *cached_table;
+       u32 table_csum;
 };
 
 /* we currently support only two vrings per rvdev */
+
 #define RVDEV_NUM_VRINGS 2
 
 /**
@@ -462,16 +469,14 @@ struct rproc_vring {
  * @rproc: the rproc handle
  * @vdev: the virio device
  * @vring: the vrings for this vdev
- * @dfeatures: virtio device features
- * @gfeatures: virtio guest features
+ * @rsc_offset: offset of the vdev's resource entry
  */
 struct rproc_vdev {
        struct list_head node;
        struct rproc *rproc;
        struct virtio_device vdev;
        struct rproc_vring vring[RVDEV_NUM_VRINGS];
-       unsigned long dfeatures;
-       unsigned long gfeatures;
+       u32 rsc_offset;
 };
 
 struct rproc *rproc_alloc(struct device *dev, const char *name,