2015-07-15 12:46:56

by Wendy Liang

[permalink] [raw]
Subject: [RFC PATCH 0/2] remoteproc: Add zynqmp R5 remoteproc driver

This patch series is to add remoteproc driver for Cortex-R5
included in Xilinx Zynq UltraScale MPSoC so that we can
boot and passing messages to the R5 subsystem.

Wendy Liang (2):
remoteproc: Add zynqmp R5 remoteproc driver
Documentation: DTS: bindings: add ZynqMP RPU remoteproc

.../bindings/remoteproc/zynqmp_r5_remoteproc.txt | 49 ++
drivers/remoteproc/Kconfig | 9 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/zynqmp_r5_remoteproc.c | 809 +++++++++++++++++++++
4 files changed, 868 insertions(+)
create mode 100644 Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
create mode 100644 drivers/remoteproc/zynqmp_r5_remoteproc.c

--
2.1.1


2015-07-15 12:39:59

by Wendy Liang

[permalink] [raw]
Subject: [RFC PATCH 1/2] remoteproc: Add zynqmp R5 remoteproc driver

R5 is included in Xilinx Zynq UltraScale MPSoC so by adding this
remotproc driver, we can boot the R5 sub-system in different
configurations.

Currently this driver only supports direct HW access with place holder
for SMC and HVC implementations which will be added later.

Signed-off-by: Jason Wu <[email protected]>
Signed-off-by: Edgar E. Iglesias <[email protected]>
Signed-off-by: Wendy Liang <[email protected]>
---
drivers/remoteproc/Kconfig | 9 +
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/zynqmp_r5_remoteproc.c | 809 ++++++++++++++++++++++++++++++
3 files changed, 819 insertions(+)
create mode 100644 drivers/remoteproc/zynqmp_r5_remoteproc.c

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 28c711f..737c8e7 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -77,4 +77,13 @@ config DA8XX_REMOTEPROC
It's safe to say n here if you're not interested in multimedia
offloading.

