Linux Audio

Check our new training course

Real-Time Linux with PREEMPT_RT training

Feb 18-20, 2025
Register
Loading...
Note: File does not exist in v3.1.
  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_ERR(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	bootreg = devm_ioremap_resource(dev, bootreg_res);
205	if (IS_ERR(bootreg))
206		return PTR_ERR(bootreg);
207
208	chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
209	chipsig = devm_ioremap_resource(dev, chipsig_res);
210	if (IS_ERR(chipsig))
211		return PTR_ERR(chipsig);
212
213	dsp_clk = devm_clk_get(dev, NULL);
214	if (IS_ERR(dsp_clk)) {
215		dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
216
217		return PTR_ERR(dsp_clk);
218	}
219
220	rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
221		sizeof(*drproc));
222	if (!rproc)
223		return -ENOMEM;
224
225	drproc = rproc->priv;
226	drproc->rproc = rproc;
227
228	platform_set_drvdata(pdev, rproc);
229
230	/* everything the ISR needs is now setup, so hook it up */
231	ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
232					handle_event, 0, "da8xx-remoteproc",
233					rproc);
234	if (ret) {
235		dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
236		goto free_rproc;
237	}
238
239	/*
240	 * rproc_add() can end up enabling the DSP's clk with the DSP
241	 * *not* in reset, but da8xx_rproc_start() needs the DSP to be
242	 * held in reset at the time it is called.
243	 */
244	ret = reset_assert(dev);
245	if (ret)
246		goto free_rproc;
247
248	drproc->chipsig = chipsig;
249	drproc->bootreg = bootreg;
250	drproc->ack_fxn = irq_data->chip->irq_ack;
251	drproc->irq_data = irq_data;
252	drproc->irq = irq;
253	drproc->dsp_clk = dsp_clk;
254
255	ret = rproc_add(rproc);
256	if (ret) {
257		dev_err(dev, "rproc_add failed: %d\n", ret);
258		goto free_rproc;
259	}
260
261	return 0;
262
263free_rproc:
264	rproc_put(rproc);
265
266	return ret;
267}
268
269static int da8xx_rproc_remove(struct platform_device *pdev)
270{
271	struct device *dev = &pdev->dev;
272	struct rproc *rproc = platform_get_drvdata(pdev);
273	struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
274
275	/*
276	 * It's important to place the DSP in reset before going away,
277	 * since a subsequent insmod of this module may enable the DSP's
278	 * clock before its program/boot-address has been loaded and
279	 * before this module's probe has had a chance to reset the DSP.
280	 * Without the reset, the DSP can lockup permanently when it
281	 * begins executing garbage.
282	 */
283	reset_assert(dev);
284
285	/*
286	 * The devm subsystem might end up releasing things before
287	 * freeing the irq, thus allowing an interrupt to sneak in while
288	 * the device is being removed.  This should prevent that.
289	 */
290	disable_irq(drproc->irq);
291
292	rproc_del(rproc);
293	rproc_put(rproc);
294
295	return 0;
296}
297
298static struct platform_driver da8xx_rproc_driver = {
299	.probe = da8xx_rproc_probe,
300	.remove = da8xx_rproc_remove,
301	.driver = {
302		.name = "davinci-rproc",
303		.owner = THIS_MODULE,
304	},
305};
306
307module_platform_driver(da8xx_rproc_driver);
308
309MODULE_LICENSE("GPL v2");
310MODULE_DESCRIPTION("DA8XX Remote Processor control driver");