diff mbox

[v9,4/6] ARM: davinci: Add a remoteproc driver implementation for OMAP-L13x DSP

Message ID 1364521307-1219-5-git-send-email-rtivy@ti.com (mailing list archive)
State Not Applicable
Headers show

Commit Message

Tivy, Robert March 29, 2013, 1:41 a.m. UTC
Signed-off-by: Robert Tivy <rtivy@ti.com>
---
 drivers/remoteproc/Kconfig            |   23 +++
 drivers/remoteproc/Makefile           |    1 +
 drivers/remoteproc/da8xx_remoteproc.c |  325 +++++++++++++++++++++++++++++++++
 3 files changed, 349 insertions(+)
 create mode 100644 drivers/remoteproc/da8xx_remoteproc.c

Comments

Ohad Ben Cohen April 7, 2013, 1:02 p.m. UTC | #1
Hi Rob,

On Fri, Mar 29, 2013 at 4:41 AM, Robert Tivy <rtivy@ti.com> wrote:> +
> +static int da8xx_rproc_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct da8xx_rproc *drproc;
> +       struct rproc *rproc;
> +       struct irq_data *irq_data;
> +       struct resource *bootreg_res;
> +       struct resource *chipsig_res;
> +       struct clk *dsp_clk;
> +       void __iomem *chipsig;
> +       void __iomem *bootreg;
> +       int irq;
> +       int ret;
> +
> +       irq = platform_get_irq(pdev, 0);
> +       if (irq < 0) {
> +               dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq);
> +               return irq;
> +       }
> +
> +       irq_data = irq_get_irq_data(irq);
> +       if (!irq_data) {
> +               dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
> +               return -EINVAL;
> +       }
> +
> +       bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (!bootreg_res) {
> +               dev_err(dev,
> +                       "platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
> +               return -EADDRNOTAVAIL;
> +       }
> +
> +       chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +       if (!chipsig_res) {
> +               dev_err(dev,
> +                       "platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
> +               return -EADDRNOTAVAIL;
> +       }
> +
> +       bootreg = devm_request_and_ioremap(dev, bootreg_res);
> +       if (!bootreg) {
> +               dev_err(dev, "unable to map boot register\n");
> +               return -EADDRNOTAVAIL;
> +       }
> +
> +       chipsig = devm_request_and_ioremap(dev, chipsig_res);
> +       if (!chipsig) {
> +               dev_err(dev, "unable to map CHIPSIG register\n");
> +               return -EADDRNOTAVAIL;
> +       }
> +
> +       dsp_clk = devm_clk_get(dev, NULL);
> +       if (IS_ERR(dsp_clk)) {
> +               dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
> +
> +               return PTR_RET(dsp_clk);
> +       }
> +
> +       rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
> +               sizeof(*drproc));
> +       if (!rproc)
> +               return -ENOMEM;
> +
> +       drproc = rproc->priv;
> +       drproc->rproc = rproc;
> +
> +       platform_set_drvdata(pdev, rproc);
> +
> +       /* everything the ISR needs is now setup, so hook it up */
> +       ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
> +               handle_event, 0, "da8xx-remoteproc", rproc);
> +       if (ret) {
> +               dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
> +               goto free_rproc;
> +       }
> +
> +       /*
> +        * rproc_add() can end up enabling the DSP's clk with the DSP
> +        * *not* in reset, but da8xx_rproc_start() needs the DSP to be
> +        * held in reset at the time it is called.
> +        */
> +       ret = reset_assert(dev);
> +       if (ret)
> +               goto free_rproc;
> +
> +       ret = rproc_add(rproc);
> +       if (ret) {
> +               dev_err(dev, "rproc_add failed: %d\n", ret);
> +               goto free_rproc;
> +       }
> +
> +       drproc->chipsig = chipsig;
> +       drproc->bootreg = bootreg;
> +       drproc->ack_fxn = irq_data->chip->irq_ack;
> +       drproc->irq_data = irq_data;
> +       drproc->irq = irq;
> +       drproc->dsp_clk = dsp_clk;
> +
> +       return 0;
> +
> +free_rproc:
> +       rproc_put(rproc);
> +
> +       return ret;
> +}