+config ZYNQMP_R5_REMOTEPROC
+ tristate "ZynqMP_r5 remoteproc support"
+ depends on ARM64
+ select REMOTEPROC
+ select RPMSG
+ help
+ Say y here to support ZynqMP R5 remote processors via the remote
+ processor framework.
+
endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 81b04d1..489963b 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_ZYNQMP_R5_REMOTEPROC) += zynqmp_r5_remoteproc.o
diff --git a/drivers/remoteproc/zynqmp_r5_remoteproc.c b/drivers/remoteproc/zynqmp_r5_remoteproc.c
new file mode 100644
index 0000000..24948af
--- /dev/null
+++ b/drivers/remoteproc/zynqmp_r5_remoteproc.c
@@ -0,0 +1,809 @@
+/*
+ * Zynq R5 Remote Processor driver
+ *
+ * Copyright (C) 2015 Jason Wu <[email protected]>
+ * Copyright (C) 2015 Xilinx, Inc.
+ *
+ * Based on origin OMAP and Zynq Remote Processor driver
+ *
+ * Copyright (C) 2012 Michal Simek <[email protected]>
+ * Copyright (C) 2012 PetaLogix
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/remoteproc.h>
+#include <linux/interrupt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <asm/cacheflush.h>
+#include <linux/slab.h>
+#include <linux/cpu.h>
+#include <linux/delay.h>
+
+#include "remoteproc_internal.h"
+
+/* Register offset definitions for RPU. */
+#define RPU_GLBL_CNTL_OFFSET 0x00000000 /* RPU control */
+#define RPU_0_CFG_OFFSET 0x00000100 /* RPU0 configuration */
+#define RPU_1_CFG_OFFSET 0x00000200 /* RPU1 Configuration */
+/* Boot memory bit. high for OCM, low for TCM */
+#define VINITHI_BIT BIT(2)
+/* CPU halt bit, high: processor is running. low: processor is halt */
+#define nCPUHALT_BIT BIT(0)
+/* RPU mode, high: split mode. low: lock step mode */
+#define SLSPLIT_BIT BIT(3)
+/* Clamp mode. high: split mode. low: lock step mode */
+#define SLCLAMP_BIT BIT(4)
+/* TCM mode. high: combine RPU TCMs. low: split TCM for RPU1 and RPU0 */
+#define TCM_COMB_BIT BIT(6)
+
+/* Clock controller low power domain (CRL_APB) for RPU */
+#define CPU_R5_CTRL_OFFSET 0x00000090 /* RPU Global Control*/
+#define RST_LPD_TOP_OFFSET 0x0000023C /* LPD block */
+#define RPU0_RESET_BIT BIT(0) /* RPU CPU0 reset bit */
+
+/* IPI reg offsets */
+#define TRIG_OFFSET 0x00000000
+#define OBS_OFFSET 0x00000004
+#define ISR_OFFSET 0x00000010
+#define IMR_OFFSET 0x00000014
+#define IER_OFFSET 0x00000018
+#define IDR_OFFSET 0x0000001C
+#define IPI_ALL_MASK 0x0F0F0301
+
+/* LOVEC BOOT ADDR -- TCM ADDR */
+#define TCM_0_LOVEC_ADDR 0xFFE00000
+#define TCM_1_LOVEC_ADDR 0xFFE40000
+
+/* HIVEC BOOT ADDR -- OCM ADDR */
+#define OCM_HIVEC_ADDR 0xFFFF0000
+
+#define MAX_INSTANCES 2 /* Support upto 2 RPU */
+
+#define ZYNQMP_SIP_SVC_CPU_START 0x82001004
+#define ZYNQMP_SIP_SVC_CPU_DISABLE 0x82001006
+
+/* Store rproc for IPI handler */
+static struct platform_device *remoteprocdev[MAX_INSTANCES];
+
+/* Register access macros */
+#define reg_read(base, reg) \
+ readl(((void __iomem *)(base)) + (reg))
+#define reg_write(base, reg, val) \
+ writel((val), ((void __iomem *)(base)) + (reg))
+
+#define DEFAULT_FIRMWARE_NAME "rproc-rpu-fw"
+
+/* Module parameter */
+static char *firmware;
+
+struct zynqmp_r5_rproc_pdata;
+
+/**
+ * struct ipi_ops - IPI operation handlers
+ * @clear: Clear IPI
+ * @reset: Reset IPI channel
+ * @set_mask: Destination mask
+ * @trigger: Trigger IPI
+ */
+struct ipi_ops {
+ void (*clear)(struct zynqmp_r5_rproc_pdata *pdata);
+ void (*reset)(struct zynqmp_r5_rproc_pdata *pdata);
+ void (*set_mask)(struct zynqmp_r5_rproc_pdata *pdata);
+ void (*trigger)(struct zynqmp_r5_rproc_pdata *pdata);
+};
+
+/**
+ * struct rpu_ops - RPU operation handlers
+ * @bootdev: Boot device
+ * @core_conf: Core configuration
+ * @halt: Enable/Disable halt
+ * @en_reset: Enable/Disable reset
+ */
+struct rpu_ops {
+ int (*start)(struct zynqmp_r5_rproc_pdata *pdata);
+ void (*stop)(struct zynqmp_r5_rproc_pdata *pdata);
+};
+
+/* enumerations for RPU/IPI control methods */
+enum control_method {
+ SMC = 0,
+ HVC,
+ HW,
+};
+
+/* enumerations for R5 boot device */
+enum rpu_bootmem {
+ TCM = 0,
+ OCM,
+};
+
+/* enumerations for R5 core configurations */
+enum rpu_core_conf {
+ LOCK_STEP = 0,
+ SPLIT,
+};
+
+/**
+ * struct zynqmp_r5_rproc_pdata - zynqmp rpu remote processor instance state
+ * @rproc: rproc handle
+ * @ipi_ops: IPI related operation handlers
+ * @rpu_ops: RPU related operation handlers
+ * @workqueue: workqueue for the RPU remoteproc
+ * @rpu_base: virt ptr to RPU control address registers
+ * @crl_apb_base: virt ptr to CRL_APB address registers for RPU
+ * @ipi_base: virt ptr to IPI channel address registers for APU
+ * @rpu_mode: RPU core configuration
+ * @rpu_id: RPU CPU id
+ * @bootmem: RPU boot memory device used
+ * @vring0: IRQ number used for vring0
+ * @ipi_dest_mask: IPI destination mask for the IPI channel
+ */
+struct zynqmp_r5_rproc_pdata {
+ struct rproc *rproc;
+ struct ipi_ops *ipi_ops;
+ struct rpu_ops *rpu_ops;
+ struct work_struct workqueue;
+ void __iomem *rpu_base;
+ void __iomem *crl_apb_base;
+ void __iomem *ipi_base;
+ enum rpu_core_conf rpu_mode;
+ enum rpu_bootmem bootmem;
+ u32 ipi_dest_mask;
+ u32 rpu_id;
+ u32 vring0;
+};
+
+static int get_elf_entry_addr(struct zynqmp_r5_rproc_pdata *pdata,
+ u32 *elf_entry_p)
+{
+ struct elf32_hdr *ehdr = 0;
+ const struct firmware *firmware_p;
+ struct rproc *rproc = pdata->rproc;
+ int ret;
+
+ ret = request_firmware(&firmware_p, rproc->firmware, &rproc->dev);
+ if (ret < 0) {
+ dev_err(&rproc->dev, "%s: request_firmware failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ ehdr = (struct elf32_hdr *)firmware_p->data;
+ *elf_entry_p = (unsigned int)ehdr->e_entry;
+ release_firmware(firmware_p);
+ return 0;
+}
+
+/*
+ * TODO: Update HW RPU operation when the driver is ready
+ */
+static void hw_r5_boot_dev(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ u32 tmp;
+ u32 offset = RPU_1_CFG_OFFSET;
+
+ pr_debug("%s: R5 ID: %d, boot_dev %d\n",
+ __func__, pdata->rpu_id, pdata->bootmem);
+ if (pdata->rpu_id == 0)
+ offset = RPU_0_CFG_OFFSET;
+
+ tmp = reg_read(pdata->rpu_base, offset);
+ if (pdata->bootmem == OCM)
+ tmp |= VINITHI_BIT;
+ else
+ tmp &= ~VINITHI_BIT;
+ reg_write(pdata->rpu_base, offset, tmp);
+}
+
+static void hw_r5_reset(struct zynqmp_r5_rproc_pdata *pdata,
+ bool do_reset)
+{
+ u32 tmp;
+
+ pr_debug("%s: R5 ID: %d, reset %d\n", __func__, pdata->rpu_id,
+ do_reset);
+ tmp = reg_read(pdata->crl_apb_base, RST_LPD_TOP_OFFSET);
+ if (do_reset)
+ tmp |= (RPU0_RESET_BIT << pdata->rpu_id);
+ else
+ tmp &= ~(RPU0_RESET_BIT << pdata->rpu_id);
+ reg_write(pdata->crl_apb_base, RST_LPD_TOP_OFFSET, tmp);
+}
+
+static void hw_r5_halt(struct zynqmp_r5_rproc_pdata *pdata,
+ bool do_halt)
+{
+ u32 tmp;
+ u32 offset = RPU_1_CFG_OFFSET;
+
+ pr_debug("%s: R5 ID: %d, halt %d\n", __func__, pdata->rpu_id,
+ do_halt);
+ if (pdata->rpu_id == 0)
+ offset = RPU_0_CFG_OFFSET;
+
+ tmp = reg_read(pdata->rpu_base, offset);
+ if (do_halt)
+ tmp &= ~nCPUHALT_BIT;
+ else
+ tmp |= nCPUHALT_BIT;
+ reg_write(pdata->rpu_base, offset, tmp);
+}
+
+static void hw_r5_core_config(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ u32 tmp;
+
+ pr_debug("%s: mode: %d\n", __func__, pdata->rpu_mode);
+ tmp = reg_read(pdata->rpu_base, 0);
+ if (pdata->rpu_mode == SPLIT) {
+ tmp |= SLSPLIT_BIT;
+ tmp &= ~TCM_COMB_BIT;
+ tmp &= ~SLCLAMP_BIT;
+ } else {
+ tmp &= ~SLSPLIT_BIT;
+ tmp |= TCM_COMB_BIT;
+ tmp |= SLCLAMP_BIT;
+ }
+ reg_write(pdata->rpu_base, 0, tmp);
+}
+
+static int hw_r5_core_start(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ u32 elf_entry_addr = 0;
+ u32 boot_addr = 0;
+ struct rproc *rproc = pdata->rproc;
+ void __iomem *vector_base = 0;
+ u32 cpu_cfg_offset = RPU_1_CFG_OFFSET;
+ u32 rpu_cfg_reg = 0;
+ int ret;
+ /*
+ * Boot trampoline is simple ASM code below.
+ *
+ * ldr pc, [pc, #-4] ; 4 <. +0x4>
+ */
+ unsigned int bootloader[] = {
+ 0xe51ff004,
+ 0,
+ 0xe59f0004,
+ 0xe5901000,
+ 0xe12fff11,
+ 0xe320f000,
+ };
+
+ /* Set up R5 */
+ hw_r5_core_config(pdata);
+ hw_r5_reset(pdata, true);
+ hw_r5_halt(pdata, true);
+ hw_r5_boot_dev(pdata);
+
+ /* Set up the bootloader */
+ /*
+ * Boot trampoline is simple ASM code below.
+ *
+ * ldr pc, [pc, #-4] ; 4 <. +0x4>
+ */
+ ret = get_elf_entry_addr(pdata, &elf_entry_addr);
+ if (ret < 0) {
+ dev_err(&rproc->dev, "%s: request_firmware failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ dev_dbg(&rproc->dev, "%s: elf entry: 0x%x\n", __func__, elf_entry_addr);
+ bootloader[1] = elf_entry_addr;
+ /* Get boot address.
+ * Put the loader to TCM if it is LOWVEC, otherwise, put to OCM.
+ */
+ if (pdata->rpu_id == 0)
+ cpu_cfg_offset = RPU_0_CFG_OFFSET;
+
+ rpu_cfg_reg = reg_read(pdata->rpu_base, cpu_cfg_offset);
+ if ((rpu_cfg_reg & VINITHI_BIT))
+ boot_addr = OCM_HIVEC_ADDR;
+ else if (pdata->rpu_id == 0)
+ boot_addr = TCM_0_LOVEC_ADDR;
+ else
+ boot_addr = TCM_1_LOVEC_ADDR;
+ dev_dbg(&rproc->dev, "%s: boot addr: 0x%x\n", __func__, boot_addr);
+ vector_base = ioremap(boot_addr, sizeof(bootloader));
+ memcpy_toio(vector_base, bootloader, sizeof(bootloader));
+ /* Just for sure synchronize memories */
+ __flush_dcache_area(vector_base, sizeof(bootloader));
+ udelay(500);
+
+ hw_r5_reset(pdata, false);
+ hw_r5_halt(pdata, false);
+ return 0;
+}
+
+static void hw_r5_core_stop(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ hw_r5_reset(pdata, true);
+ hw_r5_halt(pdata, true);
+}
+
+static struct rpu_ops rpu_hw_ops = {
+ .start = hw_r5_core_start,
+ .stop = hw_r5_core_stop,
+};
+
+asmlinkage extern int __invoke_psci_fn_smc(u64, u64, u64, u64);
+static int smc_r5_core_start(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: atf smc to be implemented\n", __func__);
+ return -ENODEV;
+}
+
+static void smc_r5_core_stop(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: atf smc to be implemented\n", __func__);
+}
+
+static struct rpu_ops rpu_smc_ops = {
+ .start = smc_r5_core_start,
+ .stop = smc_r5_core_stop,
+};
+
+static int hvc_r5_core_start(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: hypervisor hvc to be implemented\n", __func__);
+ return -ENODEV;
+}
+
+static void hvc_r5_core_stop(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: hypervisor hvc to be implemented\n", __func__);
+}
+
+static struct rpu_ops rpu_hvc_ops = {
+ .start = hvc_r5_core_start,
+ .stop = hvc_r5_core_stop,
+};
+
+/*
+ * TODO: Update HW ipi operation when the driver is ready
+ */
+static void hw_clear_ipi(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_debug("%s: irq issuer %08x clear IPI\n", __func__,
+ pdata->ipi_dest_mask);
+ reg_write(pdata->ipi_base, ISR_OFFSET, pdata->ipi_dest_mask);
+}
+
+static void hw_ipi_reset(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ reg_write(pdata->ipi_base, IDR_OFFSET, IPI_ALL_MASK);
+ reg_write(pdata->ipi_base, ISR_OFFSET, IPI_ALL_MASK);
+ /* add delay to allow ipi to be settle */
+ udelay(10);
+ pr_debug("IPI reset done\n");
+}
+
+static void hw_set_ipi_mask(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_debug("%s: set IPI mask %08x\n", __func__, pdata->ipi_dest_mask);
+ reg_write(pdata->ipi_base, IER_OFFSET, pdata->ipi_dest_mask);
+}
+
+static void hw_trigger_ipi(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_debug("%s: dest %08x\n", __func__, pdata->ipi_dest_mask);
+ reg_write(pdata->ipi_base, TRIG_OFFSET, pdata->ipi_dest_mask);
+}
+
+static void ipi_init(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_debug("%s\n", __func__);
+ pdata->ipi_ops->reset(pdata);
+ pdata->ipi_ops->set_mask(pdata);
+}
+
+static struct ipi_ops ipi_hw_ops = {
+ .clear = hw_clear_ipi,
+ .reset = hw_ipi_reset,
+ .set_mask = hw_set_ipi_mask,
+ .trigger = hw_trigger_ipi,
+};
+
+
+static void smc_clear_ipi(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: atf smc to be implemented\n", __func__);
+}
+
+static void smc_ipi_reset(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: atf smc to be implemented\n", __func__);
+}
+
+static void smc_set_ipi_mask(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: atf smc to be implemented\n", __func__);
+}
+
+static void smc_trigger_ipi(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: atf smc to be implemented\n", __func__);
+}
+
+static struct ipi_ops ipi_smc_ops = {
+ .clear = smc_clear_ipi,
+ .reset = smc_ipi_reset,
+ .set_mask = smc_set_ipi_mask,
+ .trigger = smc_trigger_ipi,
+};
+
+static void hvc_clear_ipi(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: hypervisor hvc to be implemented\n", __func__);
+}
+
+static void hvc_ipi_reset(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: hypervisor hvc to be implemented\n", __func__);
+}
+
+static void hvc_set_ipi_mask(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: hypervisor hvc to be implemented\n", __func__);
+}
+
+static void hvc_trigger_ipi(struct zynqmp_r5_rproc_pdata *pdata)
+{
+ pr_err("%s: hypervisor hvc to be implemented\n", __func__);
+}
+
+static struct ipi_ops ipi_hvc_ops = {
+ .clear = hvc_clear_ipi,
+ .reset = hvc_ipi_reset,
+ .set_mask = hvc_set_ipi_mask,
+ .trigger = hvc_trigger_ipi,
+};
+
+static void handle_event(struct zynqmp_r5_rproc_pdata *local)
+{
+ if (rproc_vq_interrupt(local->rproc, 0) == IRQ_NONE)
+ dev_dbg(&remoteprocdev[local->rpu_id]->dev,
+ "no message found in vqid 0\n");
+}
+
+static void handle_event0(struct work_struct *work)
+{
+ struct zynqmp_r5_rproc_pdata *local =
+ platform_get_drvdata(remoteprocdev[0]);
+
+ handle_event(local);
+}
+
+static void handle_event1(struct work_struct *work)
+{
+ struct zynqmp_r5_rproc_pdata *local =
+ platform_get_drvdata(remoteprocdev[1]);
+
+ handle_event(local);
+}
+
+static int zynqmp_r5_rproc_start(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev.parent;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
+ int ret = 0;
+
+ dev_dbg(dev, "%s\n", __func__);
+ /* limit to two RPU support */
+ if (local->rpu_id == 0)
+ INIT_WORK(&local->workqueue, handle_event0);
+ else
+ INIT_WORK(&local->workqueue, handle_event1);
+
+ remoteprocdev[local->rpu_id] = pdev;
+
+ ret = local->rpu_ops->start(local);
+
+ if (ret < 0) {
+ dev_err(dev, "Failed to start RPU.\n");
+ return ret;
+ }
+ ipi_init(local);
+
+ return 0;
+}
+
+/* kick a firmware */
+static void zynqmp_r5_rproc_kick(struct rproc *rproc, int vqid)
+{
+ struct device *dev = rproc->dev.parent;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
+
+ dev_dbg(dev, "KICK Firmware to start send messages vqid %d\n", vqid);
+
+ /*
+ * send irq to R5 firmware
+ * Currently vqid is not used because we only got one.
+ */
+ local->ipi_ops->trigger(local);
+}
+
+/* power off the remote processor */
+static int zynqmp_r5_rproc_stop(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev.parent;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ local->rpu_ops->stop(local);
+
+ local->ipi_ops->reset(local);
+
+ return 0;
+}
+
+static struct rproc_ops zynqmp_r5_rproc_ops = {
+ .start = zynqmp_r5_rproc_start,
+ .stop = zynqmp_r5_rproc_stop,
+ .kick = zynqmp_r5_rproc_kick,
+};
+
+static irqreturn_t r5_remoteproc_interrupt(int irq, void *dev_id)
+{
+ struct device *dev = dev_id;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
+
+ dev_dbg(dev, "KICK Linux because of pending message(irq%d)\n", irq);
+
+ local->ipi_ops->clear(local);
+ schedule_work(&local->workqueue);
+
+ dev_dbg(dev, "KICK Linux handled\n");
+ return IRQ_HANDLED;
+}
+
+static int zynqmp_r5_remoteproc_probe(struct platform_device *pdev)
+{
+ const unsigned char *prop;
+ struct resource *res;
+ int ret = 0;
+ int method = 0;
+ struct zynqmp_r5_rproc_pdata *local;
+
+ local = devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_r5_rproc_pdata),
+ GFP_KERNEL);
+ if (!local)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, local);
+
+ /* Declare vring for firmware */
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "shared");
+ if (!res) {
+ dev_err(&pdev->dev, "invalid address for shared memory\n");
+ return -ENXIO;
+ }
+
+ /* Alloc phys addr from 0 to max_addr for firmware */
+ ret = dma_declare_coherent_memory(&pdev->dev, res->start,
+ res->start, res->end - res->start + 1,
+ DMA_MEMORY_IO);
+ if (!ret) {
+ dev_err(&pdev->dev, "dma_declare_coherent_memory failed\n");
+ return -ENOMEM;
+ }
+
+ /* it may need to extend to 64/48 bit later*/
+ ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+ if (ret) {
+ dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret);
+ goto dma_mask_fault;
+ }
+
+ prop = of_get_property(pdev->dev.of_node, "core_conf", NULL);
+ if (!prop) {
+ dev_warn(&pdev->dev, "default core_conf used: lock-step\n");
+ prop = "lock-step";
+ }
+
+ dev_info(&pdev->dev, "RPU core_conf: %s\n", prop);
+ if (!strcmp(prop, "split0")) {
+ local->rpu_mode = SPLIT;
+ local->rpu_id = 0;
+ } else if (!strcmp(prop, "split1")) {
+ local->rpu_mode = SPLIT;
+ local->rpu_id = 1;
+ } else if (!strcmp(prop, "lock-step")) {
+ local->rpu_mode = LOCK_STEP;
+ local->rpu_id = 0;
+ } else {
+ dev_err(&pdev->dev, "Invalid core_conf mode provided - %s , %d\n",
+ prop, local->rpu_mode);
+ goto dma_mask_fault;
+ }
+
+ prop = of_get_property(pdev->dev.of_node, "method", NULL);
+ if (!prop) {
+ dev_warn(&pdev->dev, "default method used: smc\n");
+ prop = "smc";
+ }
+
+ dev_info(&pdev->dev, "IPI/RPU control method: %s\n", prop);
+ if (!strcmp(prop, "direct")) {
+ method = HW;
+ local->ipi_ops = &ipi_hw_ops;
+ local->rpu_ops = &rpu_hw_ops;
+ } else if (!strcmp(prop, "hvc")) {
+ method = HVC;
+ local->ipi_ops = &ipi_hvc_ops;
+ local->rpu_ops = &rpu_hvc_ops;
+ } else if (!strcmp(prop, "smc")) {
+ method = SMC;
+ local->ipi_ops = &ipi_smc_ops;
+ local->rpu_ops = &rpu_smc_ops;
+ } else {
+ dev_err(&pdev->dev, "Invalid method provided - %s\n",
+ prop);
+ goto dma_mask_fault;
+ }
+
+ /* Handle direct hardware access */
+ /* (TODO: remove once RPU and IPI drivers are ready ) */
+ if (method == HW) {
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "rpu_base");
+ local->rpu_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(local->rpu_base)) {
+ dev_err(&pdev->dev, "Unable to map RPU I/O memory\n");
+ ret = PTR_ERR(local->rpu_base);
+ goto dma_mask_fault;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+ "apb_base");
+ local->crl_apb_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(local->crl_apb_base)) {
+ dev_err(&pdev->dev, "Unable to map CRL_APB I/O memory\n");
+ ret = PTR_ERR(local->crl_apb_base);
+ goto dma_mask_fault;
+ }
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "ipi");
+ local->ipi_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(local->ipi_base)) {
+ pr_err("%s: Unable to map IPI\n", __func__);
+ ret = PTR_ERR(local->ipi_base);
+ goto dma_mask_fault;
+ }
+ }
+
+ prop = of_get_property(pdev->dev.of_node, "bootmem", NULL);
+ if (!prop) {
+ dev_warn(&pdev->dev, "default bootmem property used: tcm\n");
+ prop = "tcm";
+ }
+
+ dev_info(&pdev->dev, "RPU bootmem: %s\n", prop);
+ if (!strcmp(prop, "tcm")) {
+ local->bootmem = TCM;
+ } else if (!strcmp(prop, "ocm")) {
+ local->bootmem = OCM;
+ } else {
+ dev_err(&pdev->dev, "Invalid R5 bootmem property - %s\n",
+ prop);
+ goto dma_mask_fault;
+ }
+
+ /* IPI IRQ */
+ local->vring0 = platform_get_irq(pdev, 0);
+ if (local->vring0 < 0) {
+ ret = local->vring0;
+ dev_err(&pdev->dev, "unable to find IPI IRQ\n");
+ goto dma_mask_fault;
+ }
+ ret = devm_request_irq(&pdev->dev, local->vring0,
+ r5_remoteproc_interrupt, 0, dev_name(&pdev->dev),
+ &pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "IRQ %d already allocated\n",
+ local->vring0);
+ goto dma_mask_fault;
+ }
+ dev_dbg(&pdev->dev, "vring0 irq: %d\n", local->vring0);
+
+ ret = of_property_read_u32(pdev->dev.of_node, "ipi_dest_mask",
+ &local->ipi_dest_mask);
+ if (ret < 0) {
+ dev_warn(&pdev->dev, "default ipi_dest_mask used: 0x100\n");
+ local->ipi_dest_mask = 0x100;
+ }
+ dev_info(&pdev->dev, "ipi_dest_mask: 0x%x\n", local->ipi_dest_mask);
+
+ /* Module param firmware first */
+ prop = of_get_property(pdev->dev.of_node, "firmware", NULL);
+ if (firmware)
+ prop = firmware;
+ else if (!prop)
+ prop = DEFAULT_FIRMWARE_NAME;
+
+ if (prop) {
+ dev_dbg(&pdev->dev, "Using firmware: %s\n", prop);
+ local->rproc = rproc_alloc(&pdev->dev, dev_name(&pdev->dev),
+ &zynqmp_r5_rproc_ops, prop, sizeof(struct rproc));
+ if (!local->rproc) {
+ dev_err(&pdev->dev, "rproc allocation failed\n");
+ goto rproc_fault;
+ }
+
+ ret = rproc_add(local->rproc);
+ if (ret) {
+ dev_err(&pdev->dev, "rproc registration failed\n");
+ goto rproc_fault;
+ }
+ } else {
+ ret = -ENODEV;
+ }
+
+ return ret;
+
+rproc_fault:
+ rproc_put(local->rproc);
+
+dma_mask_fault:
+ dma_release_declared_memory(&pdev->dev);
+
+ return 0;
+}
+
+static int zynqmp_r5_remoteproc_remove(struct platform_device *pdev)
+{
+ struct zynqmp_r5_rproc_pdata *local = platform_get_drvdata(pdev);
+
+ dev_info(&pdev->dev, "%s\n", __func__);
+
+ rproc_del(local->rproc);
+ rproc_put(local->rproc);
+
+ dma_release_declared_memory(&pdev->dev);
+
+ return 0;
+}
+
+/* Match table for OF platform binding */
+static const struct of_device_id zynqmp_r5_remoteproc_match[] = {
+ { .compatible = "xlnx,zynqmp-r5-remoteproc-1.0", },
+ { /* end of list */ },
+};
+MODULE_DEVICE_TABLE(of, zynqmp_r5_remoteproc_match);
+
+static struct platform_driver zynqmp_r5_remoteproc_driver = {
+ .probe = zynqmp_r5_remoteproc_probe,
+ .remove = zynqmp_r5_remoteproc_remove,
+ .driver = {
+ .name = "zynqmp_r5_remoteproc",
+ .of_match_table = zynqmp_r5_remoteproc_match,
+ },
+};
+module_platform_driver(zynqmp_r5_remoteproc_driver);
+
+module_param(firmware, charp, 0);
+MODULE_PARM_DESC(firmware, "Override the firmware image name.");
+
+MODULE_AUTHOR("Jason Wu <[email protected]>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ZynqMP R5 remote processor control driver");
--
2.1.1

