Jelajahi Sumber

Merge branch 'rpmsg-ti-linux-4.19.y-intg' of git://git.ti.com/rpmsg/rpmsg into ti-linux-4.19.y

TI-Feature: rpmsg
TI-Tree: git://git.ti.com/rpmsg/rpmsg.git
TI-Branch: rpmsg-ti-linux-4.19.y-intg

* 'rpmsg-ti-linux-4.19.y-intg' of git://git.ti.com/rpmsg/rpmsg:
  ti_config_fragments: rpmsg: Add keystone-dsp-mem and UIO modules
  remoteproc/keystone: add support for MPM userspace loader
  remoteproc/keystone: Switch to SPDX license identifier
  remoteproc: add infrastructure support to allow pre-loaded remoteprocs
  TEMP: ARM: dts: keystone-k2g-ice: Add a memory carveout for MPM usecases
  TEMP: ARM: dts: keystone-k2g-evm: Add a memory carveout for MPM usecases
  TEMP: ARM: dts: keystone-k2e-evm: Add a memory carveout for MPM usecases
  TEMP: ARM: dts: keystone-k2l-evm: Add a memory carveout for MPM usecases
  TEMP: ARM: dts: keystone-k2hk-evm: Add a memory carveout for MPM usecases
  ARM: dts: keystone-k2g: Reserve SRAM for MPM
  ARM: dts: keystone-k2e: Reserve SRAM for MPM
  ARM: dts: keystone-k2l: Reserve SRAM for MPM
  ARM: dts: keystone-k2hk: Reserve SRAM for MPM
  TEMP: soc: ti: add the keystone_dsp_mem driver
  TEMP: dt-bindings: soc: ti: Add Keystone DSP Memory mapping binding
  ti_config_fragments: rpmsg: Add Keystone remoteproc module
  net/rpmsg: fix return value of rpmsg_sock_sendmsg()

Signed-off-by: LCPD Auto Merger <lcpd_integration@list.ti.com>
LCPD Auto Merger 7 tahun lalu
induk
melakukan
6bcef850ba

+ 73 - 0
Documentation/devicetree/bindings/soc/ti/keystone-dsp-mem.txt

@@ -0,0 +1,73 @@
+TI Keystone DSP Memory Mapping
+==============================
+
+Binding status: Unstable - ABI compatibility may be broken in the future
+
+The Keystone DSP Memory Mapping binding defines the memory regions that
+are reserved to be used with DSPs. These regions can be mapped into
+userspace for providing direct user-mode access to these regions for the
+purposes of shared memory communication with the DSP remote processor
+devices on the SoC. These memory regions can also be used for supporting
+user-space based loading of the DSP remoteproc devices.
+
+The memory regions can either be from regular DDR memory or from the
+On-chip RAM, and there can be one or more regions of each memory type.
+
+DDR Memory usage (Optional):
+----------------------------
+One or more memory regions from DDR memory can be reserved specifically
+to be used by the DSPs on the SoC. Each region should be defined as a
+child node of the reserved-memory node with the following required
+properties:
+
+- compatible : Should be "ti,keystone-dsp-mem-pool"
+- reg        : Should contain the region's start address and size following
+               the #address-cells and #size-cells defined in the parent
+               reserved-memory node.
+- no-map     : Should be defined to remove this region from kernel
+
+Please see Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
+for more details on reserved-memory node.
+
+SRAM usage (Optional):
+----------------------
+The On-chip Multicore Shared Memory (MSM) RAM can also be exposed to
+userspace by defining specific child nodes under the corresponding parent
+SRAM node. The generic SRAM binding is as per the binding document
+Documentation/devicetree/bindings/misc/sram.txt. Following properties
+should be used in each corresponding child node for the userspace mapping
+usecase:
+
+- compatible : Should be "ti,keystone-dsp-msm-ram"
+- reg        : Should contain a pair of values for the address and size
+               of the region, following the parent-child ranges convention.
+
+Example:
+--------
+	/* 66AK2H EVM */
+	reserved-memory {
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges;
+
+		dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+			compatible = "ti,keystone-dsp-mem-pool";
+			reg = <0x00000008 0x20000000 0x00000000 0x20000000>;
+			no-map;
+		};
+	};
+
+	soc {
+		msm_ram: msmram@c000000 {
+			compatible = "mmio-sram";
+			reg = <0x0c000000 0x600000>;
+			ranges = <0x0 0x0c000000 0x600000>;
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			sram-mpm@0 {
+				compatible = "ti,keystone-dsp-msm-ram";
+				reg = <0x0 0x80000>;
+			};
+		};
+	};

+ 7 - 0
arch/arm/boot/dts/keystone-k2e-evm.dts

@@ -24,6 +24,13 @@
 			reusable;
 			status = "okay";
 		};
+
+		dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+			compatible = "ti,keystone-dsp-mem-pool";
+			reg = <0x00000008 0x20000000 0x00000000 0x10000000>;
+			no-map;
+			status = "okay";
+		};
 	};
 };
 

+ 5 - 0
arch/arm/boot/dts/keystone-k2e.dtsi

@@ -93,6 +93,11 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			sram-mpm@0 {
+				compatible = "ti,keystone-dsp-msm-ram";
+				reg = <0x0 0x80000>;
+			};
+
 			sram-bm@1f0000 {
 				reg = <0x001f0000 0x8000>;
 			};

+ 7 - 0
arch/arm/boot/dts/keystone-k2g-evm.dts

@@ -22,6 +22,13 @@
 		#size-cells = <2>;
 		ranges;
 
+		dsp_common_mpm_memory: dsp-common-mpm-memory@81d000000 {
+			compatible = "ti,keystone-dsp-mem-pool";
+			reg = <0x00000008 0x1d000000 0x00000000 0x2800000>;
+			no-map;
+			status = "okay";
+		};
+
 		dsp_common_memory: dsp-common-memory@81f800000 {
 			compatible = "shared-dma-pool";
 			reg = <0x00000008 0x1f800000 0x00000000 0x800000>;

+ 7 - 0
arch/arm/boot/dts/keystone-k2g-ice.dts

@@ -23,6 +23,13 @@
 		#size-cells = <2>;
 		ranges;
 
+		dsp_common_mpm_memory: dsp-common-mpm-memory@81d000000 {
+			compatible = "ti,keystone-dsp-mem-pool";
+			reg = <0x00000008 0x1d000000 0x00000000 0x2800000>;
+			no-map;
+			status = "okay";
+		};
+
 		dsp_common_memory: dsp-common-memory@81f800000 {
 			compatible = "shared-dma-pool";
 			reg = <0x00000008 0x1f800000 0x00000000 0x800000>;

+ 5 - 0
arch/arm/boot/dts/keystone-k2g.dtsi

@@ -102,6 +102,11 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			sram-mpm@0 {
+				compatible = "ti,keystone-dsp-msm-ram";
+				reg = <0x0 0x80000>;
+			};
+
 			sram-bm@f7000 {
 				reg = <0x000f7000 0x8000>;
 			};

+ 7 - 0
arch/arm/boot/dts/keystone-k2hk-evm.dts

@@ -24,6 +24,13 @@
 			reusable;
 			status = "okay";
 		};
+
+		dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+			compatible = "ti,keystone-dsp-mem-pool";
+			reg = <0x00000008 0x20000000 0x00000000 0x10000000>;
+			no-map;
+			status = "okay";
+		};
 	};
 
 	leds {

+ 5 - 0
arch/arm/boot/dts/keystone-k2hk.dtsi

@@ -64,6 +64,11 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			sram-mpm@0 {
+				compatible = "ti,keystone-dsp-msm-ram";
+				reg = <0x0 0x80000>;
+			};
+
 			sram-bm@5f0000 {
 				reg = <0x5f0000 0x8000>;
 			};

+ 7 - 0
arch/arm/boot/dts/keystone-k2l-evm.dts

@@ -24,6 +24,13 @@
 			reusable;
 			status = "okay";
 		};