Two small changes please:

- could you please fix the error path of probe? it seems that some
resources are taken but not freed in case a failure shows up
subsequently
- seems like it's safer to set drproc before calling rproc_add
otherwise you have a small race there

If you could respin this one quickly it'd be great, thanks!

Ohad.
Sergei Shtylyov April 7, 2013, 5:55 p.m. UTC | #2
Hello.

On 04/07/2013 05:02 PM, Ohad Ben-Cohen wrote:

>
>> +static int da8xx_rproc_probe(struct platform_device *pdev)
>> +{
>> +       struct device *dev = &pdev->dev;
>> +       struct da8xx_rproc *drproc;
>> +       struct rproc *rproc;
>> +       struct irq_data *irq_data;
>> +       struct resource *bootreg_res;
>> +       struct resource *chipsig_res;
>> +       struct clk *dsp_clk;
>> +       void __iomem *chipsig;
>> +       void __iomem *bootreg;
>> +       int irq;
>> +       int ret;
>> +
[...]
>> +       bootreg = devm_request_and_ioremap(dev, bootreg_res);
>> +       if (!bootreg) {
>> +               dev_err(dev, "unable to map boot register\n");
>> +               return -EADDRNOTAVAIL;
>> +       }
>> +
>> +       chipsig = devm_request_and_ioremap(dev, chipsig_res);

    I suggest that you use more modern (yes, already a newer interface :-)
devm_ioremap_resource() instead -- it returns the error code (as a pointer)
in case of error, and it certainly doesn't require you to print error 
messages.

>> +       if (!chipsig) {
>> +               dev_err(dev, "unable to map CHIPSIG register\n");
>> +               return -EADDRNOTAVAIL;
>> +       }
>>

WBR, Sergei
Tivy, Robert April 8, 2013, 10:02 p.m. UTC | #3
Hi Ohad,

> -----Original Message-----
> From: Ohad Ben-Cohen [mailto:ohad@wizery.com]
> Sent: Sunday, April 07, 2013 6:03 AM
> To: Tivy, Robert
> Cc: davinci-linux-open-source; linux-arm; Nori, Sekhar; Rob Landley;
> linux-doc@vger.kernel.org; Grosen, Mark; Ring, Chris
> Subject: Re: [PATCH v9 4/6] ARM: davinci: Add a remoteproc driver
> implementation for OMAP-L13x DSP
> 
> Hi Rob,
> 
> On Fri, Mar 29, 2013 at 4:41 AM, Robert Tivy <rtivy@ti.com> wrote:> +
> > +static int da8xx_rproc_probe(struct platform_device *pdev)
> > +{
> > +       struct device *dev = &pdev->dev;
> > +       struct da8xx_rproc *drproc;
> > +       struct rproc *rproc;
> > +       struct irq_data *irq_data;
> > +       struct resource *bootreg_res;
> > +       struct resource *chipsig_res;
> > +       struct clk *dsp_clk;
> > +       void __iomem *chipsig;
> > +       void __iomem *bootreg;
> > +       int irq;
> > +       int ret;
> > +
> > +       irq = platform_get_irq(pdev, 0);
> > +       if (irq < 0) {
> > +               dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n",
> irq);
> > +               return irq;
> > +       }
> > +
> > +       irq_data = irq_get_irq_data(irq);
> > +       if (!irq_data) {
> > +               dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
> > +               return -EINVAL;
> > +       }
> > +
> > +       bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +       if (!bootreg_res) {
> > +               dev_err(dev,
> > +                       "platform_get_resource(IORESOURCE_MEM, 0):
> NULL\n");
> > +               return -EADDRNOTAVAIL;
> > +       }
> > +
> > +       chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> > +       if (!chipsig_res) {
> > +               dev_err(dev,
> > +                       "platform_get_resource(IORESOURCE_MEM, 1):
> NULL\n");
> > +               return -EADDRNOTAVAIL;
> > +       }
> > +
> > +       bootreg = devm_request_and_ioremap(dev, bootreg_res);
> > +       if (!bootreg) {
> > +               dev_err(dev, "unable to map boot register\n");
> > +               return -EADDRNOTAVAIL;
> > +       }
> > +
> > +       chipsig = devm_request_and_ioremap(dev, chipsig_res);
> > +       if (!chipsig) {
> > +               dev_err(dev, "unable to map CHIPSIG register\n");
> > +               return -EADDRNOTAVAIL;
> > +       }
> > +
> > +       dsp_clk = devm_clk_get(dev, NULL);
> > +       if (IS_ERR(dsp_clk)) {
> > +               dev_err(dev, "clk_get error: %ld\n",
> PTR_ERR(dsp_clk));
> > +
> > +               return PTR_RET(dsp_clk);
> > +       }
> > +
> > +       rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops,
> da8xx_fw_name,
> > +               sizeof(*drproc));
> > +       if (!rproc)
> > +               return -ENOMEM;
> > +
> > +       drproc = rproc->priv;
> > +       drproc->rproc = rproc;
> > +
> > +       platform_set_drvdata(pdev, rproc);
> > +
> > +       /* everything the ISR needs is now setup, so hook it up */
> > +       ret = devm_request_threaded_irq(dev, irq,
> da8xx_rproc_callback,
> > +               handle_event, 0, "da8xx-remoteproc", rproc);
> > +       if (ret) {
> > +               dev_err(dev, "devm_request_threaded_irq error: %d\n",
> ret);
> > +               goto free_rproc;
> > +       }
> > +
> > +       /*
> > +        * rproc_add() can end up enabling the DSP's clk with the DSP
> > +        * *not* in reset, but da8xx_rproc_start() needs the DSP to
> be
> > +        * held in reset at the time it is called.
> > +        */
> > +       ret = reset_assert(dev);
> > +       if (ret)
> > +               goto free_rproc;
> > +
> > +       ret = rproc_add(rproc);
> > +       if (ret) {
> > +               dev_err(dev, "rproc_add failed: %d\n", ret);
> > +               goto free_rproc;
> > +       }
> > +
> > +       drproc->chipsig = chipsig;
> > +       drproc->bootreg = bootreg;
> > +       drproc->ack_fxn = irq_data->chip->irq_ack;
> > +       drproc->irq_data = irq_data;
> > +       drproc->irq = irq;
> > +       drproc->dsp_clk = dsp_clk;
> > +
> > +       return 0;
> > +
> > +free_rproc:
> > +       rproc_put(rproc);
> > +
> > +       return ret;
> > +}
> 
> Two small changes please:
> 
> - could you please fix the error path of probe? it seems that some
> resources are taken but not freed in case a failure shows up
> subsequently

I'm not sure what resources you mean?

The platform_get_irq()/irq_get_irq_data()/platform_get_resource() calls don't have "put" counterparts.  The "devm_*" calls get automatically cleaned up (and I wasn't able to find any existing example of some code that actually does the cleanup explicitly).