2015-07-15 12:24:25

by Wendy Liang

[permalink] [raw]
Subject: [RFC PATCH 2/2] Documentation: DTS: bindings: add ZynqMP RPU remoteproc

Add documentation about the ZynqMP RPU remoteproc driver
DTS bindings.

Signed-off-by: Jason Wu <[email protected]>
Signed-off-by: Wendy Liang <[email protected]>
---
.../bindings/remoteproc/zynqmp_r5_remoteproc.txt | 49 ++++++++++++++++++++++
1 file changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt

diff --git a/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt b/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
new file mode 100644
index 0000000..94c1d00
--- /dev/null
+++ b/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
@@ -0,0 +1,49 @@
+Xilinx ARM Cortex A53-R5 remoteproc driver
+==========================================
+
+ZynqMP family of devices use two Cortex R5 processors to help with various
+low power / real time tasks.
+
+This driver requires specific ZynqMP hardware design.
+
+ZynqMP R5 RemoteProc Device Node:
+=================================
+A zynqmp_r5_remoteproc device node is used to represent a R5 IP instance
+within ZynqMP SoC.
+
+Required properties:
+--------------------
+ - compatible : Should be "xlnx,zynqmp-r5-remoteproc-1.0"
+ - reg : Address and length of the register set for the device. It
+ contains in the same order as described reg-names
+ - reg-names: Contain the register set names. For direct control method,
+ ipi, rpu_base and apb_base must be provided
+ - interrupts : Interrupt mapping for remoteproc IPI
+ - interrupt-parent : Phandle for the interrupt controller
+
+Optional properties:
+--------------------
+ - firmware : Default firmware image name "rproc-rpu-fw" which can be
+ loaded by remoteproc framework. This value can be override
+ by "firmware" module parameter.
+ - bootmem : R5 boot device (valid string, ocm or tcm), default is ocm.
+ - core_conf : R5 core configuration (valid string - split0 or split1 or
+ lock-step), default is lock-step.
+ - method : RPU and IPI control method - direct, smc, hvc, default is smc.
+ - ipi_dest_mask : IPI channel destination mask. This is used to clear
+ ipi SR and let the rpu know the kernel has completed the work.
+ default mask is "0x100"
+
+Example:
+--------
+ zynqmp-r5-remoteproc@0 {
+ compatible = "xlnx,zynqmp-r5-remoteproc";
+ reg = <0x0 0x3ed00000 0x800000>, <0x0 0xff300000 0x100>, <0x0 0xff9a0000 0x400>, <0x0 0xff5e0000 0x400>;
+ reg-names = "shared", "ipi", "rpu_base", "apb_base";
+ core_conf = "split0";
+ method = "direct";
+ bootmem = "ocm";
+ firmware = "r5_image";
+ interrupt-parent = <&gic>;
+ interrupts = <0 49 4>;
+ } ;
--
2.1.1