+
+		dsp_common_mpm_memory: dsp-common-mpm-memory@820000000 {
+			compatible = "ti,keystone-dsp-mem-pool";
+			reg = <0x00000008 0x20000000 0x00000000 0x10000000>;
+			no-map;
+			status = "okay";
+		};
 	};
 };
 

+ 5 - 0
arch/arm/boot/dts/keystone-k2l.dtsi

@@ -262,6 +262,11 @@
 			#address-cells = <1>;
 			#size-cells = <1>;
 
+			sram-mpm@0 {
+				compatible = "ti,keystone-dsp-msm-ram";
+				reg = <0x0 0x80000>;
+			};
+
 			sram-bm@1f8000 {
 				reg = <0x001f8000 0x8000>;
 			};

+ 1 - 0
drivers/remoteproc/Kconfig

@@ -77,6 +77,7 @@ config DA8XX_REMOTEPROC
 config KEYSTONE_REMOTEPROC
 	tristate "Keystone Remoteproc support"
 	depends on ARCH_KEYSTONE
+	depends on UIO
 	help
 	  Say Y here here to support Keystone remote processors (DSP)
 	  via the remote processor framework.

+ 661 - 25
drivers/remoteproc/keystone_remoteproc.c

@@ -1,22 +1,16 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * TI Keystone DSP remoteproc driver
  *
- * Copyright (C) 2015-2017 Texas Instruments Incorporated - http://www.ti.com/
- *
- * 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.
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
  */
 
 #include <linux/module.h>
+#include <linux/uaccess.h>
 #include <linux/slab.h>
 #include <linux/io.h>
 #include <linux/interrupt.h>
+#include <linux/spinlock.h>
 #include <linux/platform_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/workqueue.h>
@@ -26,24 +20,40 @@
 #include <linux/regmap.h>
 #include <linux/mfd/syscon.h>
 #include <linux/remoteproc.h>
+#include <linux/miscdevice.h>
+#include <linux/uio_driver.h>
 #include <linux/reset.h>
 
+#include <uapi/linux/keystone_remoteproc.h>
+
 #include "remoteproc_internal.h"
 
+#define DRIVER_UIO_VERSION			"0.1"
+
+#define KEYSTONE_RPROC_MAX_RSC_TABLE		SZ_1K
 #define KEYSTONE_RPROC_LOCAL_ADDRESS_MASK	(SZ_16M - 1)
 
