Message ID | 1364521307-1219-5-git-send-email-rtivy@ti.com (mailing list archive) |
---|---|
State | Not Applicable |
Headers | show |
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.
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
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
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
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!
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 --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");
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