Message ID | 201210092127.q99LRJQh016676@sanblnx02.sanb.design.ti.com (mailing list archive) |
---|---|
State | Changes Requested |
Headers | show |
Hi Rob, On 9/21/2012 6:27 AM, Robert Tivy wrote: > From: Robert Tivy <rtivy@ti.com> > > Still using the "old" way of reserving some contiguous physical memory > during kernel boot, need to switch to new CMA framework. > > Signed-off-by: Robert Tivy <rtivy@ti.com> > --- > arch/arm/mach-davinci/board-da850-evm.c | 8 +- > arch/arm/mach-davinci/board-omapl138-hawk.c | 8 +- > arch/arm/mach-davinci/clock.h | 3 +- > arch/arm/mach-davinci/da850.c | 19 +- > arch/arm/mach-davinci/devices-da8xx.c | 53 +++++ > arch/arm/mach-davinci/include/mach/da8xx.h | 2 + > arch/arm/mach-davinci/include/mach/psc.h | 3 +- > arch/arm/mach-davinci/include/mach/remoteproc.h | 37 +++ > arch/arm/mach-davinci/psc.c | 9 +- > drivers/remoteproc/Kconfig | 19 ++ > drivers/remoteproc/Makefile | 1 + > drivers/remoteproc/davinci_remoteproc.c | 273 +++++++++++++++++++++++ This patch seems to be doing too many things. The drivers portion should be separated out from davinci architecture improvements, soc support and board support. > 12 files changed, 429 insertions(+), 6 deletions(-) > create mode 100644 arch/arm/mach-davinci/include/mach/remoteproc.h > create mode 100644 drivers/remoteproc/davinci_remoteproc.c > > diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c > index 44f6bb0..482d95b 100644 > --- a/arch/arm/mach-davinci/board-da850-evm.c > +++ b/arch/arm/mach-davinci/board-da850-evm.c > @@ -1,7 +1,7 @@ > /* > * TI DA850/OMAP-L138 EVM board > * > - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ > + * Copyright (C) 2009-2012 Texas Instruments Incorporated - http://www.ti.com/ There is no need to update copyright headers on each file you touch. Here and in other places. > * > * Derived from: arch/arm/mach-davinci/board-da830-evm.c > * Original Copyrights follow: > @@ -44,6 +44,7 @@ > #include <mach/mux.h> > #include <mach/aemif.h> > #include <mach/spi.h> > +#include <mach/remoteproc.h> > > #define DA850_EVM_PHY_ID "davinci_mdio-0:00" > #define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8) > @@ -1387,6 +1388,10 @@ static __init void da850_evm_init(void) > ret); > > da850_evm_setup_mac_addr(); > + > + ret = da8xx_register_rproc(); > + if (ret) > + pr_warn("dsp/rproc registration failed: %d\n", ret); > } > > #ifdef CONFIG_SERIAL_8250_CONSOLE > @@ -1414,4 +1419,5 @@ MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") > .init_late = davinci_init_late, > .dma_zone_size = SZ_128M, > .restart = da8xx_restart, > + .reserve = da8xx_rproc_reserve_contig, > MACHINE_END > diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c > index e5ce9c4..3c9e7ee 100644 > --- a/arch/arm/mach-davinci/board-omapl138-hawk.c > +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c > @@ -3,7 +3,7 @@ > * > * Initial code: Syed Mohammed Khasim > * > - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com > + * Copyright (C) 2009-2012 Texas Instruments Incorporated - http://www.ti.com > * > * This file is licensed under the terms of the GNU General Public License > * version 2. This program is licensed "as is" without any warranty of > @@ -20,6 +20,7 @@ > #include <mach/cp_intc.h> > #include <mach/da8xx.h> > #include <mach/mux.h> > +#include <mach/remoteproc.h> > > #define HAWKBOARD_PHY_ID "davinci_mdio-0:07" > #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) > @@ -319,6 +320,10 @@ static __init void omapl138_hawk_init(void) > pr_warn("omapl138_hawk_init: " > "watchdog registration failed: %d\n", > ret); > + > + ret = da8xx_register_rproc(); > + if (ret) > + pr_warn("dsp/rproc registration failed: %d\n", ret); It will be nice to print the function name too. Here and in other places. > } > > #ifdef CONFIG_SERIAL_8250_CONSOLE > @@ -346,4 +351,5 @@ MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") > .init_late = davinci_init_late, > .dma_zone_size = SZ_128M, > .restart = da8xx_restart, > + .reserve = da8xx_rproc_reserve_contig, > MACHINE_END > diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h > index 46f0f1b..8481638 100644 > --- a/arch/arm/mach-davinci/clock.h > +++ b/arch/arm/mach-davinci/clock.h > @@ -1,7 +1,7 @@ > /* > * TI DaVinci clock definitions > * > - * Copyright (C) 2006-2007 Texas Instruments. > + * Copyright (C) 2006-2012 Texas Instruments. > * Copyright (C) 2008-2009 Deep Root Systems, LLC > * > * This program is free software; you can redistribute it and/or modify > @@ -112,6 +112,7 @@ struct clk { > #define PRE_PLL BIT(4) /* source is before PLL mult/div */ > #define PSC_SWRSTDISABLE BIT(5) /* Disable state is SwRstDisable */ > #define PSC_FORCE BIT(6) /* Force module state transtition */ > +#define PSC_LRST BIT(8) /* Use local reset on enable/disable */ > > #define CLK(dev, con, ck) \ > { \ > diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c > index b44dc84..539f5a8 100644 > --- a/arch/arm/mach-davinci/da850.c > +++ b/arch/arm/mach-davinci/da850.c > @@ -1,7 +1,7 @@ > /* > * TI DA850/OMAP-L138 chip specific setup > * > - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ > + * Copyright (C) 2009-2012 Texas Instruments Incorporated - http://www.ti.com/ > * > * Derived from: arch/arm/mach-davinci/da830.c > * Original Copyrights follow: > @@ -76,6 +76,13 @@ static struct clk pll0_aux_clk = { > .flags = CLK_PLL | PRE_PLL, > }; > > +static struct clk pll0_sysclk1 = { > + .name = "pll0_sysclk1", > + .parent = &pll0_clk, > + .flags = CLK_PLL, > + .div_reg = PLLDIV1, > +}; > + > static struct clk pll0_sysclk2 = { > .name = "pll0_sysclk2", > .parent = &pll0_clk, > @@ -355,10 +362,19 @@ static struct clk sata_clk = { > .flags = PSC_FORCE, > }; > > +static struct clk dsp_clk = { > + .name = "dsp", > + .parent = &pll0_sysclk1, > + .domain = DAVINCI_GPSC_DSPDOMAIN, > + .lpsc = DA8XX_LPSC0_GEM, > + .flags = PSC_LRST, > +}; > + > static struct clk_lookup da850_clks[] = { > CLK(NULL, "ref", &ref_clk), > CLK(NULL, "pll0", &pll0_clk), > CLK(NULL, "pll0_aux", &pll0_aux_clk), > + CLK(NULL, "pll0_sysclk1", &pll0_sysclk1), > CLK(NULL, "pll0_sysclk2", &pll0_sysclk2), > CLK(NULL, "pll0_sysclk3", &pll0_sysclk3), > CLK(NULL, "pll0_sysclk4", &pll0_sysclk4), > @@ -398,6 +414,7 @@ static struct clk_lookup da850_clks[] = { > CLK("spi_davinci.0", NULL, &spi0_clk), > CLK("spi_davinci.1", NULL, &spi1_clk), > CLK("ahci", NULL, &sata_clk), > + CLK(NULL, "dsp", &dsp_clk), Looking up the clock only using con_id is not correct. Need device name too. And if its only one clock the device needs, then con_id should be NULL. > CLK(NULL, NULL, NULL), > }; > > diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c > index fa5ea17..f2367be 100644 > --- a/arch/arm/mach-davinci/devices-da8xx.c > +++ b/arch/arm/mach-davinci/devices-da8xx.c > @@ -1,6 +1,7 @@ > /* > * DA8XX/OMAP L1XX platform device data > * > + * Copyright (C) 2012 Texas Instruments, Inc. > * Copyright (c) 2007-2009, MontaVista Software, Inc. <source@mvista.com> > * Derived from code that was: > * Copyright (C) 2006 Komal Shah <komal_shah802003@yahoo.com> > @@ -16,12 +17,14 @@ > #include <linux/serial_8250.h> > #include <linux/ahci_platform.h> > #include <linux/clk.h> > +#include <linux/memblock.h> > > #include <mach/cputype.h> > #include <mach/common.h> > #include <mach/time.h> > #include <mach/da8xx.h> > #include <mach/cpuidle.h> > +#include <mach/remoteproc.h> > > #include "clock.h" > > @@ -660,6 +663,56 @@ int __init da850_register_mmcsd1(struct davinci_mmc_config *config) > } > #endif > > +/* > + * The following address range was chosen because the XDC Platform for > + * OMAP-L138 has this range as its default code/data placement. > + * > + * System integrators must ensure that Linux does not own this range. > + */ > +#define DA_CONTIG_BASE (0xc3000000) > +#define DA_CONTIG_SIZE (0x02000000) Hardcoding RAM addresses like this is not good. I believe CMA will help you here. See below too. > + > +void __init da8xx_rproc_reserve_contig(void) > +{ > + pr_info("reserving contig memory\n"); This should be pr_debug()? Also, some context will be useful to figure out which part of kernel is printing this. > + > + if (memblock_is_region_reserved(DA_CONTIG_BASE, DA_CONTIG_SIZE) || > + memblock_reserve(DA_CONTIG_BASE, DA_CONTIG_SIZE) < 0) { > + pr_err("memory already reserved\n"); > + return; > + } > +} > + > +static struct platform_device *da8xx_dsp; > + > +int __init da8xx_register_rproc(void) > +{ > + struct da8xx_rproc_pdata rproc_pdata = { > + .name = "dsp", > + .firmware = "da8xx-dsp.xe674", > + .clk_name = "dsp", Passing clock name from platform data is wrong. This means that you are bypassing the clock API abstraction. > + }; > + int ret; > + > + da8xx_dsp = platform_device_register_data(NULL, "davinci-rproc", 0, > + &rproc_pdata, sizeof(rproc_pdata)); > + > + ret = dma_declare_coherent_memory(&da8xx_dsp->dev, > + DA_CONTIG_BASE, DA_CONTIG_BASE, > + DA_CONTIG_SIZE, > + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); For contiguous memory needs can you look at the CMA framework (include/linux/dma-contiguous.h). It has recently been enabled on ARMv5 and has been used on DA850 for video buffers. > + > + if (!(ret & DMA_MEMORY_MAP)) { > + pr_err("dma_declare_coherent failure\n"); > + > + platform_device_unregister(da8xx_dsp); > + > + return -ENOMEM; Unnecessary newlines here. > + } > + > + return 0; > +}; > + > static struct resource da8xx_rtc_resources[] = { > { > .start = DA8XX_RTC_BASE, > diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h > index a2f1f27..12bcc12 100644 > --- a/arch/arm/mach-davinci/include/mach/da8xx.h > +++ b/arch/arm/mach-davinci/include/mach/da8xx.h > @@ -3,6 +3,7 @@ > * > * Author: Mark A. Greer <mgreer@mvista.com> > * > + * Copyright (C) 2011-2012 Texas Instruments, Inc. No need to add a copyright for a single line addition. > * 2007, 2009-2010 (c) MontaVista Software, Inc. This file is licensed under > * the terms of the GNU General Public License version 2. This program > * is licensed "as is" without any warranty of any kind, whether express > @@ -91,6 +92,7 @@ int da850_register_cpufreq(char *async_clk); > int da8xx_register_cpuidle(void); > void __iomem * __init da8xx_get_mem_ctlr(void); > int da850_register_pm(struct platform_device *pdev); > +int da8xx_register_rproc(void); > int __init da850_register_sata(unsigned long refclkpn); > void da8xx_restart(char mode, const char *cmd); > > diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h > index 405318e..b119411 100644 > --- a/arch/arm/mach-davinci/include/mach/psc.h > +++ b/arch/arm/mach-davinci/include/mach/psc.h > @@ -1,7 +1,7 @@ > /* > * DaVinci Power & Sleep Controller (PSC) defines > * > - * Copyright (C) 2006 Texas Instruments. > + * Copyright (C) 2006-2012 Texas Instruments. > * > * This program is free software; you can redistribute it and/or modify it > * under the terms of the GNU General Public License as published by the > @@ -245,6 +245,7 @@ > > #define MDSTAT_STATE_MASK 0x3f > #define PDSTAT_STATE_MASK 0x1f > +#define MDCTL_LRST BIT(8) > #define MDCTL_FORCE BIT(31) > #define PDCTL_NEXT BIT(0) > #define PDCTL_EPCGOOD BIT(8) > diff --git a/arch/arm/mach-davinci/include/mach/remoteproc.h b/arch/arm/mach-davinci/include/mach/remoteproc.h > new file mode 100644 > index 0000000..0f56688 > --- /dev/null > +++ b/arch/arm/mach-davinci/include/mach/remoteproc.h > @@ -0,0 +1,37 @@ > +/* > + * Remote Processor > + * > + * Copyright (C) 2011-2012 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. > + * > + * This program is distributed in the hope that it will be useful, > + * but WITHOUT ANY WARRANTY; without even the implied warranty of > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the > + * GNU General Public License for more details. > + */ > + > +#ifndef _MACH_REMOTEPROC_H > +#define _MACH_REMOTEPROC_H > + > +#include <linux/remoteproc.h> > + > +/* > + * struct da8xx_rproc_pdata - da8xx remoteproc's platform data > + * @name: the remoteproc's name > + * @clk_name: the remoteproc's clock > + * @firmware: name of firmware file to load > + * @ops: start/stop rproc handlers > + */ > +struct da8xx_rproc_pdata { > + const char *name; > + const char *clk_name; > + const char *firmware; > + const struct rproc_ops *ops; > +}; All platform data should be in include/linux/platform_data or in a place that driver mandates, not in mach folder. > + > +void __init da8xx_rproc_reserve_contig(void); Since this function is used locally inside mach-davinci it can be moved to a header file local to this folder instead of keeping it in include/mach. > + > +#endif /* _MACH_REMOTEPROC_H */ > diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c > index bddaba9..c6e9d9f 100644 > --- a/arch/arm/mach-davinci/psc.c > +++ b/arch/arm/mach-davinci/psc.c > @@ -1,7 +1,7 @@ > /* > * TI DaVinci Power and Sleep Controller (PSC) > * > - * Copyright (C) 2006 Texas Instruments. > + * Copyright (C) 2006-2012 Texas Instruments. > * > * This program is free software; you can redistribute it and/or modify > * it under the terms of the GNU General Public License as published by > @@ -77,6 +77,8 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, > mdctl |= next_state; > if (flags & PSC_FORCE) > mdctl |= MDCTL_FORCE; > + if ((flags & PSC_LRST) && !enable) > + mdctl &= ~MDCTL_LRST; There is a check for enable flag just above this piece of code. Can you please merge this into that if(). > __raw_writel(mdctl, psc_base + MDCTL + 4 * id); > > pdstat = __raw_readl(psc_base + PDSTAT + 4 * domain); > @@ -108,5 +110,10 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, > mdstat = __raw_readl(psc_base + MDSTAT + 4 * id); > } while (!((mdstat & MDSTAT_STATE_MASK) == next_state)); > > + if ((flags & PSC_LRST) && enable) { > + mdctl |= MDCTL_LRST; > + __raw_writel(mdctl, psc_base + MDCTL + 4 * id); Is there a reason to write mdctl again towards the end? This means you are writing it twice. > + } > + > iounmap(psc_base); > } > diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig > index f8d818a..ceccfa1 100644 > --- a/drivers/remoteproc/Kconfig > +++ b/drivers/remoteproc/Kconfig > @@ -3,6 +3,7 @@ menu "Remoteproc drivers (EXPERIMENTAL)" > # REMOTEPROC gets selected by whoever wants it > config REMOTEPROC > tristate > + select FW_LOADER You should select this only for DaVinci and not foce this on all of remoteproc? > depends on EXPERIMENTAL > select FW_CONFIG > > @@ -27,4 +28,22 @@ config OMAP_REMOTEPROC > It's safe to say n here if you're not interested in multimedia > offloading or just want a bare minimum kernel. > > +config DAVINCI_REMOTEPROC > + tristate "DaVinci DA850/OMAPL138 remoteproc support" > + depends on EXPERIMENTAL > + depends on ARCH_DAVINCI_DA850 > + select REMOTEPROC > + select RPMSG > + default y > + help > + Say y here to support DaVinci DA850/OMAPL138 remote processors > + via the remote processor framework. > + > + Usually 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). > + > + It's safe to say n here if you're not interested in multimedia > + offloading or just want a bare minimum kernel. > + > endmenu > diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile > index 5445d9b..10d5a81 100644 > --- a/drivers/remoteproc/Makefile > +++ b/drivers/remoteproc/Makefile > @@ -7,3 +7,4 @@ remoteproc-y := remoteproc_core.o > remoteproc-y += remoteproc_debugfs.o > remoteproc-y += remoteproc_virtio.o > obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o > +obj-$(CONFIG_DAVINCI_REMOTEPROC) += davinci_remoteproc.o > diff --git a/drivers/remoteproc/davinci_remoteproc.c b/drivers/remoteproc/davinci_remoteproc.c > new file mode 100644 > index 0000000..df5a948 > --- /dev/null > +++ b/drivers/remoteproc/davinci_remoteproc.c > @@ -0,0 +1,273 @@ > +/* > + * Remote processor machine-specific module for Davinci > + * > + * Copyright (C) 2011-2012 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. > + */ > + > +#define pr_fmt(fmt) "%s: " fmt, __func__ > + > +#include <linux/kernel.h> > +#include <linux/err.h> > +#include <linux/printk.h> > +#include <linux/bitops.h> > +#include <linux/platform_device.h> > +#include <linux/remoteproc.h> > +#include <linux/clk.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/irq.h> > + > +#include <mach/da8xx.h> > +#include <mach/cputype.h> > +#include <mach/psc.h> > +#include <mach/remoteproc.h> Including mach/ headers in code present in drivers/ is not good. We cannot have a multi-platform build when using this driver. Need to see how to eliminate these. > + > +#include "remoteproc_internal.h" > + > +/* > + * OMAP-L138 Technical References: > + * http://www.ti.com/product/omap-l138 > + */ > + > +/* next state bits in MDCTL15 register (section 9.6.18) */ > +#define NEXT_ENABLED 0x3 > + > +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */ > +#define HOST1CFG 0x44 There are some DaVinci specific accessors for syscfg module, but those cannot be used here because that will mean machine code inclusion in drivers. Since this driver is the owner of this register you should be able to pass address of this register to the driver through the usual memory resource passing mechanism. > + > +#define SYSCFG_CHIPINT0_IRQ 28 Interrupt number should come through resources passed to driver. > + > +#define SYSCFG_CHIPSIG_OFFSET 0x174 > +#define SYSCFG_CHIPSIG_CLR_OFFSET 0x178 > +#define SYSCFG_CHIPINT0 (1 << 0) > +#define SYSCFG_CHIPINT1 (1 << 1) > +#define SYSCFG_CHIPINT2 (1 << 2) > +#define SYSCFG_CHIPINT3 (1 << 3) > + > +/** > + * struct davinci_rproc - davinci remote processor state > + * @rproc: rproc handle > + */ > +struct davinci_rproc { > + struct rproc *rproc; > + struct clk *dsp_clk; > +}; > + > +static void __iomem *syscfg0_base; > +static struct platform_device *remoteprocdev; > +static struct work_struct workqueue; > +static struct irq_data *irq_data; > +static void (*ack_fxn)(struct irq_data *data); > + > +/** > + * handle_event() - inbound virtqueue message workqueue function > + * > + * This funciton is registered with 'workqueue' and is scheduled by the > + * ISR handler. > + */ > +static void handle_event(struct work_struct *work) > +{ > + struct rproc *rproc = platform_get_drvdata(remoteprocdev); > + > + /* Process incoming buffers on our vring */ > + while (IRQ_HANDLED == rproc_vq_interrupt(rproc, 0)) > + ; This probably needs a timeout in case this loops indefinitely. > + > + /* Must allow wakeup of potenitally blocking senders: */ > + rproc_vq_interrupt(rproc, 1); > +} > + > +/** > + * davinci_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 davinci_rproc_callback(int irq, void *p) > +{ > + if (__raw_readl(syscfg0_base + SYSCFG_CHIPSIG_OFFSET) & > + SYSCFG_CHIPINT0) { > + /* > + * Following can fail if work is pending; but it's OK since the > + * work function will loop to process all incoming messages. > + * schedule_work() calls handle_event with pending bit off. > + */ > + (void)schedule_work(&workqueue); How about calling request_threaded_irq() down below instead of doing this. That should simplify things for you? > + > + /* Clear interrupt level source */ > + __raw_writel(SYSCFG_CHIPINT0, > + syscfg0_base + SYSCFG_CHIPSIG_CLR_OFFSET); Do not use __raw* variants since these are not safe on ARMv7 > + > + /* > + * 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. > + */ > + ack_fxn(irq_data); This is strange (you know that too). Even if these interrupts are level, acking the source of the interrupt should cause the interrupt to be not asserted again, right? It seems that this is covering up some other issue in acknowledging the interrupt. > + } > + > + return IRQ_HANDLED; > +} > + > +static int davinci_rproc_start(struct rproc *rproc) > +{ > + struct platform_device *pdev = to_platform_device(rproc->dev); > + struct device *dev = rproc->dev; > + struct da8xx_rproc_pdata *pdata = dev->platform_data; > + struct davinci_rproc *drproc = rproc->priv; > + struct clk *dsp_clk; > + int ret; > + > + /* 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; > + } > + > + irq_data = irq_get_irq_data(SYSCFG_CHIPINT0_IRQ); > + if (IS_ERR_OR_NULL(irq_data)) { > + dev_err(dev, > + "irq_get_irq_data(SYSCFG_CHIPINT0_IRQ) error: %ld\n", > + PTR_ERR(irq_data)); > + > + return PTR_ERR(irq_data); > + } > + ack_fxn = irq_data->chip->irq_ack; > + > + INIT_WORK(&workqueue, handle_event); > + remoteprocdev = pdev; > + > + dsp_clk = clk_get(dev, pdata->clk_name); > + if (IS_ERR_OR_NULL(dsp_clk)) { > + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); > + return PTR_ERR(dsp_clk); > + } > + > + ret = request_irq(SYSCFG_CHIPINT0_IRQ, davinci_rproc_callback, > + IRQF_SHARED, "davinci-remoteproc", drproc); Why is this a shared IRQ? > + if (ret) { > + dev_err(dev, "request_irq error: %d\n", ret); > + goto fail; > + } > + > + syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); > + __raw_writel(rproc->bootaddr, syscfg0_base + HOST1CFG); > + > + clk_enable(dsp_clk); > + drproc->dsp_clk = dsp_clk; > + > + return 0; > +fail: > + clk_put(dsp_clk); > + > + return ret; > +} > + > +static int davinci_rproc_stop(struct rproc *rproc) > +{ > + struct davinci_rproc *drproc = rproc->priv; > + struct clk *dsp_clk = drproc->dsp_clk; > + > + clk_disable(dsp_clk); > + clk_put(dsp_clk); > + > + iounmap(syscfg0_base); > + > + free_irq(SYSCFG_CHIPINT0_IRQ, drproc); > + > + /* Flush any pending work: */ > + (void)flush_work_sync(&workqueue); > + > + return 0; > +} > + > +/* kick a virtqueue */ > +static void davinci_rproc_kick(struct rproc *rproc, int vqid) > +{ > + /* Poll for ack from other side first */ > + while (__raw_readl(syscfg0_base + SYSCFG_CHIPSIG_OFFSET) & > + SYSCFG_CHIPINT2) > + ; Tight loops like these should use a timeout using time_after(). How long is the expected polling time? If it is large (>= 10ms), it better to yield. > + > + /* Interupt remote proc */ > + __raw_writel(SYSCFG_CHIPINT2, syscfg0_base + SYSCFG_CHIPSIG_OFFSET); > +} > + > +static struct rproc_ops davinci_rproc_ops = { > + .start = davinci_rproc_start, > + .stop = davinci_rproc_stop, > + .kick = davinci_rproc_kick, > +}; > + > +static int davinci_rproc_probe(struct platform_device *pdev) > +{ > + struct davinci_soc_info *soc_info = &davinci_soc_info; > + struct da8xx_rproc_pdata *pdata = pdev->dev.platform_data; > + struct davinci_rproc *drproc; > + struct rproc *rproc; > + void __iomem *psc_base; > + int ret; > + > + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); > + if (ret) { > + dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret); > + return ret; > + } Why set this from driver. This can be done when platform device is initialized in the platform code. > + > + rproc = rproc_alloc(&pdev->dev, pdata->name, &davinci_rproc_ops, > + pdata->firmware, sizeof(*drproc)); > + if (!rproc) > + return -ENOMEM; > + > + drproc = rproc->priv; > + drproc->rproc = rproc; > + > + platform_set_drvdata(pdev, rproc); > + > + ret = rproc_register(rproc); > + if (ret) > + goto free_rproc; > + > + /* insure the dsp is halted by asserting local reset (LRST=0) */ > + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); > + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM); > + iounmap(psc_base); This is not safe since platfom code handles the same registers. Why do you need to do this here? I see that you have added code to handle local reset to psc.c in this patch, that's not good enough? > + > + return 0; > + > +free_rproc: > + rproc_free(rproc); > + return ret; > +} > + > +static int __devexit davinci_rproc_remove(struct platform_device *pdev) > +{ > + struct rproc *rproc = platform_get_drvdata(pdev); > + > + return rproc_unregister(rproc); > +} > + > +static struct platform_driver davinci_rproc_driver = { > + .probe = davinci_rproc_probe, > + .remove = __devexit_p(davinci_rproc_remove), > + .driver = { > + .name = "davinci-rproc", > + .owner = THIS_MODULE, > + }, > +}; OMAP-L138 support suspend-to-RAM. I guess this driver would need power management support too? Or that will come later? > + > +module_platform_driver(davinci_rproc_driver); > + > +MODULE_LICENSE("GPL v2"); > +MODULE_DESCRIPTION("Davinci DA850 Remote Processor control driver"); >
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index 44f6bb0..482d95b 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1,7 +1,7 @@ /* * TI DA850/OMAP-L138 EVM board * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009-2012 Texas Instruments Incorporated - http://www.ti.com/ * * Derived from: arch/arm/mach-davinci/board-da830-evm.c * Original Copyrights follow: @@ -44,6 +44,7 @@ #include <mach/mux.h> #include <mach/aemif.h> #include <mach/spi.h> +#include <mach/remoteproc.h> #define DA850_EVM_PHY_ID "davinci_mdio-0:00" #define DA850_LCD_PWR_PIN GPIO_TO_PIN(2, 8) @@ -1387,6 +1388,10 @@ static __init void da850_evm_init(void) ret); da850_evm_setup_mac_addr(); + + ret = da8xx_register_rproc(); + if (ret) + pr_warn("dsp/rproc registration failed: %d\n", ret); } #ifdef CONFIG_SERIAL_8250_CONSOLE @@ -1414,4 +1419,5 @@ MACHINE_START(DAVINCI_DA850_EVM, "DaVinci DA850/OMAP-L138/AM18x EVM") .init_late = davinci_init_late, .dma_zone_size = SZ_128M, .restart = da8xx_restart, + .reserve = da8xx_rproc_reserve_contig, MACHINE_END diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index e5ce9c4..3c9e7ee 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -3,7 +3,7 @@ * * Initial code: Syed Mohammed Khasim * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com + * Copyright (C) 2009-2012 Texas Instruments Incorporated - http://www.ti.com * * This file is licensed under the terms of the GNU General Public License * version 2. This program is licensed "as is" without any warranty of @@ -20,6 +20,7 @@ #include <mach/cp_intc.h> #include <mach/da8xx.h> #include <mach/mux.h> +#include <mach/remoteproc.h> #define HAWKBOARD_PHY_ID "davinci_mdio-0:07" #define DA850_HAWK_MMCSD_CD_PIN GPIO_TO_PIN(3, 12) @@ -319,6 +320,10 @@ static __init void omapl138_hawk_init(void) pr_warn("omapl138_hawk_init: " "watchdog registration failed: %d\n", ret); + + ret = da8xx_register_rproc(); + if (ret) + pr_warn("dsp/rproc registration failed: %d\n", ret); } #ifdef CONFIG_SERIAL_8250_CONSOLE @@ -346,4 +351,5 @@ MACHINE_START(OMAPL138_HAWKBOARD, "AM18x/OMAP-L138 Hawkboard") .init_late = davinci_init_late, .dma_zone_size = SZ_128M, .restart = da8xx_restart, + .reserve = da8xx_rproc_reserve_contig, MACHINE_END diff --git a/arch/arm/mach-davinci/clock.h b/arch/arm/mach-davinci/clock.h index 46f0f1b..8481638 100644 --- a/arch/arm/mach-davinci/clock.h +++ b/arch/arm/mach-davinci/clock.h @@ -1,7 +1,7 @@ /* * TI DaVinci clock definitions * - * Copyright (C) 2006-2007 Texas Instruments. + * Copyright (C) 2006-2012 Texas Instruments. * Copyright (C) 2008-2009 Deep Root Systems, LLC * * This program is free software; you can redistribute it and/or modify @@ -112,6 +112,7 @@ struct clk { #define PRE_PLL BIT(4) /* source is before PLL mult/div */ #define PSC_SWRSTDISABLE BIT(5) /* Disable state is SwRstDisable */ #define PSC_FORCE BIT(6) /* Force module state transtition */ +#define PSC_LRST BIT(8) /* Use local reset on enable/disable */ #define CLK(dev, con, ck) \ { \ diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index b44dc84..539f5a8 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1,7 +1,7 @@ /* * TI DA850/OMAP-L138 chip specific setup * - * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ + * Copyright (C) 2009-2012 Texas Instruments Incorporated - http://www.ti.com/ * * Derived from: arch/arm/mach-davinci/da830.c * Original Copyrights follow: @@ -76,6 +76,13 @@ static struct clk pll0_aux_clk = { .flags = CLK_PLL | PRE_PLL, }; +static struct clk pll0_sysclk1 = { + .name = "pll0_sysclk1", + .parent = &pll0_clk, + .flags = CLK_PLL, + .div_reg = PLLDIV1, +}; + static struct clk pll0_sysclk2 = { .name = "pll0_sysclk2", .parent = &pll0_clk, @@ -355,10 +362,19 @@ static struct clk sata_clk = { .flags = PSC_FORCE, }; +static struct clk dsp_clk = { + .name = "dsp", + .parent = &pll0_sysclk1, + .domain = DAVINCI_GPSC_DSPDOMAIN, + .lpsc = DA8XX_LPSC0_GEM, + .flags = PSC_LRST, +}; + static struct clk_lookup da850_clks[] = { CLK(NULL, "ref", &ref_clk), CLK(NULL, "pll0", &pll0_clk), CLK(NULL, "pll0_aux", &pll0_aux_clk), + CLK(NULL, "pll0_sysclk1", &pll0_sysclk1), CLK(NULL, "pll0_sysclk2", &pll0_sysclk2), CLK(NULL, "pll0_sysclk3", &pll0_sysclk3), CLK(NULL, "pll0_sysclk4", &pll0_sysclk4), @@ -398,6 +414,7 @@ static struct clk_lookup da850_clks[] = { CLK("spi_davinci.0", NULL, &spi0_clk), CLK("spi_davinci.1", NULL, &spi1_clk), CLK("ahci", NULL, &sata_clk), + CLK(NULL, "dsp", &dsp_clk), CLK(NULL, NULL, NULL), }; diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c index fa5ea17..f2367be 100644 --- a/arch/arm/mach-davinci/devices-da8xx.c +++ b/arch/arm/mach-davinci/devices-da8xx.c @@ -1,6 +1,7 @@ /* * DA8XX/OMAP L1XX platform device data * + * Copyright (C) 2012 Texas Instruments, Inc. * Copyright (c) 2007-2009, MontaVista Software, Inc. <source@mvista.com> * Derived from code that was: * Copyright (C) 2006 Komal Shah <komal_shah802003@yahoo.com> @@ -16,12 +17,14 @@ #include <linux/serial_8250.h> #include <linux/ahci_platform.h> #include <linux/clk.h> +#include <linux/memblock.h> #include <mach/cputype.h> #include <mach/common.h> #include <mach/time.h> #include <mach/da8xx.h> #include <mach/cpuidle.h> +#include <mach/remoteproc.h> #include "clock.h" @@ -660,6 +663,56 @@ int __init da850_register_mmcsd1(struct davinci_mmc_config *config) } #endif +/* + * The following address range was chosen because the XDC Platform for + * OMAP-L138 has this range as its default code/data placement. + * + * System integrators must ensure that Linux does not own this range. + */ +#define DA_CONTIG_BASE (0xc3000000) +#define DA_CONTIG_SIZE (0x02000000) + +void __init da8xx_rproc_reserve_contig(void) +{ + pr_info("reserving contig memory\n"); + + if (memblock_is_region_reserved(DA_CONTIG_BASE, DA_CONTIG_SIZE) || + memblock_reserve(DA_CONTIG_BASE, DA_CONTIG_SIZE) < 0) { + pr_err("memory already reserved\n"); + return; + } +} + +static struct platform_device *da8xx_dsp; + +int __init da8xx_register_rproc(void) +{ + struct da8xx_rproc_pdata rproc_pdata = { + .name = "dsp", + .firmware = "da8xx-dsp.xe674", + .clk_name = "dsp", + }; + int ret; + + da8xx_dsp = platform_device_register_data(NULL, "davinci-rproc", 0, + &rproc_pdata, sizeof(rproc_pdata)); + + ret = dma_declare_coherent_memory(&da8xx_dsp->dev, + DA_CONTIG_BASE, DA_CONTIG_BASE, + DA_CONTIG_SIZE, + DMA_MEMORY_MAP | DMA_MEMORY_EXCLUSIVE); + + if (!(ret & DMA_MEMORY_MAP)) { + pr_err("dma_declare_coherent failure\n"); + + platform_device_unregister(da8xx_dsp); + + return -ENOMEM; + } + + return 0; +}; + static struct resource da8xx_rtc_resources[] = { { .start = DA8XX_RTC_BASE, diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h index a2f1f27..12bcc12 100644 --- a/arch/arm/mach-davinci/include/mach/da8xx.h +++ b/arch/arm/mach-davinci/include/mach/da8xx.h @@ -3,6 +3,7 @@ * * Author: Mark A. Greer <mgreer@mvista.com> * + * Copyright (C) 2011-2012 Texas Instruments, Inc. * 2007, 2009-2010 (c) MontaVista Software, Inc. This file is licensed under * the terms of the GNU General Public License version 2. This program * is licensed "as is" without any warranty of any kind, whether express @@ -91,6 +92,7 @@ int da850_register_cpufreq(char *async_clk); int da8xx_register_cpuidle(void); void __iomem * __init da8xx_get_mem_ctlr(void); int da850_register_pm(struct platform_device *pdev); +int da8xx_register_rproc(void); int __init da850_register_sata(unsigned long refclkpn); void da8xx_restart(char mode, const char *cmd); diff --git a/arch/arm/mach-davinci/include/mach/psc.h b/arch/arm/mach-davinci/include/mach/psc.h index 405318e..b119411 100644 --- a/arch/arm/mach-davinci/include/mach/psc.h +++ b/arch/arm/mach-davinci/include/mach/psc.h @@ -1,7 +1,7 @@ /* * DaVinci Power & Sleep Controller (PSC) defines * - * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2006-2012 Texas Instruments. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -245,6 +245,7 @@ #define MDSTAT_STATE_MASK 0x3f #define PDSTAT_STATE_MASK 0x1f +#define MDCTL_LRST BIT(8) #define MDCTL_FORCE BIT(31) #define PDCTL_NEXT BIT(0) #define PDCTL_EPCGOOD BIT(8) diff --git a/arch/arm/mach-davinci/include/mach/remoteproc.h b/arch/arm/mach-davinci/include/mach/remoteproc.h new file mode 100644 index 0000000..0f56688 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/remoteproc.h @@ -0,0 +1,37 @@ +/* + * Remote Processor + * + * Copyright (C) 2011-2012 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _MACH_REMOTEPROC_H +#define _MACH_REMOTEPROC_H + +#include <linux/remoteproc.h> + +/* + * struct da8xx_rproc_pdata - da8xx remoteproc's platform data + * @name: the remoteproc's name + * @clk_name: the remoteproc's clock + * @firmware: name of firmware file to load + * @ops: start/stop rproc handlers + */ +struct da8xx_rproc_pdata { + const char *name; + const char *clk_name; + const char *firmware; + const struct rproc_ops *ops; +}; + +void __init da8xx_rproc_reserve_contig(void); + +#endif /* _MACH_REMOTEPROC_H */ diff --git a/arch/arm/mach-davinci/psc.c b/arch/arm/mach-davinci/psc.c index bddaba9..c6e9d9f 100644 --- a/arch/arm/mach-davinci/psc.c +++ b/arch/arm/mach-davinci/psc.c @@ -1,7 +1,7 @@ /* * TI DaVinci Power and Sleep Controller (PSC) * - * Copyright (C) 2006 Texas Instruments. + * Copyright (C) 2006-2012 Texas Instruments. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -77,6 +77,8 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, mdctl |= next_state; if (flags & PSC_FORCE) mdctl |= MDCTL_FORCE; + if ((flags & PSC_LRST) && !enable) + mdctl &= ~MDCTL_LRST; __raw_writel(mdctl, psc_base + MDCTL + 4 * id); pdstat = __raw_readl(psc_base + PDSTAT + 4 * domain); @@ -108,5 +110,10 @@ void davinci_psc_config(unsigned int domain, unsigned int ctlr, mdstat = __raw_readl(psc_base + MDSTAT + 4 * id); } while (!((mdstat & MDSTAT_STATE_MASK) == next_state)); + if ((flags & PSC_LRST) && enable) { + mdctl |= MDCTL_LRST; + __raw_writel(mdctl, psc_base + MDCTL + 4 * id); + } + iounmap(psc_base); } diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig index f8d818a..ceccfa1 100644 --- a/drivers/remoteproc/Kconfig +++ b/drivers/remoteproc/Kconfig @@ -3,6 +3,7 @@ menu "Remoteproc drivers (EXPERIMENTAL)" # REMOTEPROC gets selected by whoever wants it config REMOTEPROC tristate + select FW_LOADER depends on EXPERIMENTAL select FW_CONFIG @@ -27,4 +28,22 @@ config OMAP_REMOTEPROC It's safe to say n here if you're not interested in multimedia offloading or just want a bare minimum kernel. +config DAVINCI_REMOTEPROC + tristate "DaVinci DA850/OMAPL138 remoteproc support" + depends on EXPERIMENTAL + depends on ARCH_DAVINCI_DA850 + select REMOTEPROC + select RPMSG + default y + help + Say y here to support DaVinci DA850/OMAPL138 remote processors + via the remote processor framework. + + Usually 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). + + It's safe to say n here if you're not interested in multimedia + offloading or just want a bare minimum kernel. + endmenu diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile index 5445d9b..10d5a81 100644 --- a/drivers/remoteproc/Makefile +++ b/drivers/remoteproc/Makefile @@ -7,3 +7,4 @@ remoteproc-y := remoteproc_core.o remoteproc-y += remoteproc_debugfs.o remoteproc-y += remoteproc_virtio.o obj-$(CONFIG_OMAP_REMOTEPROC) += omap_remoteproc.o +obj-$(CONFIG_DAVINCI_REMOTEPROC) += davinci_remoteproc.o diff --git a/drivers/remoteproc/davinci_remoteproc.c b/drivers/remoteproc/davinci_remoteproc.c new file mode 100644 index 0000000..df5a948 --- /dev/null +++ b/drivers/remoteproc/davinci_remoteproc.c @@ -0,0 +1,273 @@ +/* + * Remote processor machine-specific module for Davinci + * + * Copyright (C) 2011-2012 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include <linux/kernel.h> +#include <linux/err.h> +#include <linux/printk.h> +#include <linux/bitops.h> +#include <linux/platform_device.h> +#include <linux/remoteproc.h> +#include <linux/clk.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/irq.h> + +#include <mach/da8xx.h> +#include <mach/cputype.h> +#include <mach/psc.h> +#include <mach/remoteproc.h> + +#include "remoteproc_internal.h" + +/* + * OMAP-L138 Technical References: + * http://www.ti.com/product/omap-l138 + */ + +/* next state bits in MDCTL15 register (section 9.6.18) */ +#define NEXT_ENABLED 0x3 + +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */ +#define HOST1CFG 0x44 + +#define SYSCFG_CHIPINT0_IRQ 28 + +#define SYSCFG_CHIPSIG_OFFSET 0x174 +#define SYSCFG_CHIPSIG_CLR_OFFSET 0x178 +#define SYSCFG_CHIPINT0 (1 << 0) +#define SYSCFG_CHIPINT1 (1 << 1) +#define SYSCFG_CHIPINT2 (1 << 2) +#define SYSCFG_CHIPINT3 (1 << 3) + +/** + * struct davinci_rproc - davinci remote processor state + * @rproc: rproc handle + */ +struct davinci_rproc { + struct rproc *rproc; + struct clk *dsp_clk; +}; + +static void __iomem *syscfg0_base; +static struct platform_device *remoteprocdev; +static struct work_struct workqueue; +static struct irq_data *irq_data; +static void (*ack_fxn)(struct irq_data *data); + +/** + * handle_event() - inbound virtqueue message workqueue function + * + * This funciton is registered with 'workqueue' and is scheduled by the + * ISR handler. + */ +static void handle_event(struct work_struct *work) +{ + struct rproc *rproc = platform_get_drvdata(remoteprocdev); + + /* Process incoming buffers on our vring */ + while (IRQ_HANDLED == rproc_vq_interrupt(rproc, 0)) + ; + + /* Must allow wakeup of potenitally blocking senders: */ + rproc_vq_interrupt(rproc, 1); +} + +/** + * davinci_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 davinci_rproc_callback(int irq, void *p) +{ + if (__raw_readl(syscfg0_base + SYSCFG_CHIPSIG_OFFSET) & + SYSCFG_CHIPINT0) { + /* + * Following can fail if work is pending; but it's OK since the + * work function will loop to process all incoming messages. + * schedule_work() calls handle_event with pending bit off. + */ + (void)schedule_work(&workqueue); + + /* Clear interrupt level source */ + __raw_writel(SYSCFG_CHIPINT0, + syscfg0_base + SYSCFG_CHIPSIG_CLR_OFFSET); + + /* + * 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. + */ + ack_fxn(irq_data); + } + + return IRQ_HANDLED; +} + +static int davinci_rproc_start(struct rproc *rproc) +{ + struct platform_device *pdev = to_platform_device(rproc->dev); + struct device *dev = rproc->dev; + struct da8xx_rproc_pdata *pdata = dev->platform_data; + struct davinci_rproc *drproc = rproc->priv; + struct clk *dsp_clk; + int ret; + + /* 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; + } + + irq_data = irq_get_irq_data(SYSCFG_CHIPINT0_IRQ); + if (IS_ERR_OR_NULL(irq_data)) { + dev_err(dev, + "irq_get_irq_data(SYSCFG_CHIPINT0_IRQ) error: %ld\n", + PTR_ERR(irq_data)); + + return PTR_ERR(irq_data); + } + ack_fxn = irq_data->chip->irq_ack; + + INIT_WORK(&workqueue, handle_event); + remoteprocdev = pdev; + + dsp_clk = clk_get(dev, pdata->clk_name); + if (IS_ERR_OR_NULL(dsp_clk)) { + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk)); + return PTR_ERR(dsp_clk); + } + + ret = request_irq(SYSCFG_CHIPINT0_IRQ, davinci_rproc_callback, + IRQF_SHARED, "davinci-remoteproc", drproc); + if (ret) { + dev_err(dev, "request_irq error: %d\n", ret); + goto fail; + } + + syscfg0_base = ioremap(DA8XX_SYSCFG0_BASE, SZ_4K); + __raw_writel(rproc->bootaddr, syscfg0_base + HOST1CFG); + + clk_enable(dsp_clk); + drproc->dsp_clk = dsp_clk; + + return 0; +fail: + clk_put(dsp_clk); + + return ret; +} + +static int davinci_rproc_stop(struct rproc *rproc) +{ + struct davinci_rproc *drproc = rproc->priv; + struct clk *dsp_clk = drproc->dsp_clk; + + clk_disable(dsp_clk); + clk_put(dsp_clk); + + iounmap(syscfg0_base); + + free_irq(SYSCFG_CHIPINT0_IRQ, drproc); + + /* Flush any pending work: */ + (void)flush_work_sync(&workqueue); + + return 0; +} + +/* kick a virtqueue */ +static void davinci_rproc_kick(struct rproc *rproc, int vqid) +{ + /* Poll for ack from other side first */ + while (__raw_readl(syscfg0_base + SYSCFG_CHIPSIG_OFFSET) & + SYSCFG_CHIPINT2) + ; + + /* Interupt remote proc */ + __raw_writel(SYSCFG_CHIPINT2, syscfg0_base + SYSCFG_CHIPSIG_OFFSET); +} + +static struct rproc_ops davinci_rproc_ops = { + .start = davinci_rproc_start, + .stop = davinci_rproc_stop, + .kick = davinci_rproc_kick, +}; + +static int davinci_rproc_probe(struct platform_device *pdev) +{ + struct davinci_soc_info *soc_info = &davinci_soc_info; + struct da8xx_rproc_pdata *pdata = pdev->dev.platform_data; + struct davinci_rproc *drproc; + struct rproc *rproc; + void __iomem *psc_base; + int ret; + + ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32)); + if (ret) { + dev_err(&pdev->dev, "dma_set_coherent_mask: %d\n", ret); + return ret; + } + + rproc = rproc_alloc(&pdev->dev, pdata->name, &davinci_rproc_ops, + pdata->firmware, sizeof(*drproc)); + if (!rproc) + return -ENOMEM; + + drproc = rproc->priv; + drproc->rproc = rproc; + + platform_set_drvdata(pdev, rproc); + + ret = rproc_register(rproc); + if (ret) + goto free_rproc; + + /* insure the dsp is halted by asserting local reset (LRST=0) */ + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K); + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM); + iounmap(psc_base); + + return 0; + +free_rproc: + rproc_free(rproc); + return ret; +} + +static int __devexit davinci_rproc_remove(struct platform_device *pdev) +{ + struct rproc *rproc = platform_get_drvdata(pdev); + + return rproc_unregister(rproc); +} + +static struct platform_driver davinci_rproc_driver = { + .probe = davinci_rproc_probe, + .remove = __devexit_p(davinci_rproc_remove), + .driver = { + .name = "davinci-rproc", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(davinci_rproc_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Davinci DA850 Remote Processor control driver");