+/*
+ * XXX: evaluate if this param needs to be enhanced so that the switch between
+ * userspace and remoteproc core loaders can be controlled per device.
+ */
+static bool use_rproc_core_loader;
+module_param(use_rproc_core_loader, bool, 0444);
+
 /**
  * struct keystone_rproc_mem - internal memory structure
  * @cpu_addr: MPU virtual address of the memory region
  * @bus_addr: Bus address used to access the memory region
  * @dev_addr: Device address of the memory region from DSP view
  * @size: Size of the memory region
+ * @kobj: kobject for the sysfs directory file
  */
 struct keystone_rproc_mem {
 	void __iomem *cpu_addr;
 	phys_addr_t bus_addr;
 	u32 dev_addr;
 	size_t size;
+	struct kobject kobj;
 };
 
 /**
@@ -59,6 +69,18 @@ struct keystone_rproc_mem {
  * @irq_fault: irq entry for exception
  * @kick_gpio: gpio used for virtio kicks
  * @workqueue: workqueue for processing virtio interrupts
+ * @misc: misc device structure used to expose fops to user-space
+ * @uio: uio device information
+ * @mlock: lock to protect resources in fops
+ * @lock: lock to protect shared resources within UIO interrupt handlers
+ * @flags: flags to keep track of UIO interrupt occurrence
+ * @rsc_table: resource table pointer copied from userspace
+ * @rsc_table_size: size of resource table
+ * @loaded_rsc_table: kernel pointer of loaded resource table
+ * @boot_addr: remote processor boot address used with userspace loader
+ * @open_count: fops open reference counter
+ * @use_userspace_loader: flag to denote if driver is configured for userspace
+ *			  loader
  */
 struct keystone_rproc {
 	struct device *dev;
@@ -72,8 +94,281 @@ struct keystone_rproc {
 	int irq_fault;
 	int kick_gpio;
 	struct work_struct workqueue;
+	struct miscdevice misc;
+	struct uio_info uio;
+	struct mutex mlock; /* fops lock */
+	spinlock_t lock; /* uio handler lock */
+	unsigned long flags;
+	struct resource_table *rsc_table;
+	int rsc_table_size;
+	void *loaded_rsc_table;
+	u32 boot_addr;
+	int open_count;
+	unsigned int use_userspace_loader : 1;
+};
+
+struct mem_sysfs_entry {
+	struct attribute attr;
+	ssize_t (*show)(struct keystone_rproc_mem *mem, char *buf);
+	ssize_t (*store)(struct keystone_rproc_mem *mem, const char *buf,
+			 size_t len);
+};
+
+static ssize_t mem_addr_show(struct keystone_rproc_mem *mem, char *buf)
+{
+	return sprintf(buf, "%pa\n", &mem->bus_addr);
+}
+
+static ssize_t mem_size_show(struct keystone_rproc_mem *mem, char *buf)
+{
+	return sprintf(buf, "0x%016zx\n", mem->size);
+}
+
+static struct mem_sysfs_entry addr_attribute =
+	__ATTR(addr, 0444, mem_addr_show, NULL);
+static struct mem_sysfs_entry size_attribute =
+	__ATTR(size, 0444, mem_size_show, NULL);
+
+static struct attribute *attrs[] = {
+	&addr_attribute.attr,
+	&size_attribute.attr,
+	NULL,	/* sentinel */
 };
 
+#define to_dsp_mem(m) container_of(m, struct keystone_rproc_mem, kobj)
+
+static ssize_t mem_type_show(struct kobject *kobj, struct attribute *attr,
+			     char *buf)
+{
+	struct keystone_rproc_mem *mem = to_dsp_mem(kobj);
+	struct mem_sysfs_entry *entry;
+
+	entry = container_of(attr, struct mem_sysfs_entry, attr);
+	if (!entry->show)
+		return -EIO;
+
+	return entry->show(mem, buf);
+}
+
+static const struct sysfs_ops mem_sysfs_ops = {
+	.show = mem_type_show,
+};
+
+static struct kobj_type mem_attr_type = {
+	.sysfs_ops	= &mem_sysfs_ops,
+	.default_attrs	= attrs,
+};
+
+static int keystone_rproc_mem_add_attrs(struct keystone_rproc *ksproc)
+{
+	int i, ret;
+	struct keystone_rproc_mem *mem;
+	struct kobject *kobj_parent = &ksproc->misc.this_device->kobj;
+
+	for (i = 0; i < ksproc->num_mems; i++) {
+		mem = &ksproc->mem[i];
+		kobject_init(&mem->kobj, &mem_attr_type);
+		ret = kobject_add(&mem->kobj, kobj_parent, "memory%d", i);
+		if (ret)
+			goto err_kobj;
+		ret = kobject_uevent(&mem->kobj, KOBJ_ADD);
+		if (ret)
+			goto err_kobj;
+	}
+
+	return 0;
+
+err_kobj:
+	for (; i >= 0; i--) {
+		mem = &ksproc->mem[i];
+		kobject_put(&mem->kobj);
+	}
+	return ret;
+}
+
+static void keystone_rproc_mem_del_attrs(struct keystone_rproc *ksproc)
+{
+	int i;
+	struct keystone_rproc_mem *mem;
+
+	for (i = 0; i < ksproc->num_mems; i++) {
+		mem = &ksproc->mem[i];
+		kobject_put(&mem->kobj);
+	}
+}
+
+static void *keystone_rproc_da_to_va(struct rproc *rproc, u64 da, int len);
+
+/* uio handler dealing with userspace controlled exception interrupt */
+static irqreturn_t keystone_rproc_uio_handler(int irq, struct uio_info *uio)
+{
+	struct keystone_rproc *ksproc = uio->priv;
+
+	spin_lock(&ksproc->lock);
+	if (!__test_and_set_bit(0, &ksproc->flags))
+		disable_irq_nosync(irq);
+	spin_unlock(&ksproc->lock);
+
+	return IRQ_HANDLED;
+}
+
+/* uio driver interrupt control dealing with exception interrupt */
+static int keystone_rproc_uio_irqcontrol(struct uio_info *uio, s32 irq_on)
+{
+	struct keystone_rproc *ksproc = uio->priv;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ksproc->lock, flags);
+	if (irq_on) {
+		if (__test_and_clear_bit(0, &ksproc->flags))
+			enable_irq(uio->irq);
+	} else {
+		if (!__test_and_set_bit(0, &ksproc->flags))
+			disable_irq(uio->irq);
+	}
+	spin_unlock_irqrestore(&ksproc->lock, flags);
+
+	return 0;
+}
+
+/* Reset previously set rsc table variables */
+static void keystone_rproc_reset_rsc_table(struct keystone_rproc *ksproc)
+{
+	kfree(ksproc->rsc_table);
+	ksproc->rsc_table = NULL;
+	ksproc->loaded_rsc_table = NULL;
+	ksproc->rsc_table_size = 0;
+}
+
+/*
+ * Create/delete the virtio devices in kernel once the user-space loading is
+ * complete, configure the remoteproc states appropriately, and boot or reset
+ * the remote processor. The resource table should have been published through
+ * KEYSTONE_RPROC_IOC_SET_RSC_TABLE & KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE
+ * ioctls before invoking this. The boot address is passed through the
+ * KEYSTONE_RPROC_IOC_SET_STATE ioctl when setting the KEYSTONE_RPROC_RUNNING
+ * state.
+ *
+ * NOTE:
+ * The ioctls KEYSTONE_RPROC_IOC_DSP_RESET and KEYSTONE_RPROC_IOC_DSP_BOOT
+ * are restricted to support the booting or resetting the DSP devices only
+ * for firmware images without any resource table.
+ */
+static int keystone_rproc_set_state(struct keystone_rproc *ksproc,
+				    void __user *argp)
+{
+	struct rproc *rproc = ksproc->rproc;
+	struct keystone_rproc_set_state_params set_state_params;
+	int ret = 0;
+
+	if (copy_from_user(&set_state_params, argp, sizeof(set_state_params)))
+		return -EFAULT;
+
+	switch (set_state_params.state) {
+	case KEYSTONE_RPROC_RUNNING:
+		if (!ksproc->rsc_table || !ksproc->loaded_rsc_table)
+			return -EINVAL;
+
+		/*
+		 * store boot address for .get_boot_addr() rproc fw ops
+		 * XXX: validate the boot address so it is not set to a
+		 * random address
+		 */
+		ksproc->boot_addr = set_state_params.boot_addr;
+
+		/*
+		 * invoke rproc_boot to trigger the boot, the resource table
+		 * is parsed during the process and is agnostic of the presence
+		 * or absence of virtio devices
+		 */
+		ret = rproc_boot(rproc);
+		break;
+
+	case KEYSTONE_RPROC_OFFLINE:
+		if (rproc->state != RPROC_RUNNING)
+			return -EINVAL;
+
+		/* invoke rproc_shutdown to match rproc_boot */
+		rproc_shutdown(rproc);
+
+		mutex_lock(&ksproc->mlock);
+		keystone_rproc_reset_rsc_table(ksproc);
+		mutex_unlock(&ksproc->mlock);
+
+		break;
+
+	default:
+		ret = -ENOTSUPP;
+	}
+
+	return ret;
+}
+
+/* Copy the resource table from userspace into kernel */
+static int keystone_rproc_set_rsc_table(struct keystone_rproc *ksproc,
+					void __user *data)
+{
+	unsigned long len = 0;
+	void *rsc_table = NULL;
+
+	if (!data)
+		return -EFAULT;
+
+	if (copy_from_user(&len, data, sizeof(len)))
+		return -EFAULT;
+
+	if (len >= KEYSTONE_RPROC_MAX_RSC_TABLE)
+		return -EOVERFLOW;
+
+	data += sizeof(len);
+
+	rsc_table = kzalloc(len, GFP_KERNEL);
+	if (!rsc_table)
+		return -ENOMEM;
+
+	if (copy_from_user(rsc_table, data, len))
+		goto error_return;
+
+	mutex_lock(&ksproc->mlock);
+
+	kfree(ksproc->rsc_table);
+
+	ksproc->rsc_table = rsc_table;
+	ksproc->rsc_table_size = len;
+	ksproc->loaded_rsc_table = NULL;
+
+	mutex_unlock(&ksproc->mlock);
+
+	return 0;
+
+error_return:
+	kfree(rsc_table);
+	return -EFAULT;
+}
+
+/*
+ * Store the equivalent kernel virtual address of the loaded resource table in
+ * device memory. Userspace published the device address of the loaded resource
+ * table.
+ */
+static int keystone_rproc_set_loaded_rsc_table(struct keystone_rproc *ksproc,
+					       unsigned int dma_addr)
+{
+	struct rproc *rproc = ksproc->rproc;
+	void *ptr;
+
+	if (!ksproc->rsc_table_size || !ksproc->rsc_table)
+		return -EINVAL;
+
+	ptr = keystone_rproc_da_to_va(rproc, dma_addr, ksproc->rsc_table_size);
+	if (!ptr)
+		return -EINVAL;
+
+	ksproc->loaded_rsc_table = ptr;
+
+	return 0;
+}
+
 /* Put the DSP processor into reset */
 static void keystone_rproc_dsp_reset(struct keystone_rproc *ksproc)
 {
@@ -81,7 +376,8 @@ static void keystone_rproc_dsp_reset(struct keystone_rproc *ksproc)
 }
 
 /* Configure the boot address and boot the DSP processor */
-static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
+static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc,
+				   uint32_t boot_addr)
 {
 	int ret;
 
@@ -103,6 +399,235 @@ static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
 	return 0;
 }
 
