aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/remoteproc
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2012-10-04 12:11:57 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2012-10-04 12:11:57 -0400
commit4d6d367232813af09d9a1d90e3259e3ac42ee8a8 (patch)
treef8921106542eb5bac53f1ef5ac9fee46f1def42f /drivers/remoteproc
parentd66e6737d454553e1e62109d8298ede5351178a4 (diff)
parentd09f53a735bae43806a77754312a45d3f1198342 (diff)
Merge tag 'remoteproc-for-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc
Pull remoteproc update from Ohad Ben-Cohen: - Remoteproc Recovery - by Fernando Guzman Lugo When a remote processor crash is detected, this mechanism will remove all virtio children devices, wait until their drivers let go, hard reset the remote processor and reload the firmware (resulting in the relevant virtio children devices re-added). Essentially the entire software stack is reset, together with the relevant hardware, so users don't have to reset the entire phone. - STE Modem driver is added - by Sjur Brændeland - OMAP DSP boot address support is added - by Juan Gutierrez - A handful of fixes/cleanups - Sjur Brændeland, Dan Carpenter, Emil Goode * tag 'remoteproc-for-3.7' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc: remoteproc: Fix use of format specifyer remoteproc: fix a potential NULL-dereference on cleanup remoteproc: select VIRTIO to avoid build breakage remoteproc: return -EFAULT on copy_from_user failure remoteproc: snprintf() can return more than was printed remoteproc: Add STE modem driver remtoteproc: maintain max notifyid remoteproc: create a 'recovery' debugfs entry remoteproc: add actual recovery implementation remoteproc: add rproc_report_crash function to notify rproc crashes remoteproc: Add dependency to HAS_DMA remoteproc/omap: set bootaddr support
Diffstat (limited to 'drivers/remoteproc')
-rw-r--r--drivers/remoteproc/Kconfig14
-rw-r--r--drivers/remoteproc/Makefile1
-rw-r--r--drivers/remoteproc/omap_remoteproc.c3
-rw-r--r--drivers/remoteproc/remoteproc_core.c209
-rw-r--r--drivers/remoteproc/remoteproc_debugfs.c85
-rw-r--r--drivers/remoteproc/remoteproc_internal.h1
-rw-r--r--drivers/remoteproc/ste_modem_rproc.c322
7 files changed, 593 insertions, 42 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index f8d818abf98c..96ce101b9067 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -4,11 +4,14 @@ menu "Remoteproc drivers (EXPERIMENTAL)"
4config REMOTEPROC 4config REMOTEPROC
5 tristate 5 tristate
6 depends on EXPERIMENTAL 6 depends on EXPERIMENTAL
7 depends on HAS_DMA
7 select FW_CONFIG 8 select FW_CONFIG
9 select VIRTIO
8 10
9config OMAP_REMOTEPROC 11config OMAP_REMOTEPROC
10 tristate "OMAP remoteproc support" 12 tristate "OMAP remoteproc support"
11 depends on EXPERIMENTAL 13 depends on EXPERIMENTAL
14 depends on HAS_DMA
12 depends on ARCH_OMAP4 15 depends on ARCH_OMAP4
13 depends on OMAP_IOMMU 16 depends on OMAP_IOMMU
14 select REMOTEPROC 17 select REMOTEPROC
@@ -27,4 +30,15 @@ config OMAP_REMOTEPROC
27 It's safe to say n here if you're not interested in multimedia 30 It's safe to say n here if you're not interested in multimedia
28 offloading or just want a bare minimum kernel. 31 offloading or just want a bare minimum kernel.
29 32
33config STE_MODEM_RPROC
34 tristate "STE-Modem remoteproc support"
35 depends on EXPERIMENTAL
36 depends on HAS_DMA
37 select REMOTEPROC
38 default n
39 help
40 Say y or m here to support STE-Modem shared memory driver.
41 This can be either built-in or a loadable module.
42 If unsure say N.
43
30endmenu 44endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 934ce6e2c66b..391b65181c05 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -8,3 +8,4 @@ remoteproc-y += remoteproc_debugfs.o
8remoteproc-y += remoteproc_virtio.o 8remoteproc-y += remoteproc_virtio.o
9remoteproc-y += remoteproc_elf_loader.o 9remoteproc-y += remoteproc_elf_loader.o
10obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o 10obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o
11obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
index b54504ee61f1..32c289c2ba13 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)
116 struct omap_rproc_pdata *pdata = pdev->dev.platform_data; 116 struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
117 int ret; 117 int ret;
118 118
119 if (pdata->set_bootaddr)
120 pdata->set_bootaddr(rproc->bootaddr);
121
119 oproc->nb.notifier_call = omap_rproc_mbox_callback; 122 oproc->nb.notifier_call = omap_rproc_mbox_callback;
120 123
121 /* every omap rproc is assigned a mailbox instance for messaging */ 124 /* every omap rproc is assigned a mailbox instance for messaging */
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
index d5c2dbfc7443..dd3bfaf1ad40 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);
50/* Unique indices for remoteproc devices */ 50/* Unique indices for remoteproc devices */
51static DEFINE_IDA(rproc_dev_index); 51static DEFINE_IDA(rproc_dev_index);
52 52
53static const char * const rproc_crash_names[] = {
54 [RPROC_MMUFAULT] = "mmufault",
55};
56
57/* translate rproc_crash_type to string */
58static const char *rproc_crash_to_string(enum rproc_crash_type type)
59{
60 if (type < ARRAY_SIZE(rproc_crash_names))
61 return rproc_crash_names[type];
62 return "unkown";
63}
64
53/* 65/*
54 * This is the IOMMU fault handler we register with the IOMMU API 66 * This is the IOMMU fault handler we register with the IOMMU API
55 * (when relevant; not all remote processors access memory through 67 * (when relevant; not all remote processors access memory through
@@ -57,18 +69,19 @@ static DEFINE_IDA(rproc_dev_index);
57 * 69 *
58 * IOMMU core will invoke this handler whenever the remote processor 70 * IOMMU core will invoke this handler whenever the remote processor
59 * will try to access an unmapped device address. 71 * will try to access an unmapped device address.
60 *
61 * Currently this is mostly a stub, but it will be later used to trigger
62 * the recovery of the remote processor.
63 */ 72 */
64static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev, 73static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
65 unsigned long iova, int flags, void *token) 74 unsigned long iova, int flags, void *token)
66{ 75{
76 struct rproc *rproc = token;
77
67 dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags); 78 dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags);
68 79
80 rproc_report_crash(rproc, RPROC_MMUFAULT);
81
69 /* 82 /*
70 * Let the iommu core know we're not really handling this fault; 83 * Let the iommu core know we're not really handling this fault;
71 * we just plan to use this as a recovery trigger. 84 * we just used it as a recovery trigger.
72 */ 85 */
73 return -ENOSYS; 86 return -ENOSYS;
74} 87}
@@ -215,8 +228,11 @@ int rproc_alloc_vring(struct rproc_vdev *rvdev, int i)
215 return ret; 228 return ret;
216 } 229 }
217 230
218 dev_dbg(dev, "vring%d: va %p dma %x size %x idr %d\n", i, va, 231 /* Store largest notifyid */
219 dma, size, notifyid); 232 rproc->max_notifyid = max(rproc->max_notifyid, notifyid);
233
234 dev_dbg(dev, "vring%d: va %p dma %llx size %x idr %d\n", i, va,
235 (unsigned long long)dma, size, notifyid);
220 236
221 rvring->va = va; 237 rvring->va = va;
222 rvring->dma = dma; 238 rvring->dma = dma;
@@ -256,13 +272,25 @@ rproc_parse_vring(struct rproc_vdev *rvdev, struct fw_rsc_vdev *rsc, int i)
256 return 0; 272 return 0;
257} 273}
258 274
275static int rproc_max_notifyid(int id, void *p, void *data)
276{
277 int *maxid = data;
278 *maxid = max(*maxid, id);
279 return 0;
280}
281
259void rproc_free_vring(struct rproc_vring *rvring) 282void rproc_free_vring(struct rproc_vring *rvring)
260{ 283{
261 int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align)); 284 int size = PAGE_ALIGN(vring_size(rvring->len, rvring->align));
262 struct rproc *rproc = rvring->rvdev->rproc; 285 struct rproc *rproc = rvring->rvdev->rproc;
286 int maxid = 0;
263 287
264 dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma); 288 dma_free_coherent(rproc->dev.parent, size, rvring->va, rvring->dma);
265 idr_remove(&rproc->notifyids, rvring->notifyid); 289 idr_remove(&rproc->notifyids, rvring->notifyid);
290
291 /* Find the largest remaining notifyid */
292 idr_for_each(&rproc->notifyids, rproc_max_notifyid, &maxid);
293 rproc->max_notifyid = maxid;
266} 294}
267 295
268/** 296/**
@@ -545,17 +573,10 @@ static int rproc_handle_carveout(struct rproc *rproc,
545 dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n", 573 dev_dbg(dev, "carveout rsc: da %x, pa %x, len %x, flags %x\n",
546 rsc->da, rsc->pa, rsc->len, rsc->flags); 574 rsc->da, rsc->pa, rsc->len, rsc->flags);
547 575
548 mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
549 if (!mapping) {
550 dev_err(dev, "kzalloc mapping failed\n");
551 return -ENOMEM;
552 }
553
554 carveout = kzalloc(sizeof(*carveout), GFP_KERNEL); 576 carveout = kzalloc(sizeof(*carveout), GFP_KERNEL);
555 if (!carveout) { 577 if (!carveout) {
556 dev_err(dev, "kzalloc carveout failed\n"); 578 dev_err(dev, "kzalloc carveout failed\n");
557 ret = -ENOMEM; 579 return -ENOMEM;
558 goto free_mapping;
559 } 580 }
560 581
561 va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL); 582 va = dma_alloc_coherent(dev->parent, rsc->len, &dma, GFP_KERNEL);
@@ -565,7 +586,8 @@ static int rproc_handle_carveout(struct rproc *rproc,
565 goto free_carv; 586 goto free_carv;
566 } 587 }
567 588
568 dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len); 589 dev_dbg(dev, "carveout va %p, dma %llx, len 0x%x\n", va,
590 (unsigned long long)dma, rsc->len);
569 591
570 /* 592 /*
571 * Ok, this is non-standard. 593 * Ok, this is non-standard.
@@ -585,11 +607,18 @@ static int rproc_handle_carveout(struct rproc *rproc,
585 * physical address in this case. 607 * physical address in this case.
586 */ 608 */
587 if (rproc->domain) { 609 if (rproc->domain) {
610 mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
611 if (!mapping) {
612 dev_err(dev, "kzalloc mapping failed\n");
613 ret = -ENOMEM;
614 goto dma_free;
615 }
616
588 ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len, 617 ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len,
589 rsc->flags); 618 rsc->flags);
590 if (ret) { 619 if (ret) {
591 dev_err(dev, "iommu_map failed: %d\n", ret); 620 dev_err(dev, "iommu_map failed: %d\n", ret);
592 goto dma_free; 621 goto free_mapping;
593 } 622 }
594 623
595 /* 624 /*
@@ -603,7 +632,8 @@ static int rproc_handle_carveout(struct rproc *rproc,
603 mapping->len = rsc->len; 632 mapping->len = rsc->len;
604 list_add_tail(&mapping->node, &rproc->mappings); 633 list_add_tail(&mapping->node, &rproc->mappings);
605 634
606 dev_dbg(dev, "carveout mapped 0x%x to 0x%x\n", rsc->da, dma); 635 dev_dbg(dev, "carveout mapped 0x%x to 0x%llx\n",
636 rsc->da, (unsigned long long)dma);
607 } 637 }
608 638
609 /* 639 /*
@@ -634,12 +664,12 @@ static int rproc_handle_carveout(struct rproc *rproc,
634 664
635 return 0; 665 return 0;
636 666
667free_mapping:
668 kfree(mapping);
637dma_free: 669dma_free:
638 dma_free_coherent(dev->parent, rsc->len, va, dma); 670 dma_free_coherent(dev->parent, rsc->len, va, dma);
639free_carv: 671free_carv:
640 kfree(carveout); 672 kfree(carveout);
641free_mapping:
642 kfree(mapping);
643 return ret; 673 return ret;
644} 674}
645 675
@@ -871,6 +901,91 @@ out:
871 complete_all(&rproc->firmware_loading_complete); 901 complete_all(&rproc->firmware_loading_complete);
872} 902}
873 903
904static int rproc_add_virtio_devices(struct rproc *rproc)
905{
906 int ret;
907
908 /* rproc_del() calls must wait until async loader completes */
909 init_completion(&rproc->firmware_loading_complete);
910
911 /*
912 * We must retrieve early virtio configuration info from
913 * the firmware (e.g. whether to register a virtio device,
914 * what virtio features does it support, ...).
915 *
916 * We're initiating an asynchronous firmware loading, so we can
917 * be built-in kernel code, without hanging the boot process.
918 */
919 ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
920 rproc->firmware, &rproc->dev, GFP_KERNEL,
921 rproc, rproc_fw_config_virtio);
922 if (ret < 0) {
923 dev_err(&rproc->dev, "request_firmware_nowait err: %d\n", ret);
924 complete_all(&rproc->firmware_loading_complete);
925 }
926
927 return ret;
928}
929
930/**
931 * rproc_trigger_recovery() - recover a remoteproc
932 * @rproc: the remote processor
933 *
934 * The recovery is done by reseting all the virtio devices, that way all the
935 * rpmsg drivers will be reseted along with the remote processor making the
936 * remoteproc functional again.
937 *
938 * This function can sleep, so it cannot be called from atomic context.
939 */
940int rproc_trigger_recovery(struct rproc *rproc)
941{
942 struct rproc_vdev *rvdev, *rvtmp;
943
944 dev_err(&rproc->dev, "recovering %s\n", rproc->name);
945
946 init_completion(&rproc->crash_comp);
947
948 /* clean up remote vdev entries */
949 list_for_each_entry_safe(rvdev, rvtmp, &rproc->rvdevs, node)
950 rproc_remove_virtio_dev(rvdev);
951
952 /* wait until there is no more rproc users */
953 wait_for_completion(&rproc->crash_comp);
954
955 return rproc_add_virtio_devices(rproc);
956}
957
958/**
959 * rproc_crash_handler_work() - handle a crash
960 *
961 * This function needs to handle everything related to a crash, like cpu
962 * registers and stack dump, information to help to debug the fatal error, etc.
963 */
964static void rproc_crash_handler_work(struct work_struct *work)
965{
966 struct rproc *rproc = container_of(work, struct rproc, crash_handler);
967 struct device *dev = &rproc->dev;
968
969 dev_dbg(dev, "enter %s\n", __func__);
970
971 mutex_lock(&rproc->lock);
972
973 if (rproc->state == RPROC_CRASHED || rproc->state == RPROC_OFFLINE) {
974 /* handle only the first crash detected */
975 mutex_unlock(&rproc->lock);
976 return;
977 }
978
979 rproc->state = RPROC_CRASHED;
980 dev_err(dev, "handling crash #%u in %s\n", ++rproc->crash_cnt,
981 rproc->name);
982
983 mutex_unlock(&rproc->lock);
984
985 if (!rproc->recovery_disabled)
986 rproc_trigger_recovery(rproc);
987}
988
874/** 989/**
875 * rproc_boot() - boot a remote processor 990 * rproc_boot() - boot a remote processor
876 * @rproc: handle of a remote processor 991 * @rproc: handle of a remote processor
@@ -992,6 +1107,10 @@ void rproc_shutdown(struct rproc *rproc)
992 1107
993 rproc_disable_iommu(rproc); 1108 rproc_disable_iommu(rproc);
994 1109
1110 /* if in crash state, unlock crash handler */
1111 if (rproc->state == RPROC_CRASHED)
1112 complete_all(&rproc->crash_comp);
1113
995 rproc->state = RPROC_OFFLINE; 1114 rproc->state = RPROC_OFFLINE;
996 1115
997 dev_info(dev, "stopped remote processor %s\n", rproc->name); 1116 dev_info(dev, "stopped remote processor %s\n", rproc->name);
@@ -1026,7 +1145,7 @@ EXPORT_SYMBOL(rproc_shutdown);
1026int rproc_add(struct rproc *rproc) 1145int rproc_add(struct rproc *rproc)
1027{ 1146{
1028 struct device *dev = &rproc->dev; 1147 struct device *dev = &rproc->dev;
1029 int ret = 0; 1148 int ret;
1030 1149
1031 ret = device_add(dev); 1150 ret = device_add(dev);
1032 if (ret < 0) 1151 if (ret < 0)
@@ -1040,26 +1159,7 @@ int rproc_add(struct rproc *rproc)
1040 /* create debugfs entries */ 1159 /* create debugfs entries */
1041 rproc_create_debug_dir(rproc); 1160 rproc_create_debug_dir(rproc);
1042 1161
1043 /* rproc_del() calls must wait until async loader completes */ 1162 return rproc_add_virtio_devices(rproc);
1044 init_completion(&rproc->firmware_loading_complete);
1045
1046 /*
1047 * We must retrieve early virtio configuration info from
1048 * the firmware (e.g. whether to register a virtio device,
1049 * what virtio features does it support, ...).
1050 *
1051 * We're initiating an asynchronous firmware loading, so we can
1052 * be built-in kernel code, without hanging the boot process.
1053 */
1054 ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
1055 rproc->firmware, dev, GFP_KERNEL,
1056 rproc, rproc_fw_config_virtio);
1057 if (ret < 0) {
1058 dev_err(dev, "request_firmware_nowait failed: %d\n", ret);
1059 complete_all(&rproc->firmware_loading_complete);
1060 }
1061
1062 return ret;
1063} 1163}
1064EXPORT_SYMBOL(rproc_add); 1164EXPORT_SYMBOL(rproc_add);
1065 1165
@@ -1165,6 +1265,9 @@ struct rproc *rproc_alloc(struct device *dev, const char *name,
1165 INIT_LIST_HEAD(&rproc->traces); 1265 INIT_LIST_HEAD(&rproc->traces);
1166 INIT_LIST_HEAD(&rproc->rvdevs); 1266 INIT_LIST_HEAD(&rproc->rvdevs);
1167 1267
1268 INIT_WORK(&rproc->crash_handler, rproc_crash_handler_work);
1269 init_completion(&rproc->crash_comp);
1270
1168 rproc->state = RPROC_OFFLINE; 1271 rproc->state = RPROC_OFFLINE;
1169 1272
1170 return rproc; 1273 return rproc;
@@ -1221,6 +1324,32 @@ int rproc_del(struct rproc *rproc)
1221} 1324}
1222EXPORT_SYMBOL(rproc_del); 1325EXPORT_SYMBOL(rproc_del);
1223 1326
1327/**
1328 * rproc_report_crash() - rproc crash reporter function
1329 * @rproc: remote processor
1330 * @type: crash type
1331 *
1332 * This function must be called every time a crash is detected by the low-level
1333 * drivers implementing a specific remoteproc. This should not be called from a
1334 * non-remoteproc driver.
1335 *
1336 * This function can be called from atomic/interrupt context.
1337 */
1338void rproc_report_crash(struct rproc *rproc, enum rproc_crash_type type)
1339{
1340 if (!rproc) {
1341 pr_err("NULL rproc pointer\n");
1342 return;
1343 }
1344
1345 dev_err(&rproc->dev, "crash detected in %s: type %s\n",
1346 rproc->name, rproc_crash_to_string(type));
1347
1348 /* create a new task to handle the error */
1349 schedule_work(&rproc->crash_handler);
1350}
1351EXPORT_SYMBOL(rproc_report_crash);
1352
1224static int __init remoteproc_init(void) 1353static int __init remoteproc_init(void)
1225{ 1354{
1226 rproc_init_debugfs(); 1355 rproc_init_debugfs();
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
index 03833850f214..157a57309601 100644
--- a/drivers/remoteproc/remoteproc_debugfs.c
+++ b/drivers/remoteproc/remoteproc_debugfs.c
@@ -28,6 +28,9 @@
28#include <linux/debugfs.h> 28#include <linux/debugfs.h>
29#include <linux/remoteproc.h> 29#include <linux/remoteproc.h>
30#include <linux/device.h> 30#include <linux/device.h>
31#include <linux/uaccess.h>
32
33#include "remoteproc_internal.h"
31 34
32/* remoteproc debugfs parent dir */ 35/* remoteproc debugfs parent dir */
33static struct dentry *rproc_dbg; 36static struct dentry *rproc_dbg;
@@ -79,7 +82,7 @@ static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
79 82
80 state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state; 83 state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
81 84
82 i = snprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state], 85 i = scnprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state],
83 rproc->state); 86 rproc->state);
84 87
85 return simple_read_from_buffer(userbuf, count, ppos, buf, i); 88 return simple_read_from_buffer(userbuf, count, ppos, buf, i);
@@ -100,7 +103,7 @@ static ssize_t rproc_name_read(struct file *filp, char __user *userbuf,
100 char buf[100]; 103 char buf[100];
101 int i; 104 int i;
102 105
103 i = snprintf(buf, sizeof(buf), "%.98s\n", rproc->name); 106 i = scnprintf(buf, sizeof(buf), "%.98s\n", rproc->name);
104 107
105 return simple_read_from_buffer(userbuf, count, ppos, buf, i); 108 return simple_read_from_buffer(userbuf, count, ppos, buf, i);
106} 109}
@@ -111,6 +114,82 @@ static const struct file_operations rproc_name_ops = {
111 .llseek = generic_file_llseek, 114 .llseek = generic_file_llseek,
112}; 115};
113 116
117/* expose recovery flag via debugfs */
118static ssize_t rproc_recovery_read(struct file *filp, char __user *userbuf,
119 size_t count, loff_t *ppos)
120{
121 struct rproc *rproc = filp->private_data;
122 char *buf = rproc->recovery_disabled ? "disabled\n" : "enabled\n";
123
124 return simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
125}
126
127/*
128 * By writing to the 'recovery' debugfs entry, we control the behavior of the
129 * recovery mechanism dynamically. The default value of this entry is "enabled".
130 *
131 * The 'recovery' debugfs entry supports these commands:
132 *
133 * enabled: When enabled, the remote processor will be automatically
134 * recovered whenever it crashes. Moreover, if the remote
135 * processor crashes while recovery is disabled, it will
136 * be automatically recovered too as soon as recovery is enabled.
137 *
138 * disabled: When disabled, a remote processor will remain in a crashed
139 * state if it crashes. This is useful for debugging purposes;
140 * without it, debugging a crash is substantially harder.
141 *
142 * recover: This function will trigger an immediate recovery if the
143 * remote processor is in a crashed state, without changing
144 * or checking the recovery state (enabled/disabled).
145 * This is useful during debugging sessions, when one expects
146 * additional crashes to happen after enabling recovery. In this
147 * case, enabling recovery will make it hard to debug subsequent
148 * crashes, so it's recommended to keep recovery disabled, and
149 * instead use the "recover" command as needed.
150 */
151static ssize_t
152rproc_recovery_write(struct file *filp, const char __user *user_buf,
153 size_t count, loff_t *ppos)
154{
155 struct rproc *rproc = filp->private_data;
156 char buf[10];
157 int ret;
158
159 if (count > sizeof(buf))
160 return count;
161
162 ret = copy_from_user(buf, user_buf, count);
163 if (ret)
164 return -EFAULT;
165
166 /* remove end of line */
167 if (buf[count - 1] == '\n')
168 buf[count - 1] = '\0';
169
170 if (!strncmp(buf, "enabled", count)) {
171 rproc->recovery_disabled = false;
172 /* if rproc has crashed, trigger recovery */
173 if (rproc->state == RPROC_CRASHED)
174 rproc_trigger_recovery(rproc);
175 } else if (!strncmp(buf, "disabled", count)) {
176 rproc->recovery_disabled = true;
177 } else if (!strncmp(buf, "recover", count)) {
178 /* if rproc has crashed, trigger recovery */
179 if (rproc->state == RPROC_CRASHED)
180 rproc_trigger_recovery(rproc);
181 }
182
183 return count;
184}
185
186static const struct file_operations rproc_recovery_ops = {
187 .read = rproc_recovery_read,
188 .write = rproc_recovery_write,
189 .open = simple_open,
190 .llseek = generic_file_llseek,
191};
192
114void rproc_remove_trace_file(struct dentry *tfile) 193void rproc_remove_trace_file(struct dentry *tfile)
115{ 194{
116 debugfs_remove(tfile); 195 debugfs_remove(tfile);
@@ -154,6 +233,8 @@ void rproc_create_debug_dir(struct rproc *rproc)
154 rproc, &rproc_name_ops); 233 rproc, &rproc_name_ops);
155 debugfs_create_file("state", 0400, rproc->dbg_dir, 234 debugfs_create_file("state", 0400, rproc->dbg_dir,
156 rproc, &rproc_state_ops); 235 rproc, &rproc_state_ops);
236 debugfs_create_file("recovery", 0400, rproc->dbg_dir,
237 rproc, &rproc_recovery_ops);
157} 238}
158 239
159void __init rproc_init_debugfs(void) 240void __init rproc_init_debugfs(void)
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
index a690ebe7aa51..7bb66482d061 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);
63int rproc_alloc_vring(struct rproc_vdev *rvdev, int i); 63int rproc_alloc_vring(struct rproc_vdev *rvdev, int i);
64 64
65void *rproc_da_to_va(struct rproc *rproc, u64 da, int len); 65void *rproc_da_to_va(struct rproc *rproc, u64 da, int len);
66int rproc_trigger_recovery(struct rproc *rproc);
66 67
67static inline 68static inline
68int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw) 69int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
diff --git a/drivers/remoteproc/ste_modem_rproc.c b/drivers/remoteproc/ste_modem_rproc.c
new file mode 100644
index 000000000000..a7743c069339
--- /dev/null
+++ b/drivers/remoteproc/ste_modem_rproc.c
@@ -0,0 +1,322 @@
1/*
2 * Copyright (C) ST-Ericsson AB 2012
3 * Author: Sjur Brændeland <sjur.brandeland@stericsson.com>
4 * License terms: GNU General Public License (GPL), version 2
5 */
6
7#include <linux/module.h>
8#include <linux/dma-mapping.h>
9#include <linux/remoteproc.h>
10#include <linux/ste_modem_shm.h>
11#include "remoteproc_internal.h"
12
13#define SPROC_FW_SIZE (50 * 4096)
14#define SPROC_MAX_TOC_ENTRIES 32
15#define SPROC_MAX_NOTIFY_ID 14
16#define SPROC_RESOURCE_NAME "rsc-table"
17#define SPROC_MODEM_NAME "ste-modem"
18#define SPROC_MODEM_FIRMWARE SPROC_MODEM_NAME "-fw.bin"
19
20#define sproc_dbg(sproc, fmt, ...) \
21 dev_dbg(&sproc->mdev->pdev.dev, fmt, ##__VA_ARGS__)
22#define sproc_err(sproc, fmt, ...) \
23 dev_err(&sproc->mdev->pdev.dev, fmt, ##__VA_ARGS__)
24
25/* STE-modem control structure */
26struct sproc {
27 struct rproc *rproc;
28 struct ste_modem_device *mdev;
29 int error;
30 void *fw_addr;
31 size_t fw_size;
32 dma_addr_t fw_dma_addr;
33};
34
35/* STE-Modem firmware entry */
36struct ste_toc_entry {
37 __le32 start;
38 __le32 size;
39 __le32 flags;
40 __le32 entry_point;
41 __le32 load_addr;
42 char name[12];
43};
44
45/*
46 * The Table Of Content is located at the start of the firmware image and
47 * at offset zero in the shared memory region. The resource table typically
48 * contains the initial boot image (boot strap) and other information elements
49 * such as remoteproc resource table. Each entry is identified by a unique
50 * name.
51 */
52struct ste_toc {
53 struct ste_toc_entry table[SPROC_MAX_TOC_ENTRIES];
54};
55
56/* Loads the firmware to shared memory. */
57static int sproc_load_segments(struct rproc *rproc, const struct firmware *fw)
58{
59 struct sproc *sproc = rproc->priv;
60
61 memcpy(sproc->fw_addr, fw->data, fw->size);
62
63 return 0;
64}
65
66/* Find the entry for resource table in the Table of Content */
67static struct ste_toc_entry *sproc_find_rsc_entry(const struct firmware *fw)
68{
69 int i;
70 struct ste_toc *toc;
71
72 if (!fw)
73 return NULL;
74
75 toc = (void *)fw->data;
76
77 /* Search the table for the resource table */
78 for (i = 0; i < SPROC_MAX_TOC_ENTRIES &&
79 toc->table[i].start != 0xffffffff; i++) {
80
81 if (!strncmp(toc->table[i].name, SPROC_RESOURCE_NAME,
82 sizeof(toc->table[i].name))) {
83 if (toc->table[i].start > fw->size)
84 return NULL;
85 return &toc->table[i];
86 }
87 }
88
89 return NULL;
90}
91
92/* Find the resource table inside the remote processor's firmware. */
93static struct resource_table *
94sproc_find_rsc_table(struct rproc *rproc, const struct firmware *fw,
95 int *tablesz)
96{
97 struct sproc *sproc = rproc->priv;
98 struct resource_table *table;
99 struct ste_toc_entry *entry;
100
101 entry = sproc_find_rsc_entry(fw);
102 if (!entry) {
103 sproc_err(sproc, "resource table not found in fw\n");
104 return NULL;
105 }
106
107 table = (void *)(fw->data + entry->start);
108
109 /* sanity check size and offset of resource table */
110 if (entry->start > SPROC_FW_SIZE ||
111 entry->size > SPROC_FW_SIZE ||
112 fw->size > SPROC_FW_SIZE ||
113 entry->start + entry->size > fw->size ||
114 sizeof(struct resource_table) > entry->size) {
115 sproc_err(sproc, "bad size of fw or resource table\n");
116 return NULL;
117 }
118
119 /* we don't support any version beyond the first */
120 if (table->ver != 1) {
121 sproc_err(sproc, "unsupported fw ver: %d\n", table->ver);
122 return NULL;
123 }
124
125 /* make sure reserved bytes are zeroes */
126 if (table->reserved[0] || table->reserved[1]) {
127 sproc_err(sproc, "non zero reserved bytes\n");
128 return NULL;
129 }
130
131 /* make sure the offsets array isn't truncated */
132 if (table->num > SPROC_MAX_TOC_ENTRIES ||
133 table->num * sizeof(table->offset[0]) +
134 sizeof(struct resource_table) > entry->size) {
135 sproc_err(sproc, "resource table incomplete\n");
136 return NULL;
137 }
138
139 /* If the fw size has grown, release the previous fw allocation */
140 if (SPROC_FW_SIZE < fw->size) {
141 sproc_err(sproc, "Insufficient space for fw (%d < %zd)\n",
142 SPROC_FW_SIZE, fw->size);
143 return NULL;
144 }
145
146 sproc->fw_size = fw->size;
147 *tablesz = entry->size;
148
149 return table;
150}
151
152/* STE modem firmware handler operations */
153const struct rproc_fw_ops sproc_fw_ops = {
154 .load = sproc_load_segments,
155 .find_rsc_table = sproc_find_rsc_table,
156};
157
158/* Kick the modem with specified notification id */
159static void sproc_kick(struct rproc *rproc, int vqid)
160{
161 struct sproc *sproc = rproc->priv;
162
163 sproc_dbg(sproc, "kick vqid:%d\n", vqid);
164
165 /*
166 * We need different notification IDs for RX and TX so add
167 * an offset on TX notification IDs.
168 */
169 sproc->mdev->ops.kick(sproc->mdev, vqid + SPROC_MAX_NOTIFY_ID);
170}
171
172/* Received a kick from a modem, kick the virtqueue */
173static void sproc_kick_callback(struct ste_modem_device *mdev, int vqid)
174{
175 struct sproc *sproc = mdev->drv_data;
176
177 if (rproc_vq_interrupt(sproc->rproc, vqid) == IRQ_NONE)
178 sproc_dbg(sproc, "no message was found in vqid %d\n", vqid);
179}
180
181struct ste_modem_dev_cb sproc_dev_cb = {
182 .kick = sproc_kick_callback,
183};
184
185/* Start the STE modem */
186static int sproc_start(struct rproc *rproc)
187{
188 struct sproc *sproc = rproc->priv;
189 int i, err;
190
191 sproc_dbg(sproc, "start ste-modem\n");
192
193 /* Sanity test the max_notifyid */
194 if (rproc->max_notifyid > SPROC_MAX_NOTIFY_ID) {
195 sproc_err(sproc, "Notification IDs too high:%d\n",
196 rproc->max_notifyid);
197 return -EINVAL;
198 }
199
200 /* Subscribe to notifications */
201 for (i = 0; i < rproc->max_notifyid; i++) {
202 err = sproc->mdev->ops.kick_subscribe(sproc->mdev, i);
203 if (err) {
204 sproc_err(sproc,
205 "subscription of kicks failed:%d\n", err);
206 return err;
207 }
208 }
209
210 /* Request modem start-up*/
211 return sproc->mdev->ops.power(sproc->mdev, true);
212}
213
214/* Stop the STE modem */
215static int sproc_stop(struct rproc *rproc)
216{
217 struct sproc *sproc = rproc->priv;
218 sproc_dbg(sproc, "stop ste-modem\n");
219
220 return sproc->mdev->ops.power(sproc->mdev, false);
221}
222
223static struct rproc_ops sproc_ops = {
224 .start = sproc_start,
225 .stop = sproc_stop,
226 .kick = sproc_kick,
227};
228
229/* STE modem device is unregistered */
230static int sproc_drv_remove(struct platform_device *pdev)
231{
232 struct ste_modem_device *mdev =
233 container_of(pdev, struct ste_modem_device, pdev);
234 struct sproc *sproc = mdev->drv_data;
235
236 sproc_dbg(sproc, "remove ste-modem\n");
237
238 /* Reset device callback functions */
239 sproc->mdev->ops.setup(sproc->mdev, NULL);
240
241 /* Unregister as remoteproc device */
242 rproc_del(sproc->rproc);
243 rproc_put(sproc->rproc);
244
245 mdev->drv_data = NULL;
246
247 return 0;
248}
249
250/* Handle probe of a modem device */
251static int sproc_probe(struct platform_device *pdev)
252{
253 struct ste_modem_device *mdev =
254 container_of(pdev, struct ste_modem_device, pdev);
255 struct sproc *sproc;
256 struct rproc *rproc;
257 int err;
258
259 dev_dbg(&mdev->pdev.dev, "probe ste-modem\n");
260
261 if (!mdev->ops.setup || !mdev->ops.kick || !mdev->ops.kick_subscribe ||
262 !mdev->ops.power) {
263 dev_err(&mdev->pdev.dev, "invalid mdev ops\n");
264 return -EINVAL;
265 }
266
267 rproc = rproc_alloc(&mdev->pdev.dev, mdev->pdev.name, &sproc_ops,
268 SPROC_MODEM_FIRMWARE, sizeof(*sproc));
269 if (!rproc)
270 return -ENOMEM;
271
272 sproc = rproc->priv;
273 sproc->mdev = mdev;
274 sproc->rproc = rproc;
275 mdev->drv_data = sproc;
276
277 /* Provide callback functions to modem device */
278 sproc->mdev->ops.setup(sproc->mdev, &sproc_dev_cb);
279
280 /* Set the STE-modem specific firmware handler */
281 rproc->fw_ops = &sproc_fw_ops;
282
283 /*
284 * STE-modem requires the firmware to be located
285 * at the start of the shared memory region. So we need to
286 * reserve space for firmware at the start.
287 */
288 sproc->fw_addr = dma_alloc_coherent(rproc->dev.parent, SPROC_FW_SIZE,
289 &sproc->fw_dma_addr,
290 GFP_KERNEL);
291 if (!sproc->fw_addr) {
292 sproc_err(sproc, "Cannot allocate memory for fw\n");
293 err = -ENOMEM;
294 goto free_rproc;
295 }
296
297 /* Register as a remoteproc device */
298 err = rproc_add(rproc);
299 if (err)
300 goto free_rproc;
301
302 return 0;
303
304free_rproc:
305 /* Reset device data upon error */
306 mdev->drv_data = NULL;
307 rproc_put(rproc);
308 return err;
309}
310
311static struct platform_driver sproc_driver = {
312 .driver = {
313 .name = SPROC_MODEM_NAME,
314 .owner = THIS_MODULE,
315 },
316 .probe = sproc_probe,
317 .remove = sproc_drv_remove,
318};
319
320module_platform_driver(sproc_driver);
321MODULE_LICENSE("GPL v2");
322MODULE_DESCRIPTION("STE Modem driver using the Remote Processor Framework");