From: Wei Chen <Wei.Chen@xxxxxxx> 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 <Wei.Chen@xxxxxxx> Signed-off-by: Barry Song <Baohua.Song@xxxxxxx> --- .../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, ¬ifyid, 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, ¬ify_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 majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html