+static long
+keystone_rproc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
+{
+	struct miscdevice *misc = filp->private_data;
+	struct keystone_rproc *ksproc =
+		container_of(misc, struct keystone_rproc, misc);
+	void __user *argp = (void __user *)arg;
+	int ret = 0;
+
+	dev_dbg(ksproc->dev, "%s: cmd 0x%.8x (%d), arg 0x%lx\n",
+		__func__, cmd, _IOC_NR(cmd), arg);
+
+	if (_IOC_TYPE(cmd) != KEYSTONE_RPROC_IOC_MAGIC)
+		return -ENOTTY;
+
+	if (_IOC_NR(cmd) >= KEYSTONE_RPROC_IOC_MAXNR)
+		return -ENOTTY;
+
+	switch (cmd) {
+	case KEYSTONE_RPROC_IOC_SET_STATE:
+		ret = keystone_rproc_set_state(ksproc, argp);
+		break;
+
+	case KEYSTONE_RPROC_IOC_SET_RSC_TABLE:
+		ret = keystone_rproc_set_rsc_table(ksproc, argp);
+		break;
+
+	case KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE:
+		ret = keystone_rproc_set_loaded_rsc_table(ksproc, arg);
+		break;
+
+	case KEYSTONE_RPROC_IOC_DSP_RESET:
+		if (ksproc->rsc_table) {
+			ret = -EINVAL;
+			break;
+		}
+
+		keystone_rproc_dsp_reset(ksproc);
+		break;
+
+	case KEYSTONE_RPROC_IOC_DSP_BOOT:
+		if (ksproc->rsc_table) {
+			ret = -EINVAL;
+			break;
+		}
+
+		ret = keystone_rproc_dsp_boot(ksproc, arg);
+		break;
+
+	default:
+		ret = -ENOTTY;
+		break;
+	}
+
+	if (ret) {
+		dev_err(ksproc->dev, "error in ioctl call: cmd 0x%.8x (%d), ret %d\n",
+			cmd, _IOC_NR(cmd), ret);
+	}
+
+	return ret;
+}
+
+/*
+ * Map DSP memories into userspace for supporting Userspace loading.
+ *
+ * This is a custom mmap function following semantics based on the UIO
+ * mmap implementation. The vm_pgoff passed in the vma structure is a
+ * combination of the memory region index and the actual page offset in
+ * that region. This checks if user request is in valid range before
+ * providing mmap access.
+ *
+ * XXX: Evaluate this approach, as the internal memories can be mapped in
+ * whole into userspace as they are not super-large, or switch to using
+ * direct addresses to look more like a traditional implementation.
+ */
+static int keystone_rproc_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct miscdevice *misc = file->private_data;
+	struct keystone_rproc *ksproc =
+		container_of(misc, struct keystone_rproc, misc);
+	size_t size = vma->vm_end - vma->vm_start;
+	size_t req_offset;
+	u32 idx;
+
+	idx = vma->vm_pgoff & KEYSTONE_RPROC_UIO_MAP_INDEX_MASK;
+
+	if (idx >= ksproc->num_mems) {
+		dev_err(ksproc->dev, "invalid mmap region index %d\n", idx);
+		return -EINVAL;
+	}
+
+	req_offset = (vma->vm_pgoff - idx) << PAGE_SHIFT;
+	if (req_offset + size < req_offset) {
+		dev_err(ksproc->dev, "invalid request - overflow, mmap offset = 0x%zx size 0x%zx region %d\n",
+			req_offset, size, idx);
+		return -EINVAL;
+	}
+
+	if ((req_offset + size) > ksproc->mem[idx].size) {
+		dev_err(ksproc->dev, "invalid request - out of range, mmap offset 0x%zx size 0x%zx region %d\n",
+			req_offset, size, idx);
+		return -EINVAL;
+	}
+
+	vma->vm_page_prot =
+		phys_mem_access_prot(file,
+				     (ksproc->mem[idx].bus_addr >> PAGE_SHIFT) +
+				     (vma->vm_pgoff - idx), size,
+				     vma->vm_page_prot);
+
+	if (remap_pfn_range(vma, vma->vm_start,
+			    (ksproc->mem[idx].bus_addr >> PAGE_SHIFT) +
+			    (vma->vm_pgoff - idx), size, vma->vm_page_prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static int keystone_rproc_open(struct inode *inode, struct file *file)
+{
+	struct miscdevice *misc = file->private_data;
+	struct keystone_rproc *ksproc =
+		container_of(misc, struct keystone_rproc, misc);
+
+	mutex_lock(&ksproc->mlock);
+	ksproc->open_count++;
+	mutex_unlock(&ksproc->mlock);
+
+	return 0;
+}
+
+static int keystone_rproc_release(struct inode *inode, struct file *filp)
+{
+	struct miscdevice *misc = filp->private_data;
+	struct keystone_rproc *ksproc =
+		container_of(misc, struct keystone_rproc, misc);
+	struct rproc *rproc = ksproc->rproc;
+
+	mutex_lock(&ksproc->mlock);
+
+	if ((WARN_ON(ksproc->open_count == 0)))
+		goto end;
+
+	if (--ksproc->open_count > 0)
+		goto end;
+
+	if (rproc->state != RPROC_OFFLINE) {
+		rproc_shutdown(rproc);
+		WARN_ON(rproc->state != RPROC_OFFLINE);
+	}
+
+	keystone_rproc_reset_rsc_table(ksproc);
+
+end:
+	mutex_unlock(&ksproc->mlock);
+	return 0;
+}
+
+/*
+ * File operations exposed through a miscdevice for supporting
+ * the userspace loader/boot mechanism.
+ */
+static const struct file_operations keystone_rproc_fops = {
+	.owner		= THIS_MODULE,
+	.unlocked_ioctl	= keystone_rproc_ioctl,
+	.mmap		= keystone_rproc_mmap,
+	.open		= keystone_rproc_open,
+	.release	= keystone_rproc_release,
+};
+
+/*
+ * Used only with userspace loader/boot mechanism, the parsing of the firmware
+ * is done in userspace, and a copy of the resource table is added for the
+ * kernel-level access through an ioctl. Create the remoteproc cached table
+ * using this resource table and configure the table pointer and table size
+ * accordingly to allow the remoteproc core to process the resource table for
+ * creating the vrings and traces.
+ */
+static int keystone_rproc_load_rsc_table(struct rproc *rproc,
+					 const struct firmware *fw)
+{
+	struct keystone_rproc *ksproc = rproc->priv;
+
+	rproc->cached_table = kmemdup(ksproc->rsc_table, ksproc->rsc_table_size,
+				      GFP_KERNEL);
+	if (!rproc->cached_table)
+		return -ENOMEM;
+
+	rproc->table_ptr = rproc->cached_table;
+	rproc->table_sz = ksproc->rsc_table_size;
+
+	return 0;
+}
+
+/*
+ * Used only with userspace loader/boot mechanism, the device address of the
+ * loaded resource table is published to the kernel-level through an ioctl
+ * at which point the equivalent kernel virtual pointer is stored in a local
+ * variable in the keystone_rproc device structure. Return this kernel pointer
+ * to the remoteproc core for runtime publishing/modification of the resource
+ * table entries.
+ *
+ * NOTE: Only loaded resource tables in the DSP internal memories is supported
+ *       at present.
+ */
+static struct resource_table *
+keystone_rproc_find_loaded_rsc_table(struct rproc *rproc,
+				     const struct firmware *fw)
+{
+	struct keystone_rproc *ksproc = rproc->priv;
+
+	return ksproc->loaded_rsc_table;
+}
+
+/*
+ * Used only with userspace loader/boot mechanism, the boot address
+ * is published to the kernel-level through an ioctl call and is
+ * stored in a local variable in the keystone_rproc device structure.
+ * Return this address to the remoteproc core through the .get_boot_addr()
+ * remoteproc firmware ops
+ */
+static u32 keystone_rproc_get_boot_addr(struct rproc *rproc,
+					const struct firmware *fw)
+{
+	struct keystone_rproc *ksproc = rproc->priv;
+
+	return ksproc->boot_addr;
+}
+
 /*
  * Process the remoteproc exceptions
  *
@@ -112,7 +637,9 @@ static int keystone_rproc_dsp_boot(struct keystone_rproc *ksproc, u32 boot_addr)
  * generation register.
  *
  * This function just invokes the rproc_report_crash to report the exception
- * to the remoteproc driver core, to trigger a recovery.
+ * to the remoteproc driver core, to trigger a recovery. This is the case
+ * only when using in-kernel remoteproc core loader/boot mechanism, and is
+ * handled through an UIO interrupt otherwise.
  */
 static irqreturn_t keystone_rproc_exception_interrupt(int irq, void *dev_id)
 {
@@ -172,7 +699,11 @@ static irqreturn_t keystone_rproc_vring_interrupt(int irq, void *dev_id)
  *
  * This function will be invoked only after the firmware for this rproc
  * was loaded, parsed successfully, and all of its resource requirements
- * were met.
+ * were met. The function skips releasing the processor from reset and
+ * registering for the exception interrupt if using the userspace controlled
+ * load/boot mechanism. The processor will be started through an ioctl when
+ * controlled from userspace, but the virtio interrupt still is handled at
+ * the kernel layer.
  */
 static int keystone_rproc_start(struct rproc *rproc)
 {
@@ -189,12 +720,15 @@ static int keystone_rproc_start(struct rproc *rproc)
 		goto out;
 	}
 
-	ret = request_irq(ksproc->irq_fault, keystone_rproc_exception_interrupt,
-			  0, dev_name(ksproc->dev), ksproc);
-	if (ret) {
-		dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n",
-			ret);
-		goto free_vring_irq;
+	if (!ksproc->use_userspace_loader) {
+		ret = request_irq(ksproc->irq_fault,
+				  keystone_rproc_exception_interrupt, 0,
+				  dev_name(ksproc->dev), ksproc);
+		if (ret) {
+			dev_err(ksproc->dev, "failed to enable exception interrupt, ret = %d\n",
+				ret);
+			goto free_vring_irq;
+		}
 	}
 
 	ret = keystone_rproc_dsp_boot(ksproc, rproc->bootaddr);
@@ -216,24 +750,29 @@ out:
  * Stop the DSP remote processor.
  *
  * This function puts the DSP processor into reset, and finishes processing
- * of any pending messages.
+ * of any pending messages. The reset procedure is completed only if using
+ * kernel-mode remoteproc loading/booting mechanism, it is handled outside
+ * if using userspace load/boot mechanism either through an ioctl, or when
+ * the handle to the device is closed without triggering a reset.
  */
 static int keystone_rproc_stop(struct rproc *rproc)
 {
 	struct keystone_rproc *ksproc = rproc->priv;
 
-	keystone_rproc_dsp_reset(ksproc);
-	free_irq(ksproc->irq_fault, ksproc);
+	if (!ksproc->use_userspace_loader) {
+		keystone_rproc_dsp_reset(ksproc);
+		free_irq(ksproc->irq_fault, ksproc);
+	}
+
 	free_irq(ksproc->irq_ring, ksproc);
 	flush_work(&ksproc->workqueue);
-
 	return 0;
 }
 
 /*
  * Kick the remote processor to notify about pending unprocessed messages.
  * The vqid usage is not used and is inconsequential, as the kick is performed
- * through a simulated GPIO (a bit in an IPC interrupt-triggering register),
+ * through a simulated GPIO (an bit in an IPC interrupt-triggering register),
  * the remote processor is expected to process both its Tx and Rx virtqueues.
  */
 static void keystone_rproc_kick(struct rproc *rproc, int vqid)
@@ -371,8 +910,11 @@ static int keystone_rproc_probe(struct platform_device *pdev)
 	struct device *dev = &pdev->dev;
 	struct device_node *np = dev->of_node;
 	struct keystone_rproc *ksproc;
+	struct miscdevice *misc;
+	struct uio_info *uio;
 	struct rproc *rproc;
 	int dsp_id;
+	char *uio_name = NULL;
 	char *fw_name = NULL;
 	char *template = "keystone-dsp%d-fw";
 	int name_len = 0;
@@ -389,6 +931,13 @@ static int keystone_rproc_probe(struct platform_device *pdev)
 		return dsp_id;
 	}
 
+	/* construct a name for uio devices - assuming a single digit alias */
+	name_len = strlen("dsp%d");
+	uio_name = devm_kzalloc(dev, name_len, GFP_KERNEL);
+	if (!uio_name)
+		return -ENOMEM;
+	snprintf(uio_name, name_len, "dsp%d", dsp_id);
+
 	/* construct a custom default fw name - subject to change in future */
 	name_len = strlen(template); /* assuming a single digit alias */
 	fw_name = devm_kzalloc(dev, name_len, GFP_KERNEL);
@@ -402,9 +951,33 @@ static int keystone_rproc_probe(struct platform_device *pdev)
 		return -ENOMEM;
 
 	rproc->has_iommu = false;
+
 	ksproc = rproc->priv;
 	ksproc->rproc = rproc;
 	ksproc->dev = dev;
+	ksproc->use_userspace_loader = !use_rproc_core_loader;
+
+	/*
+	 * customize the remoteproc core config flags and ELF fw ops for
+	 * userspace loader/boot mechanism
+	 */
+	if (ksproc->use_userspace_loader) {
+		rproc->recovery_disabled = true;
+		rproc->auto_boot = false;
+		rproc->skip_firmware_request = 1;
+		rproc->skip_load = 1;
+		rproc->deny_sysfs_ops = true;
+
+		rproc->ops->parse_fw = keystone_rproc_load_rsc_table;
+		rproc->ops->find_loaded_rsc_table =
+					keystone_rproc_find_loaded_rsc_table;
+		rproc->ops->get_boot_addr = keystone_rproc_get_boot_addr;
+		rproc->ops->sanity_check = NULL;
+		rproc->ops->load = NULL;
+	}
+
+	mutex_init(&ksproc->mlock);
+	spin_lock_init(&ksproc->lock);
 
 	ret = keystone_rproc_of_get_dev_syscon(pdev, ksproc);
 	if (ret)
@@ -475,8 +1048,52 @@ static int keystone_rproc_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, ksproc);
 
+	if (ksproc->use_userspace_loader) {
+		uio = &ksproc->uio;
+		uio->name = uio_name;
+		uio->version = DRIVER_UIO_VERSION;
+		uio->irq = ksproc->irq_fault;
+		uio->priv = ksproc;
+		uio->handler = keystone_rproc_uio_handler;
+		uio->irqcontrol	= keystone_rproc_uio_irqcontrol;
+		ret = uio_register_device(dev, uio);
+		if (ret) {
+			dev_err(dev, "failed to register uio device, status = %d\n",
+				ret);
+			goto del_rproc;
+		}
+		dev_dbg(dev, "registered uio device %s\n", uio->name);
+
+		misc = &ksproc->misc;
+		misc->minor = MISC_DYNAMIC_MINOR;
+		misc->name = uio->name;
+		misc->fops = &keystone_rproc_fops;
+		misc->parent = dev;
+		ret = misc_register(misc);
+		if (ret) {
+			dev_err(dev, "failed to register misc device, status = %d\n",
+				ret);
+			goto unregister_uio;
+		}
+
+		ret = keystone_rproc_mem_add_attrs(ksproc);
+		if (ret) {
+			dev_err(ksproc->dev, "error creating sysfs files (%d)\n",
+				ret);
+			goto unregister_misc;
+		}
+
+		dev_dbg(dev, "registered misc device %s\n", misc->name);
+	}
+
 	return 0;
 
+unregister_misc:
+	misc_deregister(misc);
+unregister_uio:
+	uio_unregister_device(uio);
+del_rproc:
+	rproc_del(rproc);
 release_mem:
 	of_reserved_mem_device_release(dev);
 disable_clk:
@@ -492,6 +1109,11 @@ static int keystone_rproc_remove(struct platform_device *pdev)
 {
 	struct keystone_rproc *ksproc = platform_get_drvdata(pdev);
 
+	if (ksproc->use_userspace_loader) {
+		keystone_rproc_mem_del_attrs(ksproc);
+		misc_deregister(&ksproc->misc);
+		uio_unregister_device(&ksproc->uio);
+	}
 	rproc_del(ksproc->rproc);
 	pm_runtime_put_sync(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
@@ -519,8 +1141,22 @@ static struct platform_driver keystone_rproc_driver = {
 	},
 };
 
-module_platform_driver(keystone_rproc_driver);
+static int __init keystone_rproc_init(void)
+{
+	keystone_rproc_driver.driver.suppress_bind_attrs =
+		!use_rproc_core_loader;
+
+	return platform_driver_register(&keystone_rproc_driver);
+}
+module_init(keystone_rproc_init);
+
+static void __exit keystone_rproc_exit(void)
+{
+	platform_driver_unregister(&keystone_rproc_driver);
+}
+module_exit(keystone_rproc_exit);
 
 MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_AUTHOR("Sam Nelson <sam.nelson@ti.com>");
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("TI Keystone DSP Remoteproc driver");

+ 22 - 12
drivers/remoteproc/remoteproc_core.c

@@ -974,11 +974,14 @@ static int rproc_start(struct rproc *rproc, const struct firmware *fw)
 	struct device *dev = &rproc->dev;
 	int ret;
 
-	/* load the ELF segments to memory */
-	ret = rproc_load_segments(rproc, fw);
-	if (ret) {
-		dev_err(dev, "Failed to load program segments: %d\n", ret);
-		return ret;
+	if (!rproc->skip_load) {
+		/* load the ELF segments to memory */
+		ret = rproc_load_segments(rproc, fw);
+		if (ret) {
+			dev_err(dev, "Failed to load program segments: %d\n",
+				ret);
+			return ret;
+		}
 	}
 
 	/*
@@ -1046,7 +1049,11 @@ static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
 	if (ret)
 		return ret;
 
-	dev_info(dev, "Booting fw image %s, size %zd\n", name, fw->size);
+	if (!rproc->skip_firmware_request)
+		dev_info(dev, "Booting fw image %s, size %zd\n",
+			 name, fw->size);
+	else
+		dev_info(dev, "Booting unspecified pre-loaded fw image\n");
 
 	/*
 	 * if enabling an IOMMU isn't relevant for this rproc, this is
@@ -1410,16 +1417,19 @@ int rproc_boot(struct rproc *rproc)
 
 	dev_info(dev, "powering up %s\n", rproc->name);
 
-	/* load firmware */
-	ret = request_firmware(&firmware_p, rproc->firmware, dev);
-	if (ret < 0) {
-		dev_err(dev, "request_firmware failed: %d\n", ret);
-		goto downref_rproc;
+	if (!rproc->skip_firmware_request) {
+		/* load firmware */
+		ret = request_firmware(&firmware_p, rproc->firmware, dev);
+		if (ret < 0) {
+			dev_err(dev, "request_firmware failed: %d\n", ret);
+			goto downref_rproc;
+		}
 	}
 
 	ret = rproc_fw_boot(rproc, firmware_p);
 
-	release_firmware(firmware_p);
+	if (!rproc->skip_firmware_request)
+		release_firmware(firmware_p);
 
 downref_rproc:
 	if (ret)

+ 11 - 0
drivers/soc/ti/Kconfig

@@ -24,6 +24,17 @@ menuconfig SOC_TI
 
 if SOC_TI
 
+config KEYSTONE_DSP_MEM
+	tristate "TI Keystone DSP Memory Mapping Driver"
+	depends on ARCH_KEYSTONE
+	help
+	  Userspace memory mapping interface driver for TI Keystone SoCs.
+	  Provides access to MSM SRAM memory regions and dedicated DDR
+	  carveout memory regions to user space to aid userspace loading
+	  of the DSPs within the SoC.
+
+	  If unsure, say N.
+
 config KEYSTONE_NAVIGATOR_QMSS
 	tristate "Keystone Queue Manager Sub System"
 	depends on ARCH_KEYSTONE

+ 1 - 0
drivers/soc/ti/Makefile

@@ -2,6 +2,7 @@
 #
 # TI Keystone SOC drivers
 #
+obj-$(CONFIG_KEYSTONE_DSP_MEM)		+= keystone_dsp_mem.o
 obj-$(CONFIG_KEYSTONE_NAVIGATOR_QMSS)	+= knav_qmss.o
 knav_qmss-y := knav_qmss_queue.o knav_qmss_acc.o
 obj-$(CONFIG_KEYSTONE_NAVIGATOR_DMA)	+= knav_dma.o

+ 402 - 0
drivers/soc/ti/keystone_dsp_mem.c

@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * TI Keystone DSP Memory Mapping Driver
+ *
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <uapi/linux/keystone_dsp_mem.h>
+
+#define KEYSTONE_ALIAS_PHYS_START	0x80000000ULL
+#define KEYSTONE_ALIAS_PHYS_SIZE	0x80000000ULL /* 2G */
+
+#define KEYSTONE_HIGH_PHYS_START	0x800000000ULL
+#define KEYSTONE_HIGH_PHYS_LIMIT	(KEYSTONE_HIGH_PHYS_START + \
+					 KEYSTONE_ALIAS_PHYS_SIZE)
+
+#define to_alias_addr(addr)	(((addr) - KEYSTONE_HIGH_PHYS_START) + \
+				 KEYSTONE_ALIAS_PHYS_START)
+
+/**
+ * struct keystone_dsp_mem - internal memory structure
+ * @addr: physical address on the bus to access the memory region
+ * @size: size of the memory region
+ * @kobj: kobject for the sysfs directory file
+ */
+struct keystone_dsp_mem {
+	phys_addr_t addr;
+	resource_size_t size;
+	struct kobject kobj;
+};
+
+#define to_dsp_mem(obj) container_of(obj, struct keystone_dsp_mem, kobj)
+
+/**
+ * struct keystone_dsp_mem_info - Keystone DSP Memory device structure
+ * @misc: child miscdevice structure
+ * @mem: memory region array pointer
+ * @num_maps: number of memory regions
+ */
+struct keystone_dsp_mem_info {
+	struct miscdevice misc;
+	struct keystone_dsp_mem *mem;
+	int num_maps;
+};
+
+static struct keystone_dsp_mem_info *dsp_mem;
+
+#define to_dsp_mem_info(m) container_of(m, struct keystone_dsp_mem_info, misc)
+
+static ssize_t mem_addr_show(struct keystone_dsp_mem *mem, char *buf)
+{
+	return sprintf(buf, "%pa\n", &mem->addr);
+}
+
+static ssize_t mem_size_show(struct keystone_dsp_mem *mem, char *buf)
+{
+	return sprintf(buf, "%pa\n", &mem->size);
+}
+
+struct mem_sysfs_entry {
+	struct attribute attr;
+	ssize_t (*show)(struct keystone_dsp_mem *mem, char *buf);
+	ssize_t (*store)(struct keystone_dsp_mem *mem, const char *buf,
+			 size_t len);
+};
+
+static struct mem_sysfs_entry addr_attribute =
+	__ATTR(addr, 0444, mem_addr_show, NULL);
+static struct mem_sysfs_entry size_attribute =
+	__ATTR(size, 0444, mem_size_show, NULL);
+
+static struct attribute *attrs[] = {
+	&addr_attribute.attr,
+	&size_attribute.attr,
+	NULL,	/* sentinel */
+};
+
+static ssize_t mem_type_show(struct kobject *kobj, struct attribute *attr,
+			     char *buf)
+{
+	struct keystone_dsp_mem *mem = to_dsp_mem(kobj);
+	struct mem_sysfs_entry *entry;
+
+	entry = container_of(attr, struct mem_sysfs_entry, attr);
+	if (!entry->show)
+		return -EIO;
+
+	return entry->show(mem, buf);
+}
+
+static const struct sysfs_ops mem_sysfs_ops = {
+	.show = mem_type_show,
+};
+
+static struct kobj_type mem_attr_type = {
+	.sysfs_ops	= &mem_sysfs_ops,
+	.default_attrs	= attrs,
+};
+
+static int keystone_dsp_mem_add_attrs(struct keystone_dsp_mem_info *dsp_mem)
+{
+	int i, ret;
+	struct keystone_dsp_mem *mem;
+	struct kobject *kobj_parent = &dsp_mem->misc.this_device->kobj;
+
+	for (i = 0; i < dsp_mem->num_maps; i++) {
+		mem = &dsp_mem->mem[i];
+		kobject_init(&mem->kobj, &mem_attr_type);
+		ret = kobject_add(&mem->kobj, kobj_parent, "memory%d", i);
+		if (ret)
+			goto err_kobj;
+		ret = kobject_uevent(&mem->kobj, KOBJ_ADD);
+		if (ret)
+			goto err_kobj;
+	}
+
+	return 0;
+
+err_kobj:
+	for (; i >= 0; i--) {
+		mem = &dsp_mem->mem[i];
+		kobject_put(&mem->kobj);
+	}
+	return ret;
+}
+
+static void keystone_dsp_mem_del_attrs(struct keystone_dsp_mem_info *dsp_mem)
+{
+	int i;
+	struct keystone_dsp_mem *mem;
+
+	for (i = 0; i < dsp_mem->num_maps; i++) {
+		mem = &dsp_mem->mem[i];
+		kobject_put(&mem->kobj);
+	}
+}
+
+static int keystone_dsp_mem_check_addr(struct keystone_dsp_mem_info *dsp_mem,
+				       int mask, size_t size)
+{
+	size_t req_offset;
+	u32 index;
+
+	index = mask & KEYSTONE_DSP_MEM_MAP_INDEX_MASK;
+	if (index >= dsp_mem->num_maps) {
+		pr_err("%s: invalid mmap region index %d\n", __func__, index);
+		return -EINVAL;
+	}
+
+	req_offset = (mask - index) << PAGE_SHIFT;
+	if (req_offset + size < req_offset) {
+		pr_err("%s: invalid request - overflow, mmap offset = 0x%zx size 0x%zx region %d\n",
+		       __func__, req_offset, size, index);
+		return -EINVAL;
+	}
+
+	if ((req_offset + size) > dsp_mem->mem[index].size) {
+		pr_err("%s: invalid request - out of range, mmap offset 0x%zx size 0x%zx region %d\n",
+		       __func__, req_offset, size, index);
+		return -EINVAL;
+	}
+
+	return index;
+}
+
+/*
+ * This is a custom mmap function following semantics based on the UIO
+ * mmap implementation. The vm_pgoff passed in the vma structure is a
+ * combination of the memory region index and the actual page offset in
+ * that region. This checks if user request is in valid range before
+ * providing mmap access.
+ *
+ * XXX: Evaluate this approach, as the MSMC memory can be mapped in whole
+ * into userspace as it is not super-large, and the allowable kernel
+ * unmapped DDR memory can be mmaped using traditional mmap semantics.
+ */
+static int keystone_dsp_mem_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	size_t size = vma->vm_end - vma->vm_start;
+	struct miscdevice *misc = file->private_data;
+	struct keystone_dsp_mem_info *dsp_mem = to_dsp_mem_info(misc);
+	int index;
+
+	index = keystone_dsp_mem_check_addr(dsp_mem, vma->vm_pgoff, size);
+	if (index < 0)
+		return index;
+
+	vma->vm_page_prot =
+		phys_mem_access_prot(file,
+				     (dsp_mem->mem[index].addr >> PAGE_SHIFT) +
+				     (vma->vm_pgoff - index), size,
+				     vma->vm_page_prot);
+
+	if (remap_pfn_range(vma, vma->vm_start,
+			    (dsp_mem->mem[index].addr >> PAGE_SHIFT) +
+			    (vma->vm_pgoff - index), size, vma->vm_page_prot))
+		return -EAGAIN;
+
+	return 0;
+}
+
+static const struct file_operations keystone_dsp_mem_fops = {
+	.owner	= THIS_MODULE,
+	.mmap	= keystone_dsp_mem_mmap,
+};
+
+static int keystone_dsp_mem_parse(struct device_node *np, int index)
+{
+	phys_addr_t start, end, addr, size;
+	struct resource res;
+	resource_size_t rsize;
+	int ret, j;
+
+	if (!of_find_property(np, "no-map", NULL)) {
+		pr_err("dsp reserved memory regions without no-map are not supported\n");
+		return -EINVAL;
+	}
+
+	ret = of_address_to_resource(np, 0, &res);
+	if (ret)
+		return ret;
+
+	/* make sure only aliased addresses are covered */
+	rsize = resource_size(&res);
+	start = res.start;
+	end = res.start + rsize;
+	if (start < KEYSTONE_HIGH_PHYS_START ||
+	    start >= KEYSTONE_HIGH_PHYS_LIMIT ||
+	    end > KEYSTONE_HIGH_PHYS_LIMIT) {
+		pr_err("invalid address/size for keystone dsp memory carveout: %pa of size %pa\n",
+		       &start, &rsize);
+		return -EINVAL;
+	}
+
+	/* check for overlaps */
+	start = to_alias_addr(start);
+	end = to_alias_addr(end);
+	for (j = 0; j < index; j++) {
+		addr = dsp_mem->mem[j].addr;
+		size = dsp_mem->mem[j].size;
+		if ((end > addr && end <= addr + size) ||
+		    (start >= addr && start < addr + size) ||
+		    (start < addr && end > addr + size)) {
+			pr_err("dsp memory carveout (%pa of size %pa) overlaps with (%pa of size %pa)\n",
+			       &start, &rsize, &addr, &size);
+			return -EINVAL;
+		}
+	}
+
+	dsp_mem->mem[index].addr = to_alias_addr(res.start);
+	dsp_mem->mem[index].size = resource_size(&res);
+
+	return 0;
+}
+
+static int keystone_dsp_mem_init(void)
+{
+	struct miscdevice *misc;
+	struct resource res;
+	struct device_node *rmem_np, *sram_np, *np;
+	int ret, i = 0;
+	int num_maps = 0, num_sram = 0;
+
+	if (!of_have_populated_dt())
+		return -ENOTSUPP;
+
+	/* module is supported only on TI Keystone SoCs */
+	if (!of_machine_is_compatible("ti,keystone"))
+		return -ENOTSUPP;
+
+	/* count the number of DDR regions */
+	rmem_np = of_find_node_by_path("/reserved-memory");
+	if (rmem_np) {
+		for_each_available_child_of_node(rmem_np, np) {
+			if (of_device_is_compatible(np,
+						    "ti,keystone-dsp-mem-pool"))
+				num_maps++;
+		}
+	}
+
+	for_each_compatible_node(sram_np, NULL, "ti,keystone-dsp-msm-ram") {
+		if (!of_device_is_available(sram_np))
+			continue;
+		num_sram++;
+	}
+
+	if ((!num_maps && !num_sram) ||
+	    (num_maps + num_sram > KEYSTONE_DSP_MEM_MAP_INDEX_MASK)) {
+		ret = -EINVAL;
+		goto put_rmem;
+	}
+
+	dsp_mem = kzalloc(sizeof(*dsp_mem), GFP_KERNEL);
+	if (!dsp_mem) {
+		ret = -ENOMEM;
+		goto put_rmem;
+	}
+
+	dsp_mem->mem = kcalloc(num_maps + num_sram, sizeof(*dsp_mem->mem),
+			       GFP_KERNEL);
+	if (!dsp_mem->mem) {
+		ret = -ENOMEM;
+		goto free_dsp;
+	}
+
+	/* handle reserved-memory carveouts */
+	if (num_maps) {
+		for_each_available_child_of_node(rmem_np, np) {
+			if (!of_device_is_compatible(np,
+						"ti,keystone-dsp-mem-pool"))
+				continue;
+
+			ret = keystone_dsp_mem_parse(np, i);
+			if (ret) {
+				of_node_put(np);
+				goto free_mem;
+			}
+			i++;
+			dsp_mem->num_maps++;
+		}
+	}
+
+	/* handle on-chip SRAM reserved regions */
+	if (num_sram) {
+		for_each_compatible_node(sram_np, NULL,
+					 "ti,keystone-dsp-msm-ram") {
+			if (!of_device_is_available(sram_np))
+				continue;
+
+			ret = of_address_to_resource(sram_np, 0, &res);
+			if (ret) {
+				ret = -EINVAL;
+				of_node_put(sram_np);
+				goto free_mem;
+			}
+			dsp_mem->mem[i].addr = res.start;
+			dsp_mem->mem[i].size = resource_size(&res);
+			i++;
+			dsp_mem->num_maps++;
+		}
+	}
+
+	misc = &dsp_mem->misc;
+	misc->minor = MISC_DYNAMIC_MINOR;
+	misc->name = "dspmem";
+	misc->fops = &keystone_dsp_mem_fops;
+	misc->parent = NULL;
+	ret = misc_register(misc);
+	if (ret) {
+		pr_err("%s: could not register dspmem misc device\n", __func__);
+		goto free_mem;
+	}
+
+	ret = keystone_dsp_mem_add_attrs(dsp_mem);
+	if (ret) {
+		pr_err("%s: error creating sysfs files (%d)\n", __func__, ret);
+		goto unregister_misc;
+	}
+	of_node_put(rmem_np);
+
+	pr_info("registered dspmem misc device\n");
+
+	return 0;
+
+unregister_misc:
+	misc_deregister(&dsp_mem->misc);
+free_mem:
+	kfree(dsp_mem->mem);
+free_dsp:
+	kfree(dsp_mem);
+	dsp_mem = NULL;
+put_rmem:
+	of_node_put(rmem_np);
+	return ret;
+}
+
+static void keystone_dsp_mem_exit(void)
+{
+	keystone_dsp_mem_del_attrs(dsp_mem);
+
+	misc_deregister(&dsp_mem->misc);
+
+	kfree(dsp_mem->mem);
+	kfree(dsp_mem);
+	dsp_mem = NULL;
+}
+
+module_init(keystone_dsp_mem_init);
+module_exit(keystone_dsp_mem_exit);
+
+MODULE_AUTHOR("Suman Anna <s-anna@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("TI Keystone DSP Memory Mapping Driver");

+ 4 - 0
include/linux/remoteproc.h

@@ -442,6 +442,8 @@ struct rproc_dump_segment {
  * @table_sz: size of @cached_table
  * @has_iommu: flag to indicate if remote processor is behind an MMU
  * @deny_sysfs_ops: flag to not permit sysfs operations on state and firmware
+ * @skip_firmware_request: flag to skip requesting the firmware
+ * @skip_load: flag to skip the loading of firmware segments
  * @dump_segments: list of segments in the firmware
  */
 struct rproc {
@@ -475,6 +477,8 @@ struct rproc {
 	bool has_iommu;
 	bool auto_boot;
 	unsigned int deny_sysfs_ops		: 1;
+	unsigned int skip_firmware_request	: 1;
+	unsigned int skip_load			: 1;
 	struct list_head dump_segments;
 };
 

+ 12 - 0
include/uapi/linux/keystone_dsp_mem.h

@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef _UAPI_KEYSTONE_DSP_MEM_H_
+#define _UAPI_KEYSTONE_DSP_MEM_H_
+
+#define KEYSTONE_DSP_MEM_MAP_INDEX_MASK		(0x7)
+#define KEYSTONE_DSP_MEM_MAP_OFFSET_SHIFT	(3)
+
+#endif /* _UAPI_KEYSTONE_DSP_MEM_H_ */

+ 56 - 0
include/uapi/linux/keystone_remoteproc.h

@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#ifndef _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_
+#define _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+/**
+ * enum keystone_rproc_state - keystone remoteproc state setting values
+ *
+ * @KEYSTONE_RPROC_OFFLINE: request to configure the remoteproc into an offline
+ *			    state
+ * @KEYSTONE_RPROC_RUNNING: request to configure the remoteproc into a ready
+ *			    state
+ */
+enum keystone_rproc_state {
+	KEYSTONE_RPROC_OFFLINE,
+	KEYSTONE_RPROC_RUNNING,
+};
+
+/**
+ * struct keystone_rproc_set_state_params - keystone remoteproc set state
+ *					    parameters structure
+ *
+ * @state: enumerated state value to set
+ * @boot_addr: boot address/entry point for the remote processor
+ */
+struct keystone_rproc_set_state_params {
+	enum keystone_rproc_state state;
+	uint32_t boot_addr;
+};
+
+/* Macros used within mmap function */
+#define KEYSTONE_RPROC_UIO_MAP_INDEX_MASK	(0x7)
+#define KEYSTONE_RPROC_UIO_MAP_OFFSET_SHIFT	(3)
+
+/* IOCTL definitions */
+#define KEYSTONE_RPROC_IOC_MAGIC		'I'
+#define KEYSTONE_RPROC_IOC_SET_RSC_TABLE	_IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+						0, void *)
+#define KEYSTONE_RPROC_IOC_SET_STATE	_IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+					1, \
+					struct keystone_rproc_set_state_params)
+#define KEYSTONE_RPROC_IOC_SET_LOADED_RSC_TABLE _IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+						2, uint32_t)
+#define KEYSTONE_RPROC_IOC_DSP_RESET		_IO(KEYSTONE_RPROC_IOC_MAGIC, 3)
+#define KEYSTONE_RPROC_IOC_DSP_BOOT		_IOW(KEYSTONE_RPROC_IOC_MAGIC, \
+						4, uint32_t)
+
+#define KEYSTONE_RPROC_IOC_MAXNR		(5)
+
+#endif /* _UAPI_LINUX_KEYSTONE_REMOTEPROC_H_ */

+ 2 - 0
net/rpmsg/rpmsg_proto.c

@@ -191,6 +191,8 @@ static int rpmsg_sock_sendmsg(struct socket *sock, struct msghdr *msg,
 	err = rpmsg_send(rpsk->rpdev->ept, payload, len);
 	if (err)
 		pr_err("rpmsg_send failed: %d\n", err);
+	else
+		err = len;
 
 out:
 	release_sock(sk);

+ 7 - 0
ti_config_fragments/ipc.cfg

@@ -11,7 +11,14 @@ CONFIG_OMAP2PLUS_MBOX=y
 
 # Remoteproc
 CONFIG_REMOTEPROC=m
+CONFIG_KEYSTONE_REMOTEPROC=m
 
 # RPMsg
 CONFIG_RPMSG_VIRTIO=m
 CONFIG_RPMSG_PROTO=m
+
+# DSP Memory Mapper for Keystone MPM
+CONFIG_KEYSTONE_DSP_MEM=m
+
+# UIO Module
+CONFIG_UIO=m