> - seems like it's safer to set drproc before calling rproc_add
> otherwise you have a small race there

Agreed, will change.

> 
> If you could respin this one quickly it'd be great, thanks!
> 
> Ohad.

Sergei had a comment to change devm_request_and_ioremap() to devm_ioremap_resource(), so I will submit a new patch with your and Sergei's comments applied.

Regards,

- Rob
Tivy, Robert April 8, 2013, 11:55 p.m. UTC | #4
Hi Sergei,

> -----Original Message-----
> From: Sergei Shtylyov [mailto:sergei.shtylyov@cogentembedded.com]
> Sent: Sunday, April 07, 2013 10:55 AM
> 
> Hello.
> 
> On 04/07/2013 05:02 PM, Ohad Ben-Cohen wrote:
> 
> >
> >> +static int da8xx_rproc_probe(struct platform_device *pdev)
> >> +{
> >> +       struct device *dev = &pdev->dev;
> >> +       struct da8xx_rproc *drproc;
> >> +       struct rproc *rproc;
> >> +       struct irq_data *irq_data;
> >> +       struct resource *bootreg_res;
> >> +       struct resource *chipsig_res;
> >> +       struct clk *dsp_clk;
> >> +       void __iomem *chipsig;
> >> +       void __iomem *bootreg;
> >> +       int irq;
> >> +       int ret;
> >> +
> [...]
> >> +       bootreg = devm_request_and_ioremap(dev, bootreg_res);
> >> +       if (!bootreg) {
> >> +               dev_err(dev, "unable to map boot register\n");
> >> +               return -EADDRNOTAVAIL;
> >> +       }
> >> +
> >> +       chipsig = devm_request_and_ioremap(dev, chipsig_res);
> 
>     I suggest that you use more modern (yes, already a newer interface
> :-)
> devm_ioremap_resource() instead -- it returns the error code (as a
> pointer)
> in case of error, and it certainly doesn't require you to print error
> messages.

Thanks, will do.

I appreciate the notice of a more modern function, it's really tough to keep up with the flurry of activity to the kernel.

Regarding this change, should the code use
	return PTR_ERR(bootreg);
or
	return PTR_RET(bootreg);
I ask because PTR_ERR() returns 'long' whereas PTR_RET() returns 'int' (and probe returns 'int'), but I see that the majority of existing code uses "return PTR_ERR()" in probe functions.

> 
> >> +       if (!chipsig) {
> >> +               dev_err(dev, "unable to map CHIPSIG register\n");
> >> +               return -EADDRNOTAVAIL;
> >> +       }
> >>
> 
> WBR, Sergei

Regards,

- Rob
Ohad Ben Cohen April 9, 2013, 9:03 a.m. UTC | #5
On Tue, Apr 9, 2013 at 1:02 AM, Tivy, Robert <rtivy@ti.com> wrote:
> The platform_get_irq()/irq_get_irq_data()/platform_get_resource() calls don't have "put" counterparts.  The "devm_*" calls get automatically cleaned up (and I wasn't able to find any existing example of some code that actually does the cleanup explicitly).

