From 4980f465d21400303b234bcc3d082916f19f11ff Mon Sep 17 00:00:00 2001 From: Juan Gutierrez Date: Wed, 15 Aug 2012 10:25:48 -0500 Subject: remoteproc/omap: set bootaddr support Some remote processors (like OMAP4's DSP) require we explicitly set a boot address from which they'd start executing code when taken out of reset. Support for this is now being added to the omap-specific remoteproc driver through a set_bootaddr function in the platform data which, if needed, must be set according to the backend remote processor. For OMAP4's dsp we can use the following control function: .set_bootaddr = omap_ctrl_write_dsp_boot_addr Signed-off-by: Juan Gutierrez Signed-off-by: Suman Anna [ohad: slight changes to the commit log] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/omap_remoteproc.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c index a1f7ac1..a0c168c 100644 --- a/drivers/remoteproc/omap_remoteproc.c +++ b/drivers/remoteproc/omap_remoteproc.c @@ -116,6 +116,9 @@ static int omap_rproc_start(struct rproc *rproc) struct omap_rproc_pdata *pdata = pdev->dev.platform_data; int ret; + if (pdata->set_bootaddr) + pdata->set_bootaddr(rproc->bootaddr); + oproc->nb.notifier_call = omap_rproc_mbox_callback; /* every omap rproc is assigned a mailbox instance for messaging */ -- cgit v1.1 From a1a7e0a33ade47d65abc07cddf015b5c576cd772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Thu, 13 Sep 2012 20:03:58 +0200 Subject: remoteproc: Add dependency to HAS_DMA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remoteproc relies on HAS_DMA, add this dependency in Kconfig. Cc: Rusty Russell Signed-off-by: Sjur Brændeland Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/Kconfig | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index f8d818a..b4d8de5 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -4,11 +4,13 @@ menu "Remoteproc drivers (EXPERIMENTAL)" config REMOTEPROC tristate depends on EXPERIMENTAL + depends on HAS_DMA select FW_CONFIG config OMAP_REMOTEPROC tristate "OMAP remoteproc support" depends on EXPERIMENTAL + depends on HAS_DMA depends on ARCH_OMAP4 depends on OMAP_IOMMU select REMOTEPROC -- cgit v1.1 From 8afd519c3470f685f964deebd61aa51d83cde90a Mon Sep 17 00:00:00 2001 From: Fernando Guzman Lugo Date: Thu, 30 Aug 2012 13:26:12 -0500 Subject: remoteproc: add rproc_report_crash function to notify rproc crashes Allow low-level remoteproc drivers to report rproc crashes by exporting a new rproc_report_crash() function (invoking this from non-rproc drivers is probably wrong, and should be carefully scrutinized if ever needed). rproc_report_crash() can be called from any context; it offloads the tasks of handling the crash to a separate thread. Handling the crash from a separate thread is helpful because: - Ability to call invoke rproc_report_crash() from atomic context, due to the fact that many crashes trigger an interrupt, so this function can be called directly from ISR context. - Avoiding deadlocks which could happen if rproc_report_crash() is called from a function which indirectly holds the rproc lock. Handling the crash might involve: - Remoteproc register dump - Remoteproc stack dump - Remoteproc core dump - Saving Remoteproc traces so they can be read after the crash - Reseting the remoteproc in order to make it functional again (hard recovery) Right now, we only print the crash type which was detected, and only the mmufault type is supported. Remoteproc low-level drivers can add more types when needed. Signed-off-by: Fernando Guzman Lugo [ohad: some commentary, white space and commit log changes] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 79 ++++++++++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index d5c2dbf..93e2b35 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -50,6 +50,18 @@ typedef int (*rproc_handle_resource_t)(struct rproc *rproc, void *, int avail); /* Unique indices for remoteproc devices */ static DEFINE_IDA(rproc_dev_index); +static const char * const rproc_crash_names[] = { + [RPROC_MMUFAULT] = "mmufault", +}; + +/* translate rproc_crash_type to string */ +static const char *rproc_crash_to_string(enum rproc_crash_type type) +{ + if (type < ARRAY_SIZE(rproc_crash_names)) + return rproc_crash_names[type]; + return "unkown"; +} + /* * This is the IOMMU fault handler we register with the IOMMU API * (when relevant; not all remote processors access memory through @@ -57,18 +69,19 @@ static DEFINE_IDA(rproc_dev_index); * * IOMMU core will invoke this handler whenever the remote processor * will try to access an unmapped device address. - * - * Currently this is mostly a stub, but it will be later used to trigger - * the recovery of the remote processor. */ static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, unsigned long iova, int flags, void *token) { + struct rproc *rproc = token; + dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags); + rproc_report_crash(rproc, RPROC_MMUFAULT); + /* * Let the iommu core know we're not really handling this fault; - * we just plan to use this as a recovery trigger. + * we just used it as a recovery trigger. */ return -ENOSYS; } @@ -872,6 +885,36 @@ out: } /** + * rproc_crash_handler_work() - handle a crash + * + * This function needs to handle everything related to a crash, like cpu + * registers and stack dump, information to help to debug the fatal error, etc. + */ +static void rproc_crash_handler_work(struct work_struct *work) +{ + struct rproc *rproc = container_of(work, struct rproc, crash_handler); + struct device *dev = &rproc->dev; + + dev_dbg(dev, "enter %s\n", __func__); + + mutex_lock(&rproc->lock); + + if (rproc->state == RPROC_CRASHED || rproc->state == RPROC_OFFLINE) { + /* handle only the first crash detected */ + mutex_unlock(&rproc->lock); + return; + } + + rproc->state = RPROC_CRASHED; + dev_err(dev, "handling crash #%u in %s\n", ++rproc->crash_cnt, + rproc->name); + + mutex_unlock(&rproc->lock); + + /* TODO: handle crash */ +} + +/** * rproc_boot() - boot a remote processor * @rproc: handle of a remote processor * @@ -1165,6 +1208,8 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, INIT_LIST_HEAD(&rproc->traces); INIT_LIST_HEAD(&rproc->rvdevs); + INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); + rproc->state = RPROC_OFFLINE; return rproc; @@ -1221,6 +1266,32 @@ int rproc_del(struct rproc *rproc) } EXPORT_SYMBOL(rproc_del); +/** + * rproc_report_crash() - rproc crash reporter function + * @rproc: remote processor + * @type: crash type + * + * This function must be called every time a crash is detected by the low-level + * drivers implementing a specific remoteproc. This should not be called from a + * non-remoteproc driver. + * + * This function can be called from atomic/interrupt context. + */ +void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type) +{ + if (!rproc) { + pr_err("NULL rproc pointer\n"); + return; + } + + dev_err(&rproc->dev, "crash detected in %s: type %s\n", + rproc->name, rproc_crash_to_string(type)); + + /* create a new task to handle the error */ + schedule_work(&rproc->crash_handler); +} +EXPORT_SYMBOL(rproc_report_crash); + static int __init remoteproc_init(void) { rproc_init_debugfs(); -- cgit v1.1 From 70b85ef83ce3523f709b622d2c4cb31778686338 Mon Sep 17 00:00:00 2001 From: Fernando Guzman Lugo Date: Thu, 30 Aug 2012 13:26:13 -0500 Subject: remoteproc: add actual recovery implementation Add rproc_trigger_recovery() which takes care of the recovery itself, by removing, and re-adding, all of the remoteproc's virtio devices. This resets all virtio users of the remote processor, during which the remote processor is powered off and on again. Signed-off-by: Fernando Guzman Lugo [ohad: introduce rproc_add_virtio_devices to avoid 1.copying code 2.anomaly] [ohad: some white space, naming and commit log changes] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 84 +++++++++++++++++++++++--------- drivers/remoteproc/remoteproc_internal.h | 1 + 2 files changed, 63 insertions(+), 22 deletions(-) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 93e2b35..5000d75 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -884,6 +884,60 @@ out: complete_all(&rproc->firmware_loading_complete); } +static int rproc_add_virtio_devices(struct rproc *rproc) +{ + int ret; + + /* rproc_del() calls must wait until async loader completes */ + init_completion(&rproc->firmware_loading_complete); + + /* + * We must retrieve early virtio configuration info from + * the firmware (e.g. whether to register a virtio device, + * what virtio features does it support, ...). + * + * We're initiating an asynchronous firmware loading, so we can + * be built-in kernel code, without hanging the boot process. + */ + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, + rproc->firmware, &rproc->dev, GFP_KERNEL, + rproc, rproc_fw_config_virtio); + if (ret < 0) { + dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret); + complete_all(&rproc->firmware_loading_complete); + } + + return ret; +} + +/** + * rproc_trigger_recovery() - recover a remoteproc + * @rproc: the remote processor + * + * The recovery is done by reseting all the virtio devices, that way all the + * rpmsg drivers will be reseted along with the remote processor making the + * remoteproc functional again. + * + * This function can sleep, so it cannot be called from atomic context. + */ +int rproc_trigger_recovery(struct rproc *rproc) +{ + struct rproc_vdev *rvdev, *rvtmp; + + dev_err(&rproc->dev, "recovering %s\n", rproc->name); + + init_completion(&rproc->crash_comp); + + /* clean up remote vdev entries */ + list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node) + rproc_remove_virtio_dev(rvdev); + + /* wait until there is no more rproc users */ + wait_for_completion(&rproc->crash_comp); + + return rproc_add_virtio_devices(rproc); +} + /** * rproc_crash_handler_work() - handle a crash * @@ -911,7 +965,7 @@ static void rproc_crash_handler_work(struct work_struct *work) mutex_unlock(&rproc->lock); - /* TODO: handle crash */ + rproc_trigger_recovery(rproc); } /** @@ -1035,6 +1089,10 @@ void rproc_shutdown(struct rproc *rproc) rproc_disable_iommu(rproc); + /* if in crash state, unlock crash handler */ + if (rproc->state == RPROC_CRASHED) + complete_all(&rproc->crash_comp); + rproc->state = RPROC_OFFLINE; dev_info(dev, "stopped remote processor %s\n", rproc->name); @@ -1069,7 +1127,7 @@ EXPORT_SYMBOL(rproc_shutdown); int rproc_add(struct rproc *rproc) { struct device *dev = &rproc->dev; - int ret = 0; + int ret; ret = device_add(dev); if (ret < 0) @@ -1083,26 +1141,7 @@ int rproc_add(struct rproc *rproc) /* create debugfs entries */ rproc_create_debug_dir(rproc); - /* rproc_del() calls must wait until async loader completes */ - init_completion(&rproc->firmware_loading_complete); - - /* - * We must retrieve early virtio configuration info from - * the firmware (e.g. whether to register a virtio device, - * what virtio features does it support, ...). - * - * We're initiating an asynchronous firmware loading, so we can - * be built-in kernel code, without hanging the boot process. - */ - ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG, - rproc->firmware, dev, GFP_KERNEL, - rproc, rproc_fw_config_virtio); - if (ret < 0) { - dev_err(dev, "request_firmware_nowait failed: %d\n", ret); - complete_all(&rproc->firmware_loading_complete); - } - - return ret; + return rproc_add_virtio_devices(rproc); } EXPORT_SYMBOL(rproc_add); @@ -1209,6 +1248,7 @@ struct rproc *rproc_alloc(struct device *dev, const char *name, INIT_LIST_HEAD(&rproc->rvdevs); INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work); + init_completion(&rproc->crash_comp); rproc->state = RPROC_OFFLINE; diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h index a690ebe..7bb6648 100644 --- a/drivers/remoteproc/remoteproc_internal.h +++ b/drivers/remoteproc/remoteproc_internal.h @@ -63,6 +63,7 @@ void rproc_free_vring(struct rproc_vring *rvring); int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); +int rproc_trigger_recovery(struct rproc *rproc); static inline int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) -- cgit v1.1 From 2e37abb89a2ef13c524b0728bb9893f996a10b6b Mon Sep 17 00:00:00 2001 From: Fernando Guzman Lugo Date: Tue, 18 Sep 2012 12:26:35 +0300 Subject: remoteproc: create a 'recovery' debugfs entry Add a 'recovery' debugfs entry to dynamically disable/enable recovery at runtime. This is useful when one is trying to debug an rproc crash; without it, a recovery will immediately take place, making it harder to debug the crash. Contributions from Subramaniam Chanderashekarapuram. Examples: - disabling recovery: $ echo disabled > /remoteproc/remoteproc0/recovery - in case you want to recover a crash, but keep recovery disabled (useful in debugging sessions when you expect additional crashes you want to debug): $ echo recover > /remoteproc/remoteproc0/recovery - enabling recovery: $ echo enabled > /remoteproc/remoteproc0/recovery Signed-off-by: Fernando Guzman Lugo [ohad: some white space, commentary and commit log changes] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 3 +- drivers/remoteproc/remoteproc_debugfs.c | 81 +++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 5000d75..29fc823 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -965,7 +965,8 @@ static void rproc_crash_handler_work(struct work_struct *work) mutex_unlock(&rproc->lock); - rproc_trigger_recovery(rproc); + if (!rproc->recovery_disabled) + rproc_trigger_recovery(rproc); } /** diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 0383385..10a3825 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -28,6 +28,9 @@ #include #include #include +#include + +#include "remoteproc_internal.h" /* remoteproc debugfs parent dir */ static struct dentry *rproc_dbg; @@ -111,6 +114,82 @@ static const struct file_operations rproc_name_ops = { .llseek = generic_file_llseek, }; +/* expose recovery flag via debugfs */ +static ssize_t rproc_recovery_read(struct file *filp, char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + char *buf = rproc->recovery_disabled ? "disabled\n" : "enabled\n"; + + return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf)); +} + +/* + * By writing to the 'recovery' debugfs entry, we control the behavior of the + * recovery mechanism dynamically. The default value of this entry is "enabled". + * + * The 'recovery' debugfs entry supports these commands: + * + * enabled: When enabled, the remote processor will be automatically + * recovered whenever it crashes. Moreover, if the remote + * processor crashes while recovery is disabled, it will + * be automatically recovered too as soon as recovery is enabled. + * + * disabled: When disabled, a remote processor will remain in a crashed + * state if it crashes. This is useful for debugging purposes; + * without it, debugging a crash is substantially harder. + * + * recover: This function will trigger an immediate recovery if the + * remote processor is in a crashed state, without changing + * or checking the recovery state (enabled/disabled). + * This is useful during debugging sessions, when one expects + * additional crashes to happen after enabling recovery. In this + * case, enabling recovery will make it hard to debug subsequent + * crashes, so it's recommended to keep recovery disabled, and + * instead use the "recover" command as needed. + */ +static ssize_t +rproc_recovery_write(struct file *filp, const char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct rproc *rproc = filp->private_data; + char buf[10]; + int ret; + + if (count > sizeof(buf)) + return count; + + ret = copy_from_user(buf, user_buf, count); + if (ret) + return ret; + + /* remove end of line */ + if (buf[count - 1] == '\n') + buf[count - 1] = '\0'; + + if (!strncmp(buf, "enabled", count)) { + rproc->recovery_disabled = false; + /* if rproc has crashed, trigger recovery */ + if (rproc->state == RPROC_CRASHED) + rproc_trigger_recovery(rproc); + } else if (!strncmp(buf, "disabled", count)) { + rproc->recovery_disabled = true; + } else if (!strncmp(buf, "recover", count)) { + /* if rproc has crashed, trigger recovery */ + if (rproc->state == RPROC_CRASHED) + rproc_trigger_recovery(rproc); + } + + return count; +} + +static const struct file_operations rproc_recovery_ops = { + .read = rproc_recovery_read, + .write = rproc_recovery_write, + .open = simple_open, + .llseek = generic_file_llseek, +}; + void rproc_remove_trace_file(struct dentry *tfile) { debugfs_remove(tfile); @@ -154,6 +233,8 @@ void rproc_create_debug_dir(struct rproc *rproc) rproc, &rproc_name_ops); debugfs_create_file("state", 0400, rproc->dbg_dir, rproc, &rproc_state_ops); + debugfs_create_file("recovery", 0400, rproc->dbg_dir, + rproc, &rproc_recovery_ops); } void __init rproc_init_debugfs(void) -- cgit v1.1 From 099a3f33c82b5153a4422eb92c648098b3f7c086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Tue, 18 Sep 2012 20:32:45 +0200 Subject: remtoteproc: maintain max notifyid MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the rproc drivers (STE modem specifically) needs to know the range of the notification IDs used for notifying the device. Maintain a variable in struct rproc holding the largest allocated notification id, so low-level rproc drivers could access it. Signed-off-by: Sjur Brændeland [ohad: rebase, slightly edit commit log] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index 29fc823..b6c6229 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -228,6 +228,9 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) return ret; } + /* Store largest notifyid */ + rproc->max_notifyid = max(rproc->max_notifyid, notifyid); + dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va, dma, size, notifyid); @@ -269,13 +272,25 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i) return 0; } +static int rproc_max_notifyid(int id, void *p, void *data) +{ + int *maxid = data; + *maxid = max(*maxid, id); + return 0; +} + void rproc_free_vring(struct rproc_vring *rvring) { int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); struct rproc *rproc = rvring->rvdev->rproc; + int maxid = 0; dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); idr_remove(&rproc->notifyids, rvring->notifyid); + + /* Find the largest remaining notifyid */ + idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid); + rproc->max_notifyid = maxid; } /** -- cgit v1.1 From ec4d02d9180f407c41f8310a13b34e473c671fbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sjur=20Br=C3=A6ndeland?= Date: Thu, 20 Sep 2012 18:32:56 +0200 Subject: remoteproc: Add STE modem driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add support for the STE modem shared memory driver. This driver hooks into the remoteproc framework in order to manage configuration and the virtio devices. This driver adds custom firmware handlers, because STE modem uses a custom firmware layout. Signed-off-by: Sjur Brændeland cc: Linus Walleij cc: Alan Cox [ohad: validate mdev->ops, move setup() to probe/remove, trivial style changes] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/Kconfig | 11 ++ drivers/remoteproc/Makefile | 1 + drivers/remoteproc/ste_modem_rproc.c | 322 +++++++++++++++++++++++++++++++++++ 3 files changed, 334 insertions(+) create mode 100644 drivers/remoteproc/ste_modem_rproc.c (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index b4d8de5..a1024d9 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -29,4 +29,15 @@ config OMAP_REMOTEPROC It's safe to say n here if you're not interested in multimedia offloading or just want a bare minimum kernel. +config STE_MODEM_RPROC + tristate "STE-Modem remoteproc support" + depends on EXPERIMENTAL + depends on HAS_DMA + select REMOTEPROC + default n + help + Say y or m here to support STE-Modem shared memory driver. + 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 934ce6e..391b651 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -8,3 +8,4 @@ remoteproc-y += remoteproc_debugfs.o remoteproc-y += remoteproc_virtio.o remoteproc-y += remoteproc_elf_loader.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o +obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c new file mode 100644 index 0000000..a7743c0 --- /dev/null +++ b/drivers/remoteproc/ste_modem_rproc.c @@ -0,0 +1,322 @@ +/* + * Copyright (C) ST-Ericsson AB 2012 + * Author: Sjur Brændeland + * License terms: GNU General Public License (GPL), version 2 + */ + +#include +#include +#include +#include +#include "remoteproc_internal.h" + +#define SPROC_FW_SIZE (50 * 4096) +#define SPROC_MAX_TOC_ENTRIES 32 +#define SPROC_MAX_NOTIFY_ID 14 +#define SPROC_RESOURCE_NAME "rsc-table" +#define SPROC_MODEM_NAME "ste-modem" +#define SPROC_MODEM_FIRMWARE SPROC_MODEM_NAME "-fw.bin" + +#define sproc_dbg(sproc, fmt, ...) \ + dev_dbg(&sproc->mdev->pdev.dev, fmt, ##__VA_ARGS__) +#define sproc_err(sproc, fmt, ...) \ + dev_err(&sproc->mdev->pdev.dev, fmt, ##__VA_ARGS__) + +/* STE-modem control structure */ +struct sproc { + struct rproc *rproc; + struct ste_modem_device *mdev; + int error; + void *fw_addr; + size_t fw_size; + dma_addr_t fw_dma_addr; +}; + +/* STE-Modem firmware entry */ +struct ste_toc_entry { + __le32 start; + __le32 size; + __le32 flags; + __le32 entry_point; + __le32 load_addr; + char name[12]; +}; + +/* + * The Table Of Content is located at the start of the firmware image and + * at offset zero in the shared memory region. The resource table typically + * contains the initial boot image (boot strap) and other information elements + * such as remoteproc resource table. Each entry is identified by a unique + * name. + */ +struct ste_toc { + struct ste_toc_entry table[SPROC_MAX_TOC_ENTRIES]; +}; + +/* Loads the firmware to shared memory. */ +static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw) +{ + struct sproc *sproc = rproc->priv; + + memcpy(sproc->fw_addr, fw->data, fw->size); + + return 0; +} + +/* Find the entry for resource table in the Table of Content */ +static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw) +{ + int i; + struct ste_toc *toc; + + if (!fw) + return NULL; + + toc = (void *)fw->data; + + /* Search the table for the resource table */ + for (i = 0; i < SPROC_MAX_TOC_ENTRIES && + toc->table[i].start != 0xffffffff; i++) { + + if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME, + sizeof(toc->table[i].name))) { + if (toc->table[i].start > fw->size) + return NULL; + return &toc->table[i]; + } + } + + return NULL; +} + +/* Find the resource table inside the remote processor's firmware. */ +static struct resource_table * +sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw, + int *tablesz) +{ + struct sproc *sproc = rproc->priv; + struct resource_table *table; + struct ste_toc_entry *entry; + + entry = sproc_find_rsc_entry(fw); + if (!entry) { + sproc_err(sproc, "resource table not found in fw\n"); + return NULL; + } + + table = (void *)(fw->data + entry->start); + + /* sanity check size and offset of resource table */ + if (entry->start > SPROC_FW_SIZE || + entry->size > SPROC_FW_SIZE || + fw->size > SPROC_FW_SIZE || + entry->start + entry->size > fw->size || + sizeof(struct resource_table) > entry->size) { + sproc_err(sproc, "bad size of fw or resource table\n"); + return NULL; + } + + /* we don't support any version beyond the first */ + if (table->ver != 1) { + sproc_err(sproc, "unsupported fw ver: %d\n", table->ver); + return NULL; + } + + /* make sure reserved bytes are zeroes */ + if (table->reserved[0] || table->reserved[1]) { + sproc_err(sproc, "non zero reserved bytes\n"); + return NULL; + } + + /* make sure the offsets array isn't truncated */ + if (table->num > SPROC_MAX_TOC_ENTRIES || + table->num * sizeof(table->offset[0]) + + sizeof(struct resource_table) > entry->size) { + sproc_err(sproc, "resource table incomplete\n"); + return NULL; + } + + /* If the fw size has grown, release the previous fw allocation */ + if (SPROC_FW_SIZE < fw->size) { + sproc_err(sproc, "Insufficient space for fw (%d < %zd)\n", + SPROC_FW_SIZE, fw->size); + return NULL; + } + + sproc->fw_size = fw->size; + *tablesz = entry->size; + + return table; +} + +/* STE modem firmware handler operations */ +const struct rproc_fw_ops sproc_fw_ops = { + .load = sproc_load_segments, + .find_rsc_table = sproc_find_rsc_table, +}; + +/* Kick the modem with specified notification id */ +static void sproc_kick(struct rproc *rproc, int vqid) +{ + struct sproc *sproc = rproc->priv; + + sproc_dbg(sproc, "kick vqid:%d\n", vqid); + + /* + * We need different notification IDs for RX and TX so add + * an offset on TX notification IDs. + */ + sproc->mdev->ops.kick(sproc->mdev, vqid + SPROC_MAX_NOTIFY_ID); +} + +/* Received a kick from a modem, kick the virtqueue */ +static void sproc_kick_callback(struct ste_modem_device *mdev, int vqid) +{ + struct sproc *sproc = mdev->drv_data; + + if (rproc_vq_interrupt(sproc->rproc, vqid) == IRQ_NONE) + sproc_dbg(sproc, "no message was found in vqid %d\n", vqid); +} + +struct ste_modem_dev_cb sproc_dev_cb = { + .kick = sproc_kick_callback, +}; + +/* Start the STE modem */ +static int sproc_start(struct rproc *rproc) +{ + struct sproc *sproc = rproc->priv; + int i, err; + + sproc_dbg(sproc, "start ste-modem\n"); + + /* Sanity test the max_notifyid */ + if (rproc->max_notifyid > SPROC_MAX_NOTIFY_ID) { + sproc_err(sproc, "Notification IDs too high:%d\n", + rproc->max_notifyid); + return -EINVAL; + } + + /* Subscribe to notifications */ + for (i = 0; i < rproc->max_notifyid; i++) { + err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i); + if (err) { + sproc_err(sproc, + "subscription of kicks failed:%d\n", err); + return err; + } + } + + /* Request modem start-up*/ + return sproc->mdev->ops.power(sproc->mdev, true); +} + +/* Stop the STE modem */ +static int sproc_stop(struct rproc *rproc) +{ + struct sproc *sproc = rproc->priv; + sproc_dbg(sproc, "stop ste-modem\n"); + + return sproc->mdev->ops.power(sproc->mdev, false); +} + +static struct rproc_ops sproc_ops = { + .start = sproc_start, + .stop = sproc_stop, + .kick = sproc_kick, +}; + +/* STE modem device is unregistered */ +static int sproc_drv_remove(struct platform_device *pdev) +{ + struct ste_modem_device *mdev = + container_of(pdev, struct ste_modem_device, pdev); + struct sproc *sproc = mdev->drv_data; + + sproc_dbg(sproc, "remove ste-modem\n"); + + /* Reset device callback functions */ + sproc->mdev->ops.setup(sproc->mdev, NULL); + + /* Unregister as remoteproc device */ + rproc_del(sproc->rproc); + rproc_put(sproc->rproc); + + mdev->drv_data = NULL; + + return 0; +} + +/* Handle probe of a modem device */ +static int sproc_probe(struct platform_device *pdev) +{ + struct ste_modem_device *mdev = + container_of(pdev, struct ste_modem_device, pdev); + struct sproc *sproc; + struct rproc *rproc; + int err; + + dev_dbg(&mdev->pdev.dev, "probe ste-modem\n"); + + if (!mdev->ops.setup || !mdev->ops.kick || !mdev->ops.kick_subscribe || + !mdev->ops.power) { + dev_err(&mdev->pdev.dev, "invalid mdev ops\n"); + return -EINVAL; + } + + rproc = rproc_alloc(&mdev->pdev.dev, mdev->pdev.name, &sproc_ops, + SPROC_MODEM_FIRMWARE, sizeof(*sproc)); + if (!rproc) + return -ENOMEM; + + sproc = rproc->priv; + sproc->mdev = mdev; + sproc->rproc = rproc; + mdev->drv_data = sproc; + + /* Provide callback functions to modem device */ + sproc->mdev->ops.setup(sproc->mdev, &sproc_dev_cb); + + /* Set the STE-modem specific firmware handler */ + rproc->fw_ops = &sproc_fw_ops; + + /* + * STE-modem requires the firmware to be located + * at the start of the shared memory region. So we need to + * reserve space for firmware at the start. + */ + sproc->fw_addr = dma_alloc_coherent(rproc->dev.parent, SPROC_FW_SIZE, + &sproc->fw_dma_addr, + GFP_KERNEL); + if (!sproc->fw_addr) { + sproc_err(sproc, "Cannot allocate memory for fw\n"); + err = -ENOMEM; + goto free_rproc; + } + + /* Register as a remoteproc device */ + err = rproc_add(rproc); + if (err) + goto free_rproc; + + return 0; + +free_rproc: + /* Reset device data upon error */ + mdev->drv_data = NULL; + rproc_put(rproc); + return err; +} + +static struct platform_driver sproc_driver = { + .driver = { + .name = SPROC_MODEM_NAME, + .owner = THIS_MODULE, + }, + .probe = sproc_probe, + .remove = sproc_drv_remove, +}; + +module_platform_driver(sproc_driver); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("STE Modem driver using the Remote Processor Framework"); -- cgit v1.1 From ae768d5fac18cd4b3ff51a4860b89fabba8ccc65 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Sep 2012 10:02:51 +0300 Subject: remoteproc: snprintf() can return more than was printed snprintf() returns the number of characters which would have been printed if there were enough space. For example, on the first print if we fill up the 28 character string then it would return a number more than 30. Use scnprintf() instead because that returns the actual number of characters printed. Signed-off-by: Dan Carpenter Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_debugfs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index 10a3825..ea90a56 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -82,7 +82,7 @@ static ssize_t rproc_state_read(struct file *filp, char __user *userbuf, state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; - i = snprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state], + i = scnprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state], rproc->state); return simple_read_from_buffer(userbuf, count, ppos, buf, i); @@ -103,7 +103,7 @@ static ssize_t rproc_name_read(struct file *filp, char __user *userbuf, char buf[100]; int i; - i = snprintf(buf, sizeof(buf), "%.98s\n", rproc->name); + i = scnprintf(buf, sizeof(buf), "%.98s\n", rproc->name); return simple_read_from_buffer(userbuf, count, ppos, buf, i); } -- cgit v1.1 From bec109a430e8c67bae1743f7e71898283234a77f Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Sep 2012 10:05:33 +0300 Subject: remoteproc: return -EFAULT on copy_from_user failure copy_from_user() returns the number of bytes remaining to be copied, but we want to return an error code here. Signed-off-by: Dan Carpenter Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_debugfs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c index ea90a56..157a573 100644 --- a/drivers/remoteproc/remoteproc_debugfs.c +++ b/drivers/remoteproc/remoteproc_debugfs.c @@ -161,7 +161,7 @@ rproc_recovery_write(struct file *filp, const char __user *user_buf, ret = copy_from_user(buf, user_buf, count); if (ret) - return ret; + return -EFAULT; /* remove end of line */ if (buf[count - 1] == '\n') -- cgit v1.1 From 2ed6d29c725c4aead510b5c23f563795b265acf5 Mon Sep 17 00:00:00 2001 From: Ohad Ben-Cohen Date: Sun, 30 Sep 2012 10:25:34 +0200 Subject: remoteproc: select VIRTIO to avoid build breakage drivers/built-in.o: In function `rproc_virtio_finalize_features': remoteproc_virtio.c:(.text+0x2f9a02): undefined reference to `vring_transport_features' drivers/built-in.o: In function `rproc_virtio_del_vqs': remoteproc_virtio.c:(.text+0x2f9a74): undefined reference to `vring_del_virtqueue' drivers/built-in.o: In function `rproc_virtio_find_vqs': remoteproc_virtio.c:(.text+0x2f9c44): undefined reference to `vring_new_virtqueue' drivers/built-in.o: In function `rproc_add_virtio_dev': (.text+0x2f9e2c): undefined reference to `register_virtio_device' drivers/built-in.o: In function `rproc_vq_interrupt': (.text+0x2f9db7): undefined reference to `vring_interrupt' drivers/built-in.o: In function `rproc_remove_virtio_dev': (.text+0x2f9e9f): undefined reference to `unregister_virtio_device' Cc: stable@vger.kernel.org Reported-by: Randy Dunlap Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index a1024d9..96ce101 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -6,6 +6,7 @@ config REMOTEPROC depends on EXPERIMENTAL depends on HAS_DMA select FW_CONFIG + select VIRTIO config OMAP_REMOTEPROC tristate "OMAP remoteproc support" -- cgit v1.1 From 7168d914a782086e217214c57ddfc7cc4b738c0c Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 25 Sep 2012 10:01:56 +0300 Subject: remoteproc: fix a potential NULL-dereference on cleanup We only need to allocate mapping if there is an IOMMU domain. Otherwise, when the mappings are released, the assumption that an IOMMU domain is there will crash and burn. CC: stable@vger.kernel.org Signed-off-by: Dan Carpenter [ohad: revise commit log] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index b6c6229..f163704 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -573,17 +573,10 @@ static int rproc_handle_carveout(struct rproc *rproc, dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n", rsc->da, rsc->pa, rsc->len, rsc->flags); - mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); - if (!mapping) { - dev_err(dev, "kzalloc mapping failed\n"); - return -ENOMEM; - } - carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); if (!carveout) { dev_err(dev, "kzalloc carveout failed\n"); - ret = -ENOMEM; - goto free_mapping; + return -ENOMEM; } va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); @@ -613,11 +606,18 @@ static int rproc_handle_carveout(struct rproc *rproc, * physical address in this case. */ if (rproc->domain) { + mapping = kzalloc(sizeof(*mapping), GFP_KERNEL); + if (!mapping) { + dev_err(dev, "kzalloc mapping failed\n"); + ret = -ENOMEM; + goto dma_free; + } + ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len, rsc->flags); if (ret) { dev_err(dev, "iommu_map failed: %d\n", ret); - goto dma_free; + goto free_mapping; } /* @@ -662,12 +662,12 @@ static int rproc_handle_carveout(struct rproc *rproc, return 0; +free_mapping: + kfree(mapping); dma_free: dma_free_coherent(dev->parent, rsc->len, va, dma); free_carv: kfree(carveout); -free_mapping: - kfree(mapping); return ret; } -- cgit v1.1 From d09f53a735bae43806a77754312a45d3f1198342 Mon Sep 17 00:00:00 2001 From: Emil Goode Date: Fri, 28 Sep 2012 17:35:06 +0200 Subject: remoteproc: Fix use of format specifyer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The dma_addr_t type can be either u32 or u64 depending on the configuration. We should use a format specifyer for the larger type and explicitly cast to it. Sparse warnings: drivers/remoteproc/remoteproc_core.c:234:2: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 6 has type ‘dma_addr_t’ [-Wformat] drivers/remoteproc/remoteproc_core.c:596:2: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 5 has type ‘dma_addr_t’ [-Wformat] drivers/remoteproc/remoteproc_core.c:634:3: warning: format ‘%x’ expects argument of type ‘unsigned int’, but argument 5 has type ‘dma_addr_t’ [-Wformat] Signed-off-by: Emil Goode [fix commit log typos] Signed-off-by: Ohad Ben-Cohen --- drivers/remoteproc/remoteproc_core.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/remoteproc') diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c index f163704..dd3bfaf 100644 --- a/drivers/remoteproc/remoteproc_core.c +++ b/drivers/remoteproc/remoteproc_core.c @@ -231,8 +231,8 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i) /* Store largest notifyid */ rproc->max_notifyid = max(rproc->max_notifyid, notifyid); - dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va, - dma, size, notifyid); + dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va, + (unsigned long long)dma, size, notifyid); rvring->va = va; rvring->dma = dma; @@ -586,7 +586,8 @@ static int rproc_handle_carveout(struct rproc *rproc, goto free_carv; } - dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len); + dev_dbg(dev, "carveout va %p, dma %llx, len 0x%x\n", va, + (unsigned long long)dma, rsc->len); /* * Ok, this is non-standard. @@ -631,7 +632,8 @@ static int rproc_handle_carveout(struct rproc *rproc, mapping->len = rsc->len; list_add_tail(&mapping->node, &rproc->mappings); - dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma); + dev_dbg(dev, "carveout mapped 0x%x to 0x%llx\n", + rsc->da, (unsigned long long)dma); } /* -- cgit v1.1