From: Wei Chen <[email protected]>

In CSRaltas7, Cortex-A7 uses this proc to communicate with Cortex-M3.
But M3 doesn't have to be a slave, it can boot indenpently or depend
on Linux to load firmware for it.

we reserve a memory for data and resource descriptors in DRAM.

Signed-off-by: Wei Chen <[email protected]>
Signed-off-by: Barry Song <[email protected]>
---
 .../bindings/remoteproc/sirf,remoteproc.txt        |  33 ++
 drivers/remoteproc/Kconfig                         |  13 +
 drivers/remoteproc/Makefile                        |   1 +
 drivers/remoteproc/sirf_remoteproc.c               | 467 +++++++++++++++++++++
 4 files changed, 514 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/remoteproc/sirf,remoteproc.txt
 create mode 100644 drivers/remoteproc/sirf_remoteproc.c

diff --git a/Documentation/devicetree/bindings/remoteproc/sirf,remoteproc.txt 
b/Documentation/devicetree/bindings/remoteproc/sirf,remoteproc.txt
new file mode 100644
index 0000000..409fb40
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/sirf,remoteproc.txt
@@ -0,0 +1,33 @@
+SIRF Atlas7 Remote processor Device Binding
+------------------------------------------------
+1) Main node
+       Required properties :
+
+       - compatible : "sirf,atlas7-rproc"
+
+       - reg : register address of remoteproc device
+
+       - interrupts: the irq number this rproc need to handle.
+
+       - hwlocks: the hwlocks this rproc to used to protect data
+         between two processors.
+
+       - memory-region: the memory region, which is used to store virtual
+         device info, fifo buffers and share memory between two processors.
+
+       - firmware: the firmware file that will be loaded to remote processor.
+
+Please refer to ../reserved-memory/reserved-memory.txt for details of the
+memory-region bindings.
+Please refer to ../hwlock/hwlock.txt for details of the hwlock bindings.
+
+2) Example:
+                       ns_m3_rproc@0 {
+                               compatible = "sirf,atlas7-rproc";
+                               reg = <0x13240108 0x4>,
+                                       <0x13240208 0x4>;
+                               interrupts = <0 123 0>;
+                               hwlocks = <&hwlock 0>, <&hwlock 1>;
+                               memory-region = <&ipc_mem0>;
+                               firmware = "RTOSDemo.bin";
+                       };
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 28c711f..aeabbfa 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -77,4 +77,17 @@ config DA8XX_REMOTEPROC
          It's safe to say n here if you're not interested in multimedia
          offloading.
 
+config SIRF_REMOTEPROC
+       tristate "CSR atals7 remoteproc support"
+       depends on ARCH_ATLAS7
+       select REMOTEPROC
+       select RPMSG
+       default y
+       help
+         Say y or m here to support CSR Atlas7 Inter-Processors
+         Communication driver via remote processor framework.
+
+         This can be either built-in or a loadable module.
+         If unsure say N.
+
 endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 81b04d1..8cc4790 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_OMAP_REMOTEPROC)         += omap_remoteproc.o
 obj-$(CONFIG_STE_MODEM_RPROC)          += ste_modem_rproc.o
 obj-$(CONFIG_WKUP_M3_RPROC)            += wkup_m3_rproc.o
 obj-$(CONFIG_DA8XX_REMOTEPROC)         += da8xx_remoteproc.o
