diff options
Diffstat (limited to 'drivers/remoteproc/omap_remoteproc.c')
-rw-r--r-- | drivers/remoteproc/omap_remoteproc.c | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c new file mode 100644 index 00000000000..b49ecbb91ef --- /dev/null +++ b/drivers/remoteproc/omap_remoteproc.c | |||
@@ -0,0 +1,249 @@ | |||
1 | /* | ||
2 | * OMAP Remote Processor driver | ||
3 | * | ||
4 | * Copyright (C) 2011 Texas Instruments, Inc. | ||
5 | * Copyright (C) 2011 Google, Inc. | ||
6 | * | ||
7 | * Ohad Ben-Cohen <ohad@wizery.com> | ||
8 | * Brian Swetland <swetland@google.com> | ||
9 | * Fernando Guzman Lugo <fernando.lugo@ti.com> | ||
10 | * Mark Grosen <mgrosen@ti.com> | ||
11 | * Suman Anna <s-anna@ti.com> | ||
12 | * Hari Kanigeri <h-kanigeri2@ti.com> | ||
13 | * | ||
14 | * This program is free software; you can redistribute it and/or | ||
15 | * modify it under the terms of the GNU General Public License | ||
16 | * version 2 as published by the Free Software Foundation. | ||
17 | * | ||
18 | * This program is distributed in the hope that it will be useful, | ||
19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
21 | * GNU General Public License for more details. | ||
22 | */ | ||
23 | |||
24 | #include <linux/kernel.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/err.h> | ||
27 | #include <linux/platform_device.h> | ||
28 | #include <linux/dma-mapping.h> | ||
29 | #include <linux/remoteproc.h> | ||
30 | |||
31 | #include <plat/mailbox.h> | ||
32 | #include <plat/remoteproc.h> | ||
33 | |||
34 | #include "omap_remoteproc.h" | ||
35 | #include "remoteproc_internal.h" | ||
36 | |||
37 | /** | ||
38 | * struct omap_rproc - omap remote processor state | ||
39 | * @mbox: omap mailbox handle | ||
40 | * @nb: notifier block that will be invoked on inbound mailbox messages | ||
41 | * @rproc: rproc handle | ||
42 | */ | ||
43 | struct omap_rproc { | ||
44 | struct omap_mbox *mbox; | ||
45 | struct notifier_block nb; | ||
46 | struct rproc *rproc; | ||
47 | }; | ||
48 | |||
49 | /** | ||
50 | * omap_rproc_mbox_callback() - inbound mailbox message handler | ||
51 | * @this: notifier block | ||
52 | * @index: unused | ||
53 | * @data: mailbox payload | ||
54 | * | ||
55 | * This handler is invoked by omap's mailbox driver whenever a mailbox | ||
56 | * message is received. Usually, the mailbox payload simply contains | ||
57 | * the index of the virtqueue that is kicked by the remote processor, | ||
58 | * and we let remoteproc core handle it. | ||
59 | * | ||
60 | * In addition to virtqueue indices, we also have some out-of-band values | ||
61 | * that indicates different events. Those values are deliberately very | ||
62 | * big so they don't coincide with virtqueue indices. | ||
63 | */ | ||
64 | static int omap_rproc_mbox_callback(struct notifier_block *this, | ||
65 | unsigned long index, void *data) | ||
66 | { | ||
67 | mbox_msg_t msg = (mbox_msg_t) data; | ||
68 | struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb); | ||
69 | struct device *dev = oproc->rproc->dev; | ||
70 | const char *name = oproc->rproc->name; | ||
71 | |||
72 | dev_dbg(dev, "mbox msg: 0x%x\n", msg); | ||
73 | |||
74 | switch (msg) { | ||
75 | case RP_MBOX_CRASH: | ||
76 | /* just log this for now. later, we'll also do recovery */ | ||
77 | dev_err(dev, "omap rproc %s crashed\n", name); | ||
78 | break; | ||
79 | case RP_MBOX_ECHO_REPLY: | ||
80 | dev_info(dev, "received echo reply from %s\n", name); | ||
81 | break; | ||
82 | default: | ||
83 | /* ignore vq indices which are too large to be valid */ | ||
84 | if (msg >= 2) { | ||
85 | dev_warn(dev, "invalid mbox msg: 0x%x\n", msg); | ||
86 | break; | ||
87 | } | ||
88 | |||
89 | /* | ||
90 | * At this point, 'msg' contains the index of the vring | ||
91 | * which was just triggered. | ||
92 | */ | ||
93 | if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE) | ||
94 | dev_dbg(dev, "no message was found in vqid %d\n", msg); | ||
95 | } | ||
96 | |||
97 | return NOTIFY_DONE; | ||
98 | } | ||
99 | |||
100 | /* kick a virtqueue */ | ||
101 | static void omap_rproc_kick(struct rproc *rproc, int vqid) | ||
102 | { | ||
103 | struct omap_rproc *oproc = rproc->priv; | ||
104 | int ret; | ||
105 | |||
106 | /* send the index of the triggered virtqueue in the mailbox payload */ | ||
107 | ret = omap_mbox_msg_send(oproc->mbox, vqid); | ||
108 | if (ret) | ||
109 | dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret); | ||
110 | } | ||
111 | |||
112 | /* | ||
113 | * Power up the remote processor. | ||
114 | * | ||
115 | * This function will be invoked only after the firmware for this rproc | ||
116 | * was loaded, parsed successfully, and all of its resource requirements | ||
117 | * were met. | ||
118 | */ | ||
119 | static int omap_rproc_start(struct rproc *rproc) | ||
120 | { | ||
121 | struct omap_rproc *oproc = rproc->priv; | ||
122 | struct platform_device *pdev = to_platform_device(rproc->dev); | ||
123 | struct omap_rproc_pdata *pdata = pdev->dev.platform_data; | ||
124 | int ret; | ||
125 | |||
126 | oproc->nb.notifier_call = omap_rproc_mbox_callback; | ||
127 | |||
128 | /* every omap rproc is assigned a mailbox instance for messaging */ | ||
129 | oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb); | ||
130 | if (IS_ERR(oproc->mbox)) { | ||
131 | ret = PTR_ERR(oproc->mbox); | ||
132 | dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); | ||
133 | return ret; | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * Ping the remote processor. this is only for sanity-sake; | ||
138 | * there is no functional effect whatsoever. | ||
139 | * | ||
140 | * Note that the reply will _not_ arrive immediately: this message | ||
141 | * will wait in the mailbox fifo until the remote processor is booted. | ||
142 | */ | ||
143 | ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST); | ||
144 | if (ret) { | ||
145 | dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret); | ||
146 | goto put_mbox; | ||
147 | } | ||
148 | |||
149 | ret = pdata->device_enable(pdev); | ||
150 | if (ret) { | ||
151 | dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret); | ||
152 | goto put_mbox; | ||
153 | } | ||
154 | |||
155 | return 0; | ||
156 | |||
157 | put_mbox: | ||
158 | omap_mbox_put(oproc->mbox, &oproc->nb); | ||
159 | return ret; | ||
160 | } | ||
161 | |||
162 | /* power off the remote processor */ | ||
163 | static int omap_rproc_stop(struct rproc *rproc) | ||
164 | { | ||
165 | struct platform_device *pdev = to_platform_device(rproc->dev); | ||
166 | struct omap_rproc_pdata *pdata = pdev->dev.platform_data; | ||
167 | struct omap_rproc *oproc = rproc->priv; | ||
168 | int ret; | ||
169 | |||
170 | ret = pdata->device_shutdown(pdev); | ||
171 | if (ret) | ||
172 | return ret; | ||
173 | |||
174 | omap_mbox_put(oproc->mbox, &oproc->nb); | ||
175 | |||
176 | return 0; | ||
177 | } | ||
178 | |||
179 | static struct rproc_ops omap_rproc_ops = { | ||
180 | .start = omap_rproc_start, | ||
181 | .stop = omap_rproc_stop, | ||
182 | .kick = omap_rproc_kick, | ||
183 | }; | ||
184 | |||
185 | static int __devinit omap_rproc_probe(struct platform_device *pdev) | ||
186 | { | ||
187 | struct omap_rproc_pdata *pdata = pdev->dev.platform_data; | ||
188 | struct omap_rproc *oproc; | ||
189 | struct rproc *rproc; | ||
190 | int ret; | ||
191 | |||
192 | ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); | ||
193 | if (ret) { | ||
194 | dev_err(pdev->dev.parent, "dma_set_coherent_mask: %d\n", ret); | ||
195 | return ret; | ||
196 | } | ||
197 | |||
198 | rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops, | ||
199 | pdata->firmware, sizeof(*oproc)); | ||
200 | if (!rproc) | ||
201 | return -ENOMEM; | ||
202 | |||
203 | oproc = rproc->priv; | ||
204 | oproc->rproc = rproc; | ||
205 | |||
206 | platform_set_drvdata(pdev, rproc); | ||
207 | |||
208 | ret = rproc_register(rproc); | ||
209 | if (ret) | ||
210 | goto free_rproc; | ||
211 | |||
212 | return 0; | ||
213 | |||
214 | free_rproc: | ||
215 | rproc_free(rproc); | ||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | static int __devexit omap_rproc_remove(struct platform_device *pdev) | ||
220 | { | ||
221 | struct rproc *rproc = platform_get_drvdata(pdev); | ||
222 | |||
223 | return rproc_unregister(rproc); | ||
224 | } | ||
225 | |||
226 | static struct platform_driver omap_rproc_driver = { | ||
227 | .probe = omap_rproc_probe, | ||
228 | .remove = __devexit_p(omap_rproc_remove), | ||
229 | .driver = { | ||
230 | .name = "omap-rproc", | ||
231 | .owner = THIS_MODULE, | ||
232 | }, | ||
233 | }; | ||
234 | |||
235 | /* most of the below will go when module_platform_driver is merged */ | ||
236 | static int __init omap_rproc_init(void) | ||
237 | { | ||
238 | return platform_driver_register(&omap_rproc_driver); | ||
239 | } | ||
240 | module_init(omap_rproc_init); | ||
241 | |||
242 | static void __exit omap_rproc_exit(void) | ||
243 | { | ||
244 | platform_driver_unregister(&omap_rproc_driver); | ||
245 | } | ||
246 | module_exit(omap_rproc_exit); | ||
247 | |||
248 | MODULE_LICENSE("GPL v2"); | ||
249 | MODULE_DESCRIPTION("OMAP Remote Processor control driver"); | ||