aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRobert Tivy <rtivy@ti.com>2013-04-09 17:20:21 -0400
committerOhad Ben-Cohen <ohad@wizery.com>2013-04-15 02:23:40 -0400
commit13be5432d8721d89cadae105663761f45f427842 (patch)
treeee9468b54d7515f8db561079f2234e81e701a5fb
parent8b4aec9ac7b59754df9c594569af9ae8f456ee07 (diff)
remoteproc/davinci: add a remoteproc driver for OMAP-L13x DSP
Adding a new remoteproc driver for OMAP-L13x DSP Signed-off-by: Robert Tivy <rtivy@ti.com> [removed 'EXPERIMENTAL' and fixed some indentation issues] Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>
-rw-r--r--drivers/remoteproc/Kconfig23
-rw-r--r--drivers/remoteproc/Makefile1
-rw-r--r--drivers/remoteproc/da8xx_remoteproc.c324
3 files changed, 348 insertions, 0 deletions
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 5ad65710c4ff..c3675ae3cc9e 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -39,4 +39,27 @@ config STE_MODEM_RPROC
39 This can be either built-in or a loadable module. 39 This can be either built-in or a loadable module.
40 If unsure say N. 40 If unsure say N.
41 41
42config DA8XX_REMOTEPROC
43 tristate "DA8xx/OMAP-L13x remoteproc support"
44 depends on ARCH_DAVINCI_DA8XX
45 select CMA
46 select REMOTEPROC
47 select RPMSG
48 help
49 Say y here to support DA8xx/OMAP-L13x remote processors via the
50 remote processor framework.
51
52 You want to say y here in order to enable AMP
53 use-cases to run on your platform (multimedia codecs are
54 offloaded to remote DSP processors using this framework).
55
56 This module controls the name of the firmware file that gets
57 loaded on the DSP. This file must reside in the /lib/firmware
58 directory. It can be specified via the module parameter
59 da8xx_fw_name=<filename>, and if not specified will default to
60 "rproc-dsp-fw".
61
62 It's safe to say n here if you're not interested in multimedia
63 offloading.
64
42endmenu 65endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 391b65181c05..ac2ff75686d2 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -9,3 +9,4 @@ remoteproc-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 11obj-$(CONFIG_STE_MODEM_RPROC) += ste_modem_rproc.o
12obj-$(CONFIG_DA8XX_REMOTEPROC) += da8xx_remoteproc.o
diff --git a/drivers/remoteproc/da8xx_remoteproc.c b/drivers/remoteproc/da8xx_remoteproc.c
new file mode 100644
index 000000000000..9b2e60afa1a6
--- /dev/null
+++ b/drivers/remoteproc/da8xx_remoteproc.c
@@ -0,0 +1,324 @@
1/*
2 * Remote processor machine-specific module for DA8XX
3 *
4 * Copyright (C) 2013 Texas Instruments, Inc.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * version 2 as published by the Free Software Foundation.
9 */
10
11#include <linux/bitops.h>
12#include <linux/clk.h>
13#include <linux/err.h>
14#include <linux/interrupt.h>
15#include <linux/io.h>
16#include <linux/irq.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/platform_device.h>
20#include <linux/remoteproc.h>
21
22#include <mach/clock.h> /* for davinci_clk_reset_assert/deassert() */
23
24#include "remoteproc_internal.h"
25
26static char *da8xx_fw_name;
27module_param(da8xx_fw_name, charp, S_IRUGO);
28MODULE_PARM_DESC(da8xx_fw_name,
29 "\n\t\tName of DSP firmware file in /lib/firmware"
30 " (if not specified defaults to 'rproc-dsp-fw')");
31
32/*
33 * OMAP-L138 Technical References:
34 * http://www.ti.com/product/omap-l138
35 */
36#define SYSCFG_CHIPSIG0 BIT(0)
37#define SYSCFG_CHIPSIG1 BIT(1)
38#define SYSCFG_CHIPSIG2 BIT(2)
39#define SYSCFG_CHIPSIG3 BIT(3)
40#define SYSCFG_CHIPSIG4 BIT(4)
41
42/**
43 * struct da8xx_rproc - da8xx remote processor instance state
44 * @rproc: rproc handle
45 * @dsp_clk: placeholder for platform's DSP clk
46 * @ack_fxn: chip-specific ack function for ack'ing irq
47 * @irq_data: ack_fxn function parameter
48 * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR)
49 * @bootreg: virt ptr to DSP boot address register (HOST1CFG)
50 * @irq: irq # used by this instance
51 */
52struct da8xx_rproc {
53 struct rproc *rproc;
54 struct clk *dsp_clk;
55 void (*ack_fxn)(struct irq_data *data);
56 struct irq_data *irq_data;
57 void __iomem *chipsig;
58 void __iomem *bootreg;
59 int irq;
60};
61
62/**
63 * handle_event() - inbound virtqueue message workqueue function
64 *
65 * This function is registered as a kernel thread and is scheduled by the
66 * kernel handler.
67 */
68static irqreturn_t handle_event(int irq, void *p)
69{
70 struct rproc *rproc = (struct rproc *)p;
71
72 /* Process incoming buffers on all our vrings */
73 rproc_vq_interrupt(rproc, 0);
74 rproc_vq_interrupt(rproc, 1);
75
76 return IRQ_HANDLED;
77}
78
79/**
80 * da8xx_rproc_callback() - inbound virtqueue message handler
81 *
82 * This handler is invoked directly by the kernel whenever the remote
83 * core (DSP) has modified the state of a virtqueue. There is no
84 * "payload" message indicating the virtqueue index as is the case with
85 * mailbox-based implementations on OMAP4. As such, this handler "polls"
86 * each known virtqueue index for every invocation.
87 */
88static irqreturn_t da8xx_rproc_callback(int irq, void *p)
89{
90 struct rproc *rproc = (struct rproc *)p;
91 struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
92 u32 chipsig;
93
94 chipsig = readl(drproc->chipsig);
95 if (chipsig & SYSCFG_CHIPSIG0) {
96 /* Clear interrupt level source */
97 writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4);
98
99 /*
100 * ACK intr to AINTC.
101 *
102 * It has already been ack'ed by the kernel before calling
103 * this function, but since the ARM<->DSP interrupts in the
104 * CHIPSIG register are "level" instead of "pulse" variety,
105 * we need to ack it after taking down the level else we'll
106 * be called again immediately after returning.
107 */
108 drproc->ack_fxn(drproc->irq_data);
109
110 return IRQ_WAKE_THREAD;
111 }
112
113 return IRQ_HANDLED;
114}
115
116static int da8xx_rproc_start(struct rproc *rproc)
117{
118 struct device *dev = rproc->dev.parent;
119 struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
120 struct clk *dsp_clk = drproc->dsp_clk;
121
122 /* hw requires the start (boot) address be on 1KB boundary */
123 if (rproc->bootaddr & 0x3ff) {
124 dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
125
126 return -EINVAL;
127 }
128
129 writel(rproc->bootaddr, drproc->bootreg);
130
131 clk_enable(dsp_clk);
132 davinci_clk_reset_deassert(dsp_clk);
133
134 return 0;
135}
136
137static int da8xx_rproc_stop(struct rproc *rproc)
138{
139 struct da8xx_rproc *drproc = rproc->priv;
140
141 clk_disable(drproc->dsp_clk);
142
143 return 0;
144}
145
146/* kick a virtqueue */
147static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
148{
149 struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
150
151 /* Interupt remote proc */
152 writel(SYSCFG_CHIPSIG2, drproc->chipsig);
153}
154
155static struct rproc_ops da8xx_rproc_ops = {
156 .start = da8xx_rproc_start,
157 .stop = da8xx_rproc_stop,
158 .kick = da8xx_rproc_kick,
159};
160
161static int reset_assert(struct device *dev)
162{
163 struct clk *dsp_clk;
164
165 dsp_clk = clk_get(dev, NULL);
166 if (IS_ERR(dsp_clk)) {
167 dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
168 return PTR_RET(dsp_clk);
169 }
170
171 davinci_clk_reset_assert(dsp_clk);
172 clk_put(dsp_clk);
173
174 return 0;
175}
176
177static int da8xx_rproc_probe(struct platform_device *pdev)
178{
179 struct device *dev = &pdev->dev;
180 struct da8xx_rproc *drproc;
181 struct rproc *rproc;
182 struct irq_data *irq_data;
183 struct resource *bootreg_res;
184 struct resource *chipsig_res;
185 struct clk *dsp_clk;
186 void __iomem *chipsig;
187 void __iomem *bootreg;
188 int irq;
189 int ret;
190
191 irq = platform_get_irq(pdev, 0);
192 if (irq < 0) {
193 dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq);
194 return irq;
195 }
196
197 irq_data = irq_get_irq_data(irq);
198 if (!irq_data) {
199 dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
200 return -EINVAL;
201 }
202
203 bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
204 if (!bootreg_res) {
205 dev_err(dev,
206 "platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
207 return -EADDRNOTAVAIL;
208 }
209
210 chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
211 if (!chipsig_res) {
212 dev_err(dev,
213 "platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
214 return -EADDRNOTAVAIL;
215 }
216
217 bootreg = devm_ioremap_resource(dev, bootreg_res);
218 if (IS_ERR(bootreg))
219 return PTR_ERR(bootreg);
220
221 chipsig = devm_ioremap_resource(dev, chipsig_res);
222 if (IS_ERR(chipsig))
223 return PTR_ERR(chipsig);
224
225 dsp_clk = devm_clk_get(dev, NULL);
226 if (IS_ERR(dsp_clk)) {
227 dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
228
229 return PTR_ERR(dsp_clk);
230 }
231
232 rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
233 sizeof(*drproc));
234 if (!rproc)
235 return -ENOMEM;
236
237 drproc = rproc->priv;
238 drproc->rproc = rproc;
239
240 platform_set_drvdata(pdev, rproc);
241
242 /* everything the ISR needs is now setup, so hook it up */
243 ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
244 handle_event, 0, "da8xx-remoteproc",
245 rproc);
246 if (ret) {
247 dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
248 goto free_rproc;
249 }
250
251 /*
252 * rproc_add() can end up enabling the DSP's clk with the DSP
253 * *not* in reset, but da8xx_rproc_start() needs the DSP to be
254 * held in reset at the time it is called.
255 */
256 ret = reset_assert(dev);
257 if (ret)
258 goto free_rproc;
259
260 drproc->chipsig = chipsig;
261 drproc->bootreg = bootreg;
262 drproc->ack_fxn = irq_data->chip->irq_ack;
263 drproc->irq_data = irq_data;
264 drproc->irq = irq;
265 drproc->dsp_clk = dsp_clk;
266
267 ret = rproc_add(rproc);
268 if (ret) {
269 dev_err(dev, "rproc_add failed: %d\n", ret);
270 goto free_rproc;
271 }
272
273 return 0;
274
275free_rproc:
276 rproc_put(rproc);
277
278 return ret;
279}
280
281static int da8xx_rproc_remove(struct platform_device *pdev)
282{
283 struct device *dev = &pdev->dev;
284 struct rproc *rproc = platform_get_drvdata(pdev);
285 struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
286
287 /*
288 * It's important to place the DSP in reset before going away,
289 * since a subsequent insmod of this module may enable the DSP's
290 * clock before its program/boot-address has been loaded and
291 * before this module's probe has had a chance to reset the DSP.
292 * Without the reset, the DSP can lockup permanently when it
293 * begins executing garbage.
294 */
295 reset_assert(dev);
296
297 /*
298 * The devm subsystem might end up releasing things before
299 * freeing the irq, thus allowing an interrupt to sneak in while
300 * the device is being removed. This should prevent that.
301 */
302 disable_irq(drproc->irq);
303
304 devm_clk_put(dev, drproc->dsp_clk);
305
306 rproc_del(rproc);
307 rproc_put(rproc);
308
309 return 0;
310}
311
312static struct platform_driver da8xx_rproc_driver = {
313 .probe = da8xx_rproc_probe,
314 .remove = da8xx_rproc_remove,
315 .driver = {
316 .name = "davinci-rproc",
317 .owner = THIS_MODULE,
318 },
319};
320
321module_platform_driver(da8xx_rproc_driver);
322
323MODULE_LICENSE("GPL v2");
324MODULE_DESCRIPTION("DA8XX Remote Processor control driver");