2015-07-15 12:50:55

by Mark Rutland

[permalink] [raw]
Subject: Re: [RFC PATCH 2/2] Documentation: DTS: bindings: add ZynqMP RPU remoteproc

On Wed, Jul 15, 2015 at 01:22:55PM +0100, Wendy Liang wrote:
> Add documentation about the ZynqMP RPU remoteproc driver
> DTS bindings.
>
> Signed-off-by: Jason Wu <[email protected]>
> Signed-off-by: Wendy Liang <[email protected]>
> ---
> .../bindings/remoteproc/zynqmp_r5_remoteproc.txt | 49 ++++++++++++++++++++++
> 1 file changed, 49 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
>
> diff --git a/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt b/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
> new file mode 100644
> index 0000000..94c1d00
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
> @@ -0,0 +1,49 @@
> +Xilinx ARM Cortex A53-R5 remoteproc driver
> +==========================================
> +
> +ZynqMP family of devices use two Cortex R5 processors to help with various
> +low power / real time tasks.
> +
> +This driver requires specific ZynqMP hardware design.
> +
> +ZynqMP R5 RemoteProc Device Node:
> +=================================
> +A zynqmp_r5_remoteproc device node is used to represent a R5 IP instance
> +within ZynqMP SoC.
> +
> +Required properties:
> +--------------------
> + - compatible : Should be "xlnx,zynqmp-r5-remoteproc-1.0"
> + - reg : Address and length of the register set for the device. It
> + contains in the same order as described reg-names
> + - reg-names: Contain the register set names. For direct control method,
> + ipi, rpu_base and apb_base must be provided
> + - interrupts : Interrupt mapping for remoteproc IPI
> + - interrupt-parent : Phandle for the interrupt controller
> +
> +Optional properties:
> +--------------------
> + - firmware : Default firmware image name "rproc-rpu-fw" which can be
> + loaded by remoteproc framework. This value can be override
> + by "firmware" module parameter.