Fair enough, thanks for looking.

> Sergei had a comment to change devm_request_and_ioremap() to devm_ioremap_resource(), so I will submit a new patch with your and Sergei's comments applied.

Thanks!
Sergei Shtylyov April 9, 2013, 1:49 p.m. UTC | #6
Hello.

On 09-04-2013 3:55, Tivy, Robert wrote:

>>>> +static int da8xx_rproc_probe(struct platform_device *pdev)
>>>> +{
>>>> +       struct device *dev = &pdev->dev;
>>>> +       struct da8xx_rproc *drproc;
>>>> +       struct rproc *rproc;
>>>> +       struct irq_data *irq_data;
>>>> +       struct resource *bootreg_res;
>>>> +       struct resource *chipsig_res;
>>>> +       struct clk *dsp_clk;
>>>> +       void __iomem *chipsig;
>>>> +       void __iomem *bootreg;
>>>> +       int irq;
>>>> +       int ret;
>>>> +
>> [...]
>>>> +       bootreg = devm_request_and_ioremap(dev, bootreg_res);
>>>> +       if (!bootreg) {
>>>> +               dev_err(dev, "unable to map boot register\n");
>>>> +               return -EADDRNOTAVAIL;
>>>> +       }
>>>> +
>>>> +       chipsig = devm_request_and_ioremap(dev, chipsig_res);

>>      I suggest that you use more modern (yes, already a newer interface
>> :-)
>> devm_ioremap_resource() instead -- it returns the error code (as a
>> pointer)
>> in case of error, and it certainly doesn't require you to print error
>> messages.

> Thanks, will do.

> I appreciate the notice of a more modern function, it's really tough to keep up with the flurry of activity to the kernel.

> Regarding this change, should the code use
> 	return PTR_ERR(bootreg);
> or
> 	return PTR_RET(bootreg);

    The former, to avoid duplicate IS_ERR() check.

> I ask because PTR_ERR() returns 'long' whereas PTR_RET() returns 'int' (and probe returns 'int'), but I see that the majority of existing code uses "return PTR_ERR()" in probe functions.

    But PTR_RET() uses PTR_ERR() internally anyway.

>>>> +       if (!chipsig) {
>>>> +               dev_err(dev, "unable to map CHIPSIG register\n");
>>>> +               return -EADDRNOTAVAIL;
>>>> +       }

WBR, Sergei
diff mbox

Patch

diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index c6d77e2..394c1b4 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -38,4 +38,27 @@  config STE_MODEM_RPROC
 	  This can be either built-in or a loadable module.
 	  If unsure say N.
 
+config DA8XX_REMOTEPROC
+	tristate "DA8xx/OMAP-L13x remoteproc support (EXPERIMENTAL)"
+	depends on ARCH_DAVINCI_DA8XX
+	select CMA
+	select REMOTEPROC
+	select RPMSG
+	help
+	  Say y here to support DA8xx/OMAP-L13x remote processors via the
+	  remote processor framework.
+
+	  You want to say y here in order to enable AMP
+	  use-cases to run on your platform (multimedia codecs are
+	  offloaded to remote DSP processors using this framework).
+
+	  This module controls the name of the firmware file that gets
+	  loaded on the DSP.  This file must reside in the /lib/firmware
+	  directory.  It can be specified via the module parameter
+	  da8xx_fw_name=<filename>, and if not specified will default to
+	  "rproc-dsp-fw".
+
+	  It's safe to say n here if you're not interested in multimedia
+	  offloading.
+
 endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 391b651..ac2ff75 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -9,3 +9,4 @@  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
+obj-$(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 0000000..5e26aa8
--- /dev/null
+++ b/drivers/remoteproc/da8xx_remoteproc.c
@@ -0,0 +1,325 @@ 
+/*
+ * Remote processor machine-specific module for DA8XX
+ *
+ * Copyright (C) 2013 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+
+#include <mach/clock.h>   /* for davinci_clk_reset_assert/deassert() */
+
+#include "remoteproc_internal.h"
+
+static char *da8xx_fw_name;
+module_param(da8xx_fw_name, charp, S_IRUGO);
+MODULE_PARM_DESC(da8xx_fw_name,
+	"\n\t\tName of DSP firmware file in /lib/firmware"
+	" (if not specified defaults to 'rproc-dsp-fw')");
+
+/*
+ * OMAP-L138 Technical References:
+ * http://www.ti.com/product/omap-l138
+ */
+#define SYSCFG_CHIPSIG0 BIT(0)
+#define SYSCFG_CHIPSIG1 BIT(1)
+#define SYSCFG_CHIPSIG2 BIT(2)
+#define SYSCFG_CHIPSIG3 BIT(3)
+#define SYSCFG_CHIPSIG4 BIT(4)
+
+/**
+ * struct da8xx_rproc - da8xx remote processor instance state
+ * @rproc: rproc handle
+ * @dsp_clk: placeholder for platform's DSP clk
+ * @ack_fxn: chip-specific ack function for ack'ing irq
+ * @irq_data: ack_fxn function parameter
+ * @chipsig: virt ptr to DSP interrupt registers (CHIPSIG & CHIPSIG_CLR)
+ * @bootreg: virt ptr to DSP boot address register (HOST1CFG)
+ * @irq: irq # used by this instance
+ */
+struct da8xx_rproc {
+	struct rproc *rproc;
+	struct clk *dsp_clk;
+	void (*ack_fxn)(struct irq_data *data);
+	struct irq_data *irq_data;
+	void __iomem *chipsig;
+	void __iomem *bootreg;
+	int irq;
+};
+
+/**
+ * handle_event() - inbound virtqueue message workqueue function
+ *
+ * This function is registered as a kernel thread and is scheduled by the
+ * kernel handler.
+ */
+static irqreturn_t handle_event(int irq, void *p)
+{
+	struct rproc *rproc = (struct rproc *)p;
+
+	/* Process incoming buffers on all our vrings */
+	rproc_vq_interrupt(rproc, 0);
+	rproc_vq_interrupt(rproc, 1);
+
+	return IRQ_HANDLED;
+}
+
+/**
+ * da8xx_rproc_callback() - inbound virtqueue message handler
+ *
+ * This handler is invoked directly by the kernel whenever the remote
+ * core (DSP) has modified the state of a virtqueue.  There is no
+ * "payload" message indicating the virtqueue index as is the case with
+ * mailbox-based implementations on OMAP4.  As such, this handler "polls"
+ * each known virtqueue index for every invocation.
+ */
+static irqreturn_t da8xx_rproc_callback(int irq, void *p)
+{
+	struct rproc *rproc = (struct rproc *)p;
+	struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+	u32 chipsig;
+
+	chipsig = readl(drproc->chipsig);
+	if (chipsig & SYSCFG_CHIPSIG0) {
+		/* Clear interrupt level source */
+		writel(SYSCFG_CHIPSIG0, drproc->chipsig + 4);
+
+		/*
+		 * ACK intr to AINTC.
+		 *
+		 * It has already been ack'ed by the kernel before calling
+		 * this function, but since the ARM<->DSP interrupts in the
+		 * CHIPSIG register are "level" instead of "pulse" variety,
+		 * we need to ack it after taking down the level else we'll
+		 * be called again immediately after returning.
+		 */
+		drproc->ack_fxn(drproc->irq_data);
+
+		return IRQ_WAKE_THREAD;
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int da8xx_rproc_start(struct rproc *rproc)
+{
+	struct device *dev = rproc->dev.parent;
+	struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+	struct clk *dsp_clk = drproc->dsp_clk;
+
+	/* hw requires the start (boot) address be on 1KB boundary */
+	if (rproc->bootaddr & 0x3ff) {
+		dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
+
+		return -EINVAL;
+	}
+	writel(rproc->bootaddr, drproc->bootreg);
+
+	clk_enable(dsp_clk);
+	davinci_clk_reset_deassert(dsp_clk);
+
+	return 0;
+}
+
+static int da8xx_rproc_stop(struct rproc *rproc)
+{
+	struct da8xx_rproc *drproc = rproc->priv;
+
+	clk_disable(drproc->dsp_clk);
+
+	return 0;
+}
+
+/* kick a virtqueue */
+static void da8xx_rproc_kick(struct rproc *rproc, int vqid)
+{
+	struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+
+	/* Interupt remote proc */
+	writel(SYSCFG_CHIPSIG2, drproc->chipsig);
+}
+
+static struct rproc_ops da8xx_rproc_ops = {
+	.start = da8xx_rproc_start,
+	.stop = da8xx_rproc_stop,
+	.kick = da8xx_rproc_kick,
+};
+
+static int reset_assert(struct device *dev)
+{
+	struct clk *dsp_clk;
+
+	dsp_clk = clk_get(dev, NULL);
+	if (IS_ERR(dsp_clk)) {
+		dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
+		return PTR_RET(dsp_clk);
+	}
+	davinci_clk_reset_assert(dsp_clk);
+	clk_put(dsp_clk);
+
+	return 0;
+}
+
+static int da8xx_rproc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct da8xx_rproc *drproc;
+	struct rproc *rproc;
+	struct irq_data *irq_data;
+	struct resource *bootreg_res;
+	struct resource *chipsig_res;
+	struct clk *dsp_clk;
+	void __iomem *chipsig;
+	void __iomem *bootreg;
+	int irq;
+	int ret;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "platform_get_irq(pdev, 0) error: %d\n", irq);
+		return irq;
+	}
+
+	irq_data = irq_get_irq_data(irq);
+	if (!irq_data) {
+		dev_err(dev, "irq_get_irq_data(%d): NULL\n", irq);
+		return -EINVAL;
+	}
+
+	bootreg_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!bootreg_res) {
+		dev_err(dev,
+			"platform_get_resource(IORESOURCE_MEM, 0): NULL\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	chipsig_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!chipsig_res) {
+		dev_err(dev,
+			"platform_get_resource(IORESOURCE_MEM, 1): NULL\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	bootreg = devm_request_and_ioremap(dev, bootreg_res);
+	if (!bootreg) {
+		dev_err(dev, "unable to map boot register\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	chipsig = devm_request_and_ioremap(dev, chipsig_res);
+	if (!chipsig) {
+		dev_err(dev, "unable to map CHIPSIG register\n");
+		return -EADDRNOTAVAIL;
+	}
+
+	dsp_clk = devm_clk_get(dev, NULL);
+	if (IS_ERR(dsp_clk)) {
+		dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
+
+		return PTR_RET(dsp_clk);
+	}
+
+	rproc = rproc_alloc(dev, "dsp", &da8xx_rproc_ops, da8xx_fw_name,
+		sizeof(*drproc));
+	if (!rproc)
+		return -ENOMEM;
+
+	drproc = rproc->priv;
+	drproc->rproc = rproc;
+
+	platform_set_drvdata(pdev, rproc);
+
+	/* everything the ISR needs is now setup, so hook it up */
+	ret = devm_request_threaded_irq(dev, irq, da8xx_rproc_callback,
+		handle_event, 0, "da8xx-remoteproc", rproc);
+	if (ret) {
+		dev_err(dev, "devm_request_threaded_irq error: %d\n", ret);
+		goto free_rproc;
+	}
+
+	/*
+	 * rproc_add() can end up enabling the DSP's clk with the DSP
+	 * *not* in reset, but da8xx_rproc_start() needs the DSP to be
+	 * held in reset at the time it is called.
+	 */
+	ret = reset_assert(dev);
+	if (ret)
+		goto free_rproc;
+
+	ret = rproc_add(rproc);
+	if (ret) {
+		dev_err(dev, "rproc_add failed: %d\n", ret);
+		goto free_rproc;
+	}
+
+	drproc->chipsig = chipsig;
+	drproc->bootreg = bootreg;
+	drproc->ack_fxn = irq_data->chip->irq_ack;
+	drproc->irq_data = irq_data;
+	drproc->irq = irq;
+	drproc->dsp_clk = dsp_clk;
+
+	return 0;
+
+free_rproc:
+	rproc_put(rproc);
+
+	return ret;
+}
+
+static int da8xx_rproc_remove(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rproc *rproc = platform_get_drvdata(pdev);
+	struct da8xx_rproc *drproc = (struct da8xx_rproc *)rproc->priv;
+
+	/*
+	 * It's important to place the DSP in reset before going away,
+	 * since a subsequent insmod of this module may enable the DSP's
+	 * clock before its program/boot-address has been loaded and
+	 * before this module's probe has had a chance to reset the DSP.
+	 * Without the reset, the DSP can lockup permanently when it
+	 * begins executing garbage.
+	 */
+	reset_assert(dev);
+
+	/*
+	 * The devm subsystem might end up releasing things before
+	 * freeing the irq, thus allowing an interrupt to sneak in while
+	 * the device is being removed.  This should prevent that.
+	 */
+	disable_irq(drproc->irq);
+
+	devm_clk_put(dev, drproc->dsp_clk);
+
+	rproc_del(rproc);
+	rproc_put(rproc);
+
+	return 0;
+}
+
+static struct platform_driver da8xx_rproc_driver = {
+	.probe = da8xx_rproc_probe,
+	.remove = da8xx_rproc_remove,
+	.driver = {
+		.name = "davinci-rproc",
+		.owner = THIS_MODULE,
+	},
+};
+
+module_platform_driver(da8xx_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DA8XX Remote Processor control driver");