+obj-$(CONFIG_SIRF_REMOTEPROC)  += sirf_remoteproc.o
diff --git a/drivers/remoteproc/sirf_remoteproc.c 
b/drivers/remoteproc/sirf_remoteproc.c
new file mode 100644
index 0000000..cb7568d
--- /dev/null
+++ b/drivers/remoteproc/sirf_remoteproc.c
@@ -0,0 +1,467 @@
+/*
+ * SIRF Remote processor machine-specific module
+ *
+ * Copyright (c) 2014 Cambridge Silicon Radio Limited, a CSR plc group company.
+ *
+ * Licensed under GPLv2 or later.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/interrupt.h>
+#include <linux/hwspinlock.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_device.h>
+#include <linux/remoteproc.h>
+
+#include "remoteproc_internal.h"
+
+struct fifo_buffer {
+       struct hwspinlock *lock;
+       unsigned char *buffer;
+       u32 w_pos;
+       u32 r_pos;
+       u32 size;
+       u32 *count; /* pointer to shared memory */
+};
+
+static int fifo_write(struct fifo_buffer *fifo,
+               const void *data, u32 len)
+{
+       int err;
+       u32 overflow, count;
+       ulong flags;
+
+       err = hwspin_lock_timeout_irqsave(fifo->lock, 100, &flags);
+       if (err) {
+               pr_err("%s, Get hwspinlock failed!err= %d\n",
+                       __func__, err);
+               WARN_ON(err);
+               return -EBUSY;
+       }
+
+       if (len > fifo->size) {
+               err = -EFBIG;
+               goto err_exit;
+       }
+
+       count = *fifo->count;
+       overflow = len > (fifo->size - count);
+       if (overflow) {
+               /* previous data hasn't been read, FIFO busy */
+               err = -EBUSY;
+               goto err_exit;
+       }
+
+       /* copy data to fifo buffer */
+       memcpy(fifo->buffer + fifo->w_pos, data, len);
+       /* update fifo position */
+       fifo->w_pos = (fifo->w_pos + len) % fifo->size;
+       *fifo->count = count + len;
+
+       hwspin_unlock_irqrestore(fifo->lock, &flags);
+       err = 0;
+
+err_exit:
+       return err;
+}
+
+static int fifo_read(struct fifo_buffer *fifo,
+               void *data, u32 len)
+{
+       int err;
+       u32 count;
+
+       err = hwspin_lock_timeout(fifo->lock, 100);
+       if (err) {
+               pr_err("%s, Get hwspinlock failed!err= %d\n",
+                       __func__, err);
+                       WARN_ON(err);
+               return err;
+       }
+
+       count = *fifo->count;
+       if (!count) {
+               err = -ENOSPC;
+               goto err_exit;
+       }
+
+       if (len > count)
+               len = count;
+
+       /* copy data from fifo buffer */
+       memcpy(data, fifo->buffer + fifo->r_pos, len);
+       /* update fifo position */
+       fifo->r_pos = (fifo->r_pos + len) % fifo->size;
+       *fifo->count = count - len;
+
+       err = 0;
+
+err_exit:
+       hwspin_unlock(fifo->lock);
+
+       return err;
+}
+
+static int fifo_init(struct fifo_buffer *fifo, void *buffer,
+                       int size, int hwlock_id)
+{
+
+       fifo->lock = hwspin_lock_request_specific(hwlock_id);
+       if (!fifo->lock) {
+               pr_err("%s:Could not request specific hwspin lock!\n",
+                       __func__);
+               return -ENODEV;
+       }
+
+       fifo->count = (u32 *)buffer;
+       fifo->buffer = (unsigned char *)(buffer + sizeof(u32));
+       fifo->w_pos = 0;
+       fifo->r_pos = 0;
+       fifo->size = size - sizeof(u32);
+       *fifo->count = 0;
+
+       return 0;
+}
+
+/* FIFO shared memory has been divide into 2 logical channels */
+#define FIFO_LOGIC_CHN_0       0
+#define FIFO_LOGIC_CHN_1       1
+
+#define RPROC_DEF_FIFO_SIZE    0x1000
+
+/**
+ * struct sirf_rproc - SIRF remote processor instance state
+ * @rproc: rproc handle
+ * @rsc_dma: the dma address of the resource memory, include fifo.
+ * @rsc_size: resource memory size.
+ * @table_ptr: the virtual address of rproc resource table area.
+ * @table_len: the length of rproc resource table.
+ * @fifo_rx_lock: lock for fifo receive data.
+ * @fifo_tx_lock: lock for fifo send data.
+ * @tx_avail_wq: wait queue of send data when fifo is busy.
+ * @fifo_avail: fifo status for send data.
+ *             fifo can send data when fifo_avail is true.
+ * @fifo_msg_rx: memory address for fifo arrived data.
+ * @fifo_msg_tx: memory address for fifo send data.
+ * @fifo_iomemmem: iomem address for fifo register.
+ * @irq: the irq number of fifo allocated in backend OS.
+ * @irq_gen_count: generate IRQ counter for statistic
+ * @irq_get_count: arrive IRQ counter for statistic
+ */
+struct sirf_rproc {
+       struct rproc *rproc;
+       void *rsc_dma;
+       size_t rsc_size;
+       struct resource_table *table_ptr;
+       u32 table_len;
+       void __iomem *set_reg;
+       void __iomem *clr_reg;
+       struct fifo_buffer w_fifo;
+       struct fifo_buffer r_fifo;
+       u32 w_fifo_hwlock;
+       u32 r_fifo_hwlock;
+       int irq;
+};
+
+/* Interrupt handler for IRQs from remote processor */
+static irqreturn_t sirf_rproc_ipc_isr(int irq, void *data)
+{
+       struct rproc *rproc = (struct rproc *)data;
+       struct sirf_rproc *srproc = (struct sirf_rproc *)rproc->priv;
+       u32 notifyid;
+       int err;
+
+       /* clear interrupt */
+       readl(srproc->clr_reg);
+
+       do {
+               err = fifo_read(&srproc->r_fifo, &notifyid, sizeof(notifyid));
+               if (err)
+                       break;
+               rproc_vq_interrupt(rproc, notifyid);
+       } while (1);
+
+       return IRQ_HANDLED;
+}
+
+static void sirf_rproc_kick(struct rproc *rproc, int notify_id)
+{
+       struct sirf_rproc *srproc = rproc->priv;
+       int ret;
+
+       ret = fifo_write(&srproc->w_fifo, &notify_id, sizeof(notify_id));
+       if (ret) {
+               dev_err(&rproc->dev,
+                       "%s could not completed, err=%d\n",
+                       __func__, ret);
+               WARN_ON(1);
+       }
+
+       /*
+        * Trigger interrupt to ask remote side to get new added data
+        * or handle the data already in the FIFO as fast as possible.
+        */
+       smp_mb();
+
+       writel(0x01, srproc->set_reg);
+}
+
+static const struct of_device_id sirf_rproc_dt_ids[] = {
+       { .compatible = "sirf,atlas7-rproc", },
+       {},
+};
+
+static struct rproc_ops sirf_rproc_ops = {
+       .kick = sirf_rproc_kick,
+};
+
+static struct resource_table *
+srproc_fw_find_rsc_table(struct rproc *rproc,
+                               const struct firmware *fw,
+                               int *tablesz)
+{
+       struct sirf_rproc *srproc = (struct sirf_rproc *)rproc->priv;
+
+       *tablesz = srproc->table_len;
+       return srproc->table_ptr;
+}
+
+struct rproc_fw_ops sirf_rproc_fw_ops = {
+       .find_rsc_table = srproc_fw_find_rsc_table,
+};
+
+static int sirf_rproc_parse_memory(struct platform_device *pdev,
+                               struct sirf_rproc *srproc)
+{
+       struct device_node *m_node;
+       struct resource res;
+       void *rsc_addr;
+       size_t rsc_size;
+       int ret;
+
+       m_node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);
+       if (!m_node)
+               return -ENODEV;
+
+       ret = of_address_to_resource(m_node, 0, &res);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Convert address to resource failed! ret=%d\n",
+                       ret);
+               return ret;
+       }
+
+       rsc_addr = (void *)__phys_to_virt(res.start);
+       rsc_size = res.end - res.start + 1;
+
+       /* create a coherent mapping */
+       srproc->rsc_dma = dma_common_contiguous_remap(virt_to_page(rsc_addr),
+                               rsc_size, VM_IO,
+                               pgprot_dmacoherent(PAGE_KERNEL),
+                               NULL);
+       if (!srproc->rsc_dma)
+               return -ENOMEM;
+
+       srproc->rsc_size = rsc_size;
+
+       return 0;
+}
+
+static int sirf_rproc_parse_args(struct platform_device *pdev,
+                               struct sirf_rproc *srproc)
+{
+       void *tx_buffer, *rx_buffer;
+       struct resource *res;
+       int ret;
+
+       /* retrieve trigger interrupt io base */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       srproc->set_reg = devm_ioremap_resource(&pdev->dev, res);
+       if (!srproc->set_reg) {
+               dev_err(&pdev->dev,
+                       "Unable to map rproc trigger interrupt registers!\n");
+               return -ENOMEM;
+       }
+
+       /* retrieve clear interrupt io base */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       srproc->clr_reg = devm_ioremap_resource(&pdev->dev, res);
+       if (!srproc->clr_reg) {
+               dev_err(&pdev->dev,
+                       "Unable to map rproc clear interrupt registers!\n");
+               return -ENOMEM;
+       }
+
+       ret = of_irq_get(pdev->dev.of_node, 0);
+       if (ret == -EPROBE_DEFER) {
+               dev_err(&pdev->dev,
+                       "Unable to find IRQ number. ret=%d\n", ret);
+               return ret;
+       }
+       srproc->irq = ret;
+
+       /* Request hwlocks for rproc */
+       srproc->w_fifo_hwlock = of_hwspin_lock_get_id(pdev->dev.of_node, 0);
+       if (srproc->w_fifo_hwlock < 0) {
+               ret = srproc->w_fifo_hwlock;
+               dev_err(&pdev->dev,
+                       "Unable to get hwlock for write fifo. ret=%d\n", ret);
+               goto failed;
+       }
+
+       srproc->r_fifo_hwlock = of_hwspin_lock_get_id(pdev->dev.of_node, 1);
+       if (srproc->r_fifo_hwlock < 0) {
+               ret = srproc->r_fifo_hwlock;
+               dev_err(&pdev->dev,
+                       "Unable to get hwlock for read fifo. ret=%d\n", ret);
+               goto failed;
+       }
+
+       /* Parse share memory information */
+       ret = sirf_rproc_parse_memory(pdev, srproc);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "Unable to setup ipc share memory info. ret=%d\n",
+                       ret);
+               goto failed;
+       }
+       srproc->table_ptr = srproc->rsc_dma;
+
+       /* check resource table size */
+       if (RPROC_DEF_FIFO_SIZE * 2 >= srproc->rsc_size) {
+               dev_err(&pdev->dev,
+                       "There is no memory left for resource table!\n");
+               ret = -EINVAL;
+               goto free_rsc;
+       }
+
+       if (srproc->table_ptr->ver != 1) {
+               dev_err(&pdev->dev,
+                       "unsupported fw ver: %d\n",
+                       srproc->table_ptr->ver);
+               ret = -EINVAL;
+               goto free_rsc;
+       }
+
+       srproc->table_len = srproc->rsc_size - RPROC_DEF_FIFO_SIZE * 2;
+       tx_buffer = srproc->rsc_dma + srproc->table_len +
+               RPROC_DEF_FIFO_SIZE * FIFO_LOGIC_CHN_0;
+       rx_buffer = srproc->rsc_dma + srproc->table_len +
+               RPROC_DEF_FIFO_SIZE * FIFO_LOGIC_CHN_1;
+
+       ret = fifo_init(&srproc->w_fifo, tx_buffer,
+                       RPROC_DEF_FIFO_SIZE, srproc->w_fifo_hwlock);
+       if (ret)
+               goto free_rsc;
+
+       ret = fifo_init(&srproc->r_fifo, rx_buffer,
+                       RPROC_DEF_FIFO_SIZE, srproc->r_fifo_hwlock);
+       if (ret)
+               goto free_rsc;
+
+       return 0;
+
+free_rsc:
+       dma_common_free_remap(srproc->rsc_dma,
+                       srproc->rsc_size, VM_IO);
+       srproc->table_ptr = NULL;
+       srproc->rsc_dma = NULL;
+
+failed:
+       return ret;
+}
+
+static int sirf_rproc_remove(struct platform_device *pdev)
+{
+       struct rproc *rproc = platform_get_drvdata(pdev);
+       struct sirf_rproc *srproc = rproc->priv;
+
+       dma_common_free_remap(srproc->rsc_dma,
+                               srproc->rsc_size, VM_IO);
+
+       rproc->table_ptr = 0;
+
+       rproc_del(rproc);
+       rproc_put(rproc);
+
+       return 0;
+}
+
+static int sirf_rproc_probe(struct platform_device *pdev)
+{
+       struct sirf_rproc *srproc;
+       struct rproc *rproc;
+       const char *fw;
+       int ret;
+
+       ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+       if (ret) {
+               dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret);
+               return ret;
+       }
+
+       ret = of_property_read_string(pdev->dev.of_node, "firmware", &fw);
+       if (ret)
+               fw = NULL; /* Set to NULL, rproc core will use default name */
+
+       rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev), &sirf_rproc_ops,
+                               fw, sizeof(*srproc));
+       if (!rproc)
+               return -ENOMEM;
+
+       srproc = rproc->priv;
+       srproc->rproc = rproc;
+       /* Setup sirf rproc firmware ops */
+       rproc->fw_ops = &sirf_rproc_fw_ops;
+       /* This rproc is always on */
+       rproc->state = RPROC_ALWAYS_ON;
+
+       ret = sirf_rproc_parse_args(pdev, srproc);
+       if (ret)
+               goto free_rproc;
+
+       ret = devm_request_threaded_irq(&rproc->dev, srproc->irq,
+                               NULL, sirf_rproc_ipc_isr,
+                               IRQF_ONESHOT,
+                               dev_name(&pdev->dev), rproc);
+       if (ret) {
+               dev_err(&rproc->dev,
+                       "request_threaded_irq %d error: %d\n",
+                       srproc->irq, ret);
+               goto free_rproc;
+       }
+
+       ret = rproc_add(rproc);
+       if (ret) {
+               dev_err(&rproc->dev, "rproc_add failed: %d\n", ret);
+               goto free_rproc;
+       }
+
+       platform_set_drvdata(pdev, rproc);
+
+       return 0;
+
+free_rproc:
+       rproc_put(rproc);
+
+       return ret;
+}
+
+static struct platform_driver sirf_rproc_driver = {
+       .probe = sirf_rproc_probe,
+       .remove = sirf_rproc_remove,
+       .driver = {
+               .name = "sirfsoc_remoteproc",
+               .of_match_table = of_match_ptr(sirf_rproc_dt_ids),
+       },
+};
+module_platform_driver(sirf_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("SIRF Remote Processor driver");
-- 
1.9.1

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to