The binding shouldn't mention Linux specifics, to the stuff about
modules should go.

Why do you think you need the firmware name in the DT? It breaks
layering (the DT has to know about the user's filesystem), so I'm not
sure that makes much sense.

> + - bootmem : R5 boot device (valid string, ocm or tcm), default is ocm.
> + - core_conf : R5 core configuration (valid string - split0 or split1 or
> + lock-step), default is lock-step.

What do these mean, when would I choose each?

s/_/-/ in property names.

> + - method : RPU and IPI control method - direct, smc, hvc, default is smc.

Likewise.

> + - ipi_dest_mask : IPI channel destination mask. This is used to clear
> + ipi SR and let the rpu know the kernel has completed the work.
> + default mask is "0x100"

We shouldn't encode masks like this in DT.

Could you elaborate on how this is used?

Thanks,
Mark.

> +
> +Example:
> +--------
> + zynqmp-r5-remoteproc@0 {
> + compatible = "xlnx,zynqmp-r5-remoteproc";
> + reg = <0x0 0x3ed00000 0x800000>, <0x0 0xff300000 0x100>, <0x0 0xff9a0000 0x400>, <0x0 0xff5e0000 0x400>;
> + reg-names = "shared", "ipi", "rpu_base", "apb_base";
> + core_conf = "split0";
> + method = "direct";
> + bootmem = "ocm";
> + firmware = "r5_image";
> + interrupt-parent = <&gic>;
> + interrupts = <0 49 4>;
> + } ;
> --
> 2.1.1
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>

2015-07-16 07:04:56

by Wendy Liang

[permalink] [raw]
Subject: Re: [RFC PATCH 2/2] Documentation: DTS: bindings: add ZynqMP RPU remoteproc

Hi Mark,

On Wed, Jul 15, 2015 at 10:50 PM, Mark Rutland <[email protected]> wrote:
>
> On Wed, Jul 15, 2015 at 01:22:55PM +0100, Wendy Liang wrote:
> > Add documentation about the ZynqMP RPU remoteproc driver
> > DTS bindings.
> >
> > Signed-off-by: Jason Wu <[email protected]>
> > Signed-off-by: Wendy Liang <[email protected]>
> > ---
> > .../bindings/remoteproc/zynqmp_r5_remoteproc.txt | 49 ++++++++++++++++++++++
> > 1 file changed, 49 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
> >
> > diff --git a/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt b/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
> > new file mode 100644
> > index 0000000..94c1d00
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/remoteproc/zynqmp_r5_remoteproc.txt
> > @@ -0,0 +1,49 @@
> > +Xilinx ARM Cortex A53-R5 remoteproc driver
> > +==========================================
> > +
> > +ZynqMP family of devices use two Cortex R5 processors to help with various
> > +low power / real time tasks.
> > +
> > +This driver requires specific ZynqMP hardware design.
> > +
> > +ZynqMP R5 RemoteProc Device Node:
> > +=================================
> > +A zynqmp_r5_remoteproc device node is used to represent a R5 IP instance
> > +within ZynqMP SoC.
> > +
> > +Required properties:
> > +--------------------
> > + - compatible : Should be "xlnx,zynqmp-r5-remoteproc-1.0"
> > + - reg : Address and length of the register set for the device. It
> > + contains in the same order as described reg-names
> > + - reg-names: Contain the register set names. For direct control method,
> > + ipi, rpu_base and apb_base must be provided
> > + - interrupts : Interrupt mapping for remoteproc IPI
> > + - interrupt-parent : Phandle for the interrupt controller
> > +
> > +Optional properties:
> > +--------------------
> > + - firmware : Default firmware image name "rproc-rpu-fw" which can be
> > + loaded by remoteproc framework. This value can be override
> > + by "firmware" module parameter.
>
> The binding shouldn't mention Linux specifics, to the stuff about
> modules should go.
>
> Why do you think you need the firmware name in the DT? It breaks
> layering (the DT has to know about the user's filesystem), so I'm not
> sure that makes much sense.


Ok, We will remove it from the DTS.
We use it in the DTS is because we want to have different remoteproc instance,
and each of it can use a different firmware.
We previously think it the simplest to put it to DTS.

>
>
> > + - bootmem : R5 boot device (valid string, ocm or tcm), default is ocm.
> > + - core_conf : R5 core configuration (valid string - split0 or split1 or
> > + lock-step), default is lock-step.
>
> What do these mean, when would I choose each?

bootmem: where the RPU boots, if bootmem is "ocm", it means it will
be configured
as "HIVEC" and boots from 0xffff0000 which is in OCM in our ZynqMP SoC.
If it is "tcm", it will be configured as "LOVEC", and boots form 0
which is TCM.
Since it is confusing, I will rename it, and will use "hivec" and
"lovec" for the values.

core_conf: it actually means the operation mode. We have R5-0 and R5-1,
"split0" means RPU is running in split mode and the remoteproc is for R5-0,
"lock-step" means RPU is running in lockstep mode.
>
>
> s/_/-/ in property names.

Will change it
>
>
> > + - method : RPU and IPI control method - direct, smc, hvc, default is smc.
>
> Likewise.
>
> > + - ipi_dest_mask : IPI channel destination mask. This is used to clear
> > + ipi SR and let the rpu know the kernel has completed the work.
> > + default mask is "0x100"
>
> We shouldn't encode masks like this in DT.
>
> Could you elaborate on how this is used?


We have IPI in our SoC, we don't have hardcoded IPI channel binding, though
we have default channel binding settings.
this property here is because in case user don't want to use default bindings
and they have their own configuration settings.

We will give number to each channel, and will change to do the
mapping in the driver,
instead of using the mask directly in the DTS.

Thanks & Regards,
Wendy

>
>
> Thanks,
> Mark.
>
> > +
> > +Example:
> > +--------
> > + zynqmp-r5-remoteproc@0 {
> > + compatible = "xlnx,zynqmp-r5-remoteproc";
> > + reg = <0x0 0x3ed00000 0x800000>, <0x0 0xff300000 0x100>, <0x0 0xff9a0000 0x400>, <0x0 0xff5e0000 0x400>;
> > + reg-names = "shared", "ipi", "rpu_base", "apb_base";
> > + core_conf = "split0";
> > + method = "direct";
> > + bootmem = "ocm";
> > + firmware = "r5_image";
> > + interrupt-parent = <&gic>;
> > + interrupts = <0 49 4>;
> > + } ;
> > --
> > 2.1.1
> >
> >
> > _______________________________________________
> > linux-arm-kernel mailing list
> > [email protected]
> > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
> >