* Re: [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Shimoda, Yoshihiro @ 2012-01-27 8:37 UTC (permalink / raw)
To: Guennadi Liakhovetski
Cc: linux-kernel, linux-sh, Vinod Koul, Magnus Damm, linux-mmc,
alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-2-git-send-email-g.liakhovetski@gmx.de>
Hello Guennadi-san,
2012/01/26 23:56, Guennadi Liakhovetski wrote:
> This patch adds a library of functions, helping to implement dmaengine
> drivers for hardware, unable to handle scatter-gather lists natively.
> The first version of this driver only supports memcpy and slave DMA
> operation.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
>
> v2:
>
> 1. switch from using a tasklet to threaded IRQ, which allowed to
I tested this driver on 2 environment, but it could not work correctly.
- renesas_usbhs driver with shdma driver on the mackerel board
- renesas_usbhs driver with experimental SUDMAC driver
In this case, should we modify the renesas_usbhs driver?
For reference, if I changed the threaded IRQ to tasklet,
the 2 environment worked correctly:
==============
diff --git a/drivers/dma/dma-simple.c b/drivers/dma/dma-simple.c
index 49d8f7d..65a5170 100644
--- a/drivers/dma/dma-simple.c
+++ b/drivers/dma/dma-simple.c
@@ -715,16 +715,18 @@ static irqreturn_t chan_irq(int irq, void *dev)
spin_lock(&schan->chan_lock);
- ret = ops->chan_irq(schan, irq) ? IRQ_WAKE_THREAD : IRQ_NONE;
+ ret = ops->chan_irq(schan, irq) ? IRQ_HANDLED : IRQ_NONE;
+ if (ret == IRQ_HANDLED)
+ tasklet_schedule(&schan->tasklet);
spin_unlock(&schan->chan_lock);
return ret;
}
-static irqreturn_t chan_irqt(int irq, void *dev)
+static void chan_irqt(unsigned long dev)
{
- struct dma_simple_chan *schan = dev;
+ struct dma_simple_chan *schan = (struct dma_simple_chan *)dev;
const struct dma_simple_ops *ops =
to_simple_dev(schan->dma_chan.device)->ops;
struct dma_simple_desc *sdesc;
@@ -744,15 +746,13 @@ static irqreturn_t chan_irqt(int irq, void *dev)
spin_unlock_irq(&schan->chan_lock);
simple_chan_ld_cleanup(schan, false);
-
- return IRQ_HANDLED;
}
int dma_simple_request_irq(struct dma_simple_chan *schan, int irq,
unsigned long flags, const char *name)
{
- int ret = request_threaded_irq(irq, chan_irq, chan_irqt,
- flags, name, schan);
+ int ret = request_irq(irq, chan_irq, flags, name, schan);
+ tasklet_init(&schan->tasklet, chan_irqt, (unsigned long)schan);
schan->irq = ret < 0 ? ret : irq;
diff --git a/include/linux/dma-simple.h b/include/linux/dma-simple.h
index 5336674..beb1489 100644
--- a/include/linux/dma-simple.h
+++ b/include/linux/dma-simple.h
@@ -68,6 +68,7 @@ struct dma_simple_chan {
int id; /* Raw id of this channel */
int irq; /* Channel IRQ */
enum dma_simple_pm_state pm_state;
+ struct tasklet_struct tasklet;
};
/**
==============
Best regards,
Yoshihiro Shimoda
^ permalink raw reply related
* Re: [PATCH 2/3] tty: serial: OMAP: block idle while the UART is transferring data in PIO mode
From: Govindraj @ 2012-01-27 7:23 UTC (permalink / raw)
To: Paul Walmsley
Cc: linux-omap, linux-serial, linux-arm-kernel, govindraj.r,
Kevin Hilman, Govindraj.R, Greg Kroah-Hartman, Alan Cox,
Tomi Valkeinen
In-Reply-To: <20120126025036.31613.67819.stgit@dusk>
On Thu, Jan 26, 2012 at 8:20 AM, Paul Walmsley <paul@pwsan.com> wrote:
> Prevent OMAP UARTs from going idle while they are still transferring
> data in PIO mode. This works around an oversight in the OMAP UART
> hardware present in OMAP34xx and earlier: an idle UART won't send a
> wakeup when the TX FIFO threshold is reached. This causes long delays
> during data transmission when the MPU powerdomain enters a low-power
> mode. The MPU interrupt controller is not able to respond to
> interrupts when it's in a low-power state, so the TX buffer is not
> refilled until another wakeup event occurs.
>
> This fix changes the erratum i291 DMA idle workaround. Rather than
> toggling between force-idle and no-idle, it will toggle between
> smart-idle and no-idle. The important part of the workaround is the
> no-idle part, so this shouldn't result in any change in behavior.
>
> This fix should work on all OMAP UARTs. Future patches intended for
> the 3.4 merge window will make this workaround conditional on a
> "feature" flag, and will use the OMAP36xx+ TX event wakeup support.
>
> Thanks to Kevin Hilman <khilman@ti.com> for mentioning the erratum i291
> workaround, which led to the development of this approach.
>
> Signed-off-by: Paul Walmsley <paul@pwsan.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Govindraj.R <govindraj.raja@ti.com>
> Cc: Greg Kroah-Hartman <gregkh@suse.de>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Acked-by: Govindraj.R <govindraj.raja@ti.com>
> ---
> arch/arm/mach-omap2/serial.c | 8 ++++----
> drivers/tty/serial/omap-serial.c | 7 +++++++
> 2 files changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm/mach-omap2/serial.c b/arch/arm/mach-omap2/serial.c
> index 247d894..f590afc 100644
> --- a/arch/arm/mach-omap2/serial.c
> +++ b/arch/arm/mach-omap2/serial.c
> @@ -107,18 +107,18 @@ static void omap_uart_set_noidle(struct platform_device *pdev)
> omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_NO);
> }
>
> -static void omap_uart_set_forceidle(struct platform_device *pdev)
> +static void omap_uart_set_smartidle(struct platform_device *pdev)
> {
> struct omap_device *od = to_omap_device(pdev);
>
> - omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_FORCE);
> + omap_hwmod_set_slave_idlemode(od->hwmods[0], HWMOD_IDLEMODE_SMART);
> }
>
> #else
> static void omap_uart_enable_wakeup(struct platform_device *pdev, bool enable)
> {}
> static void omap_uart_set_noidle(struct platform_device *pdev) {}
> -static void omap_uart_set_forceidle(struct platform_device *pdev) {}
> +static void omap_uart_set_smartidle(struct platform_device *pdev) {}
> #endif /* CONFIG_PM */
>
> #ifdef CONFIG_OMAP_MUX
> @@ -349,7 +349,7 @@ void __init omap_serial_init_port(struct omap_board_data *bdata,
> omap_up.uartclk = OMAP24XX_BASE_BAUD * 16;
> omap_up.flags = UPF_BOOT_AUTOCONF;
> omap_up.get_context_loss_count = omap_pm_get_dev_context_loss_count;
> - omap_up.set_forceidle = omap_uart_set_forceidle;
> + omap_up.set_forceidle = omap_uart_set_smartidle;
> omap_up.set_noidle = omap_uart_set_noidle;
> omap_up.enable_wakeup = omap_uart_enable_wakeup;
> omap_up.dma_rx_buf_size = info->dma_rx_buf_size;
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index c9c9ba2..11fa156 100644
> --- a/drivers/tty/serial/omap-serial.c
> +++ b/drivers/tty/serial/omap-serial.c
> @@ -136,6 +136,7 @@ static void serial_omap_enable_ms(struct uart_port *port)
> static void serial_omap_stop_tx(struct uart_port *port)
> {
> struct uart_omap_port *up = (struct uart_omap_port *)port;
> + struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
>
> if (up->use_dma &&
> up->uart_dma.tx_dma_channel != OMAP_UART_DMA_CH_FREE) {
> @@ -158,6 +159,9 @@ static void serial_omap_stop_tx(struct uart_port *port)
> serial_out(up, UART_IER, up->ier);
> }
>
> + if (!up->use_dma && pdata->set_forceidle)
> + pdata->set_forceidle(up->pdev);
> +
> pm_runtime_mark_last_busy(&up->pdev->dev);
> pm_runtime_put_autosuspend(&up->pdev->dev);
> }
> @@ -286,6 +290,7 @@ static inline void serial_omap_enable_ier_thri(struct uart_omap_port *up)
> static void serial_omap_start_tx(struct uart_port *port)
> {
> struct uart_omap_port *up = (struct uart_omap_port *)port;
> + struct omap_uart_port_info *pdata = up->pdev->dev.platform_data;
> struct circ_buf *xmit;
> unsigned int start;
> int ret = 0;
> @@ -293,6 +298,8 @@ static void serial_omap_start_tx(struct uart_port *port)
> if (!up->use_dma) {
> pm_runtime_get_sync(&up->pdev->dev);
> serial_omap_enable_ier_thri(up);
> + if (pdata->set_noidle)
> + pdata->set_noidle(up->pdev);
> pm_runtime_mark_last_busy(&up->pdev->dev);
> pm_runtime_put_autosuspend(&up->pdev->dev);
> return;
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: [PATCH v2 3/3] tty: serial: omap-serial: wakeup latency constraint is in microseconds, not milliseconds
From: Govindraj @ 2012-01-27 7:19 UTC (permalink / raw)
To: Paul Walmsley
Cc: linux-omap, linux-serial, linux-arm-kernel, Kevin Hilman,
Greg Kroah-Hartman, Govindraj.R, Tomi Valkeinen, Alan Cox
In-Reply-To: <20120126025052.31613.37828.stgit@dusk>
On Thu, Jan 26, 2012 at 8:20 AM, Paul Walmsley <paul@pwsan.com> wrote:
> The receive FIFO wakeup latency estimate in the omap-serial driver is
> three orders of magnitude too small. This effectively prevents the
> MPU from going to a low-power state when CONFIG_CPU_IDLE=y. This is a
> major power management regression and masks some other FIFO-related
> bugs in the driver.
>
> Fix by correcting the most egregious problem in the RX wakeup latency
> estimate. There are several other flaws in the estimator; these will
> be fixed by a separate patch series intended for 3.4.
>
> The difference in low-power states with this patch can be observed via
> debugfs in pm_debug/count.
>
> This estimate does not have any effect when CONFIG_CPU_IDLE=n.
>
> Signed-off-by: Paul Walmsley <paul@pwsan.com>
> Cc: Kevin Hilman <khilman@ti.com>
> Cc: Govindraj.R <govindraj.raja@ti.com>
> Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
> Cc: Alan Cox <alan@linux.intel.com>
> Cc: Greg Kroah-Hartman <gregkh@suse.de>
Acked-by: Govindraj.R <govindraj.raja@ti.com>
> ---
> drivers/tty/serial/omap-serial.c | 3 +--
> 1 files changed, 1 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
> index 11fa156..72fa783 100644
> --- a/drivers/tty/serial/omap-serial.c
> +++ b/drivers/tty/serial/omap-serial.c
> @@ -740,8 +740,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
> quot = serial_omap_get_divisor(port, baud);
>
> /* calculate wakeup latency constraint */
> - up->calc_latency = (1000000 * up->port.fifosize) /
> - (1000 * baud / 8);
> + up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8);
> up->latency = up->calc_latency;
> schedule_work(&up->qos_work);
>
>
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-serial" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* Re: 3.3-rc1 console lag (was: Re: [PATCH v8 00/20] OMAP2+: UART: Runtime adaptation + cleanup)
From: Ramirez Luna, Omar @ 2012-01-27 0:14 UTC (permalink / raw)
To: Paul Walmsley
Cc: Kevin Hilman, Tony Lindgren, Rajendra Nayak, Partha Basak,
Govindraj Raja, Santosh Shilimkar, linux-serial,
Vishwanath Sripathy, linux-omap, linux-arm-kernel
In-Reply-To: <alpine.DEB.2.00.1201252000410.11174@utopia.booyaka.com>
Hi Paul,
On Wed, Jan 25, 2012 at 9:00 PM, Paul Walmsley <paul@pwsan.com> wrote:
...
>> > Ensure CONFIG_OMAP_PRM is set while testing irq_chaining with uart.
>> > And for pm_qos usage ensure CONFIG_CPU_IDLE is selected other wise
>> > console might be sluggish.
>>
>> There is console lag for omap2plus_defconfig given that
>> CONFIG_CPU_IDLE is not enabled. Is the intention to force CPU_IDLE
>> into the defconfig or find an alternative for the new pm_qos when cpu
>> idle is disabled.
>>
>> Seen on beagle-xm and 3.3-rc1.
>
> Try this
>
> http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
I tried the series and the console returned to normal, I can confirm
that the following patch helps:
tty: serial: OMAP: use a 1-byte RX FIFO threshold in PIO mode
Thanks,
Omar
^ permalink raw reply
* Re: [alsa-devel] [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Guennadi Liakhovetski @ 2012-01-26 21:07 UTC (permalink / raw)
To: Sascha Hauer
Cc: linux-kernel, alsa-devel, linux-sh, Vinod Koul, Magnus Damm,
Yoshihiro Shimoda, linux-mmc, Paul Mundt, linux-serial
In-Reply-To: <20120126173444.GM5446@pengutronix.de>
Hi Sascha
On Thu, 26 Jan 2012, Sascha Hauer wrote:
> Hi Guennadi,
>
> On Thu, Jan 26, 2012 at 03:56:18PM +0100, Guennadi Liakhovetski wrote:
> > This patch adds a library of functions, helping to implement dmaengine
> > drivers for hardware, unable to handle scatter-gather lists natively.
> > The first version of this driver only supports memcpy and slave DMA
> > operation.
> >
> > Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> > ---
> >
>
> [...]
>
> > +
> > +/*
> > + * For slave DMA we assume, that there is a finite number of DMA slaves in the
> > + * system, and that each such slave can only use a finite number of channels.
> > + * We use slave channel IDs to make sure, that no such slave channel ID is
> > + * allocated more than once.
> > + */
> > +static unsigned int slave_num = 256;
> > +module_param(slave_num, uint, 0444);
> > +
> > +/* A bitmask with slave_num bits */
> > +static unsigned long *simple_slave_used;
>
> You never check that the slave ids passed into this code are
> within the range of slave_num. Given that this is a user changeable
> value this is a bit flawy.
Right, never trust the user;-) I'll fix that.
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply
* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Paul Walmsley @ 2012-01-26 19:34 UTC (permalink / raw)
To: Greg KH
Cc: khilman, govindraj.raja, tomi.valkeinen, linux-serial, linux-omap,
linux-arm-kernel
In-Reply-To: <20120126191604.GA15516@suse.de>
On Thu, 26 Jan 2012, Greg KH wrote:
> Ok, I've just reverted both of these patches for now, please send new
> ones when you have them.
Thanks. A pull request is at the bottom of this message. The patches
are also available from the mailing list archives here:
http://marc.info/?l=linux-arm-kernel&m=132754676014389&w=2
http://marc.info/?l=linux-arm-kernel&m=132754677914395&w=2
http://marc.info/?l=linux-arm-kernel&m=132754676014388&w=2
- Paul
The following changes since commit dcd6c92267155e70a94b3927bce681ce74b80d1f:
Linux 3.3-rc1 (2012-01-19 15:04:48 -0800)
are available in the git repository at:
git://git.pwsan.com/linux-2.6 omap_serial_fixes_3.3rc
Paul Walmsley (3):
tty: serial: OMAP: use a 1-byte RX FIFO threshold in PIO mode
tty: serial: OMAP: block idle while the UART is transferring data in PIO mode
tty: serial: OMAP: wakeup latency constraint is in microseconds, not milliseconds
arch/arm/mach-omap2/serial.c | 8 ++++----
drivers/tty/serial/omap-serial.c | 30 +++++++++++++++++++++++++-----
2 files changed, 29 insertions(+), 9 deletions(-)
^ permalink raw reply
* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Greg KH @ 2012-01-26 19:16 UTC (permalink / raw)
To: Paul Walmsley
Cc: govindraj.raja, khilman, tomi.valkeinen, linux-omap,
linux-arm-kernel, linux-serial
In-Reply-To: <alpine.DEB.2.00.1201252124470.11174@utopia.booyaka.com>
On Wed, Jan 25, 2012 at 09:31:53PM -0700, Paul Walmsley wrote:
> On Wed, 25 Jan 2012, Greg KH wrote:
>
> > On Wed, Jan 25, 2012 at 08:02:09PM -0700, Paul Walmsley wrote:
> > > On Tue, 24 Jan 2012, gregkh@suse.de wrote:
> > > >
> > > > This is a note to let you know that I've just added the patch titled
> > > >
> > > > tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
> > > >
> > > > to my tty git tree which can be found at
> > > > git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> > > > in the tty-linus branch.
> > >
> > > If it's not too late, I was wondering if you could drop this patch and the
> > > subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts
> > > don't wake the"), in favor of the second version of this series that was
> > > just posted at
> > >
> > > http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
> > >
> > > If it is too late, we'll deal with it in 3.4.
> >
> > What is wrong with the patches that I applied?
>
> A new workaround is used that reduces the number of interrupts to normal.
> The commit messages are improved since we have a better idea of what was
> wrong. There is also a new patch (patch 3) for a power management
> regression in the driver.
>
> > How about a fix-up patch on top of what I have applied instead of whole
> > new ones?
>
> That's fine, if that's your preference. It will be several patches,
> though. And about 75% of the previous series would be reverted, since a
> different workaround would be used.
>
> Let me know if that is indeed what you'd like.
Ok, I've just reverted both of these patches for now, please send new
ones when you have them.
thanks,
greg k-h
^ permalink raw reply
* Re: [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Sascha Hauer @ 2012-01-26 17:34 UTC (permalink / raw)
To: Guennadi Liakhovetski
Cc: alsa-devel, linux-sh, Vinod Koul, linux-mmc, Yoshihiro Shimoda,
Magnus Damm, linux-kernel, Paul Mundt, linux-serial
In-Reply-To: <1327589784-4287-2-git-send-email-g.liakhovetski@gmx.de>
Hi Guennadi,
On Thu, Jan 26, 2012 at 03:56:18PM +0100, Guennadi Liakhovetski wrote:
> This patch adds a library of functions, helping to implement dmaengine
> drivers for hardware, unable to handle scatter-gather lists natively.
> The first version of this driver only supports memcpy and slave DMA
> operation.
>
> Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
> ---
>
[...]
> +
> +/*
> + * For slave DMA we assume, that there is a finite number of DMA slaves in the
> + * system, and that each such slave can only use a finite number of channels.
> + * We use slave channel IDs to make sure, that no such slave channel ID is
> + * allocated more than once.
> + */
> +static unsigned int slave_num = 256;
> +module_param(slave_num, uint, 0444);
> +
> +/* A bitmask with slave_num bits */
> +static unsigned long *simple_slave_used;
You never check that the slave ids passed into this code are
within the range of slave_num. Given that this is a user changeable
value this is a bit flawy.
Sascha
--
Pengutronix e.K. | |
Industrial Linux Solutions | http://www.pengutronix.de/ |
Peiner Str. 6-8, 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
^ permalink raw reply
* [PATCH 3/3] serial: Kill off Moorestown code
From: Alan Cox @ 2012-01-26 17:45 UTC (permalink / raw)
To: greg, linux-serial
In-Reply-To: <20120126174121.16858.13827.stgit@bob.linux.org.uk>
From: Alan Cox <alan@linux.intel.com>
All production devices operate in the Oaktrail configuration with legacy PC
elements present and an ACPI BIOS. Continue stripping out the Moorestown
elements from the tree leaving Medfield.
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/tty/serial/Kconfig | 9 -
drivers/tty/serial/Makefile | 1
drivers/tty/serial/max3107-aava.c | 344 -------------------------------------
3 files changed, 0 insertions(+), 354 deletions(-)
delete mode 100644 drivers/tty/serial/max3107-aava.c
diff --git a/drivers/tty/serial/Kconfig b/drivers/tty/serial/Kconfig
index 0bff238..2de9924 100644
--- a/drivers/tty/serial/Kconfig
+++ b/drivers/tty/serial/Kconfig
@@ -264,15 +264,6 @@ config SERIAL_MAX3107
help
MAX3107 chip support
-config SERIAL_MAX3107_AAVA
- tristate "MAX3107 AAVA platform support"
- depends on X86_MRST && SERIAL_MAX3107 && GPIOLIB
- select SERIAL_CORE
- help
- Support for the MAX3107 chip configuration found on the AAVA
- platform. Includes the extra initialisation and GPIO support
- neded for this device.
-
config SERIAL_DZ
bool "DECstation DZ serial driver"
depends on MACH_DECSTATION && 32BIT
diff --git a/drivers/tty/serial/Makefile b/drivers/tty/serial/Makefile
index a6d1ac0..fef32e1 100644
--- a/drivers/tty/serial/Makefile
+++ b/drivers/tty/serial/Makefile
@@ -29,7 +29,6 @@ obj-$(CONFIG_SERIAL_BFIN_SPORT) += bfin_sport_uart.o
obj-$(CONFIG_SERIAL_SAMSUNG) += samsung.o
obj-$(CONFIG_SERIAL_MAX3100) += max3100.o
obj-$(CONFIG_SERIAL_MAX3107) += max3107.o
-obj-$(CONFIG_SERIAL_MAX3107_AAVA) += max3107-aava.o
obj-$(CONFIG_SERIAL_IP22_ZILOG) += ip22zilog.o
obj-$(CONFIG_SERIAL_MUX) += mux.o
obj-$(CONFIG_SERIAL_68328) += 68328serial.o
diff --git a/drivers/tty/serial/max3107-aava.c b/drivers/tty/serial/max3107-aava.c
deleted file mode 100644
index aae772a..0000000
--- a/drivers/tty/serial/max3107-aava.c
+++ /dev/null
@@ -1,344 +0,0 @@
-/*
- * max3107.c - spi uart protocol driver for Maxim 3107
- * Based on max3100.c
- * by Christian Pellegrin <chripell@evolware.org>
- * and max3110.c
- * by Feng Tang <feng.tang@intel.com>
- *
- * Copyright (C) Aavamobile 2009
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * 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 Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- */
-
-#include <linux/delay.h>
-#include <linux/device.h>
-#include <linux/serial_core.h>
-#include <linux/serial.h>
-#include <linux/spi/spi.h>
-#include <linux/freezer.h>
-#include <linux/platform_device.h>
-#include <linux/gpio.h>
-#include <linux/sfi.h>
-#include <linux/module.h>
-#include <asm/mrst.h>
-#include "max3107.h"
-
-/* GPIO direction to input function */
-static int max3107_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
-{
- struct max3107_port *s = container_of(chip, struct max3107_port, chip);
- u16 buf[1]; /* Buffer for SPI transfer */
-
- if (offset >= MAX3107_GPIO_COUNT) {
- dev_err(&s->spi->dev, "Invalid GPIO\n");
- return -EINVAL;
- }
-
- /* Read current GPIO configuration register */
- buf[0] = MAX3107_GPIOCFG_REG;
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
- dev_err(&s->spi->dev, "SPI transfer GPIO read failed\n");
- return -EIO;
- }
- buf[0] &= MAX3107_SPI_RX_DATA_MASK;
-
- /* Set GPIO to input */
- buf[0] &= ~(0x0001 << offset);
-
- /* Write new GPIO configuration register value */
- buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, NULL, 2)) {
- dev_err(&s->spi->dev, "SPI transfer GPIO write failed\n");
- return -EIO;
- }
- return 0;
-}
-
-/* GPIO direction to output function */
-static int max3107_gpio_direction_out(struct gpio_chip *chip, unsigned offset,
- int value)
-{
- struct max3107_port *s = container_of(chip, struct max3107_port, chip);
- u16 buf[2]; /* Buffer for SPI transfers */
-
- if (offset >= MAX3107_GPIO_COUNT) {
- dev_err(&s->spi->dev, "Invalid GPIO\n");
- return -EINVAL;
- }
-
- /* Read current GPIO configuration and data registers */
- buf[0] = MAX3107_GPIOCFG_REG;
- buf[1] = MAX3107_GPIODATA_REG;
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
- dev_err(&s->spi->dev, "SPI transfer gpio failed\n");
- return -EIO;
- }
- buf[0] &= MAX3107_SPI_RX_DATA_MASK;
- buf[1] &= MAX3107_SPI_RX_DATA_MASK;
-
- /* Set GPIO to output */
- buf[0] |= (0x0001 << offset);
- /* Set value */
- if (value)
- buf[1] |= (0x0001 << offset);
- else
- buf[1] &= ~(0x0001 << offset);
-
- /* Write new GPIO configuration and data register values */
- buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIOCFG_REG);
- buf[1] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, NULL, 4)) {
- dev_err(&s->spi->dev,
- "SPI transfer for GPIO conf data w failed\n");
- return -EIO;
- }
- return 0;
-}
-
-/* GPIO value query function */
-static int max3107_gpio_get(struct gpio_chip *chip, unsigned offset)
-{
- struct max3107_port *s = container_of(chip, struct max3107_port, chip);
- u16 buf[1]; /* Buffer for SPI transfer */
-
- if (offset >= MAX3107_GPIO_COUNT) {
- dev_err(&s->spi->dev, "Invalid GPIO\n");
- return -EINVAL;
- }
-
- /* Read current GPIO data register */
- buf[0] = MAX3107_GPIODATA_REG;
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 2)) {
- dev_err(&s->spi->dev, "SPI transfer GPIO data r failed\n");
- return -EIO;
- }
- buf[0] &= MAX3107_SPI_RX_DATA_MASK;
-
- /* Return value */
- return buf[0] & (0x0001 << offset);
-}
-
-/* GPIO value set function */
-static void max3107_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
-{
- struct max3107_port *s = container_of(chip, struct max3107_port, chip);
- u16 buf[2]; /* Buffer for SPI transfers */
-
- if (offset >= MAX3107_GPIO_COUNT) {
- dev_err(&s->spi->dev, "Invalid GPIO\n");
- return;
- }
-
- /* Read current GPIO configuration registers*/
- buf[0] = MAX3107_GPIODATA_REG;
- buf[1] = MAX3107_GPIOCFG_REG;
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, (u8 *)buf, 4)) {
- dev_err(&s->spi->dev,
- "SPI transfer for GPIO data and config read failed\n");
- return;
- }
- buf[0] &= MAX3107_SPI_RX_DATA_MASK;
- buf[1] &= MAX3107_SPI_RX_DATA_MASK;
-
- if (!(buf[1] & (0x0001 << offset))) {
- /* Configured as input, can't set value */
- dev_warn(&s->spi->dev,
- "Trying to set value for input GPIO\n");
- return;
- }
-
- /* Set value */
- if (value)
- buf[0] |= (0x0001 << offset);
- else
- buf[0] &= ~(0x0001 << offset);
-
- /* Write new GPIO data register value */
- buf[0] |= (MAX3107_WRITE_BIT | MAX3107_GPIODATA_REG);
- /* Perform SPI transfer */
- if (max3107_rw(s, (u8 *)buf, NULL, 2))
- dev_err(&s->spi->dev, "SPI transfer GPIO data w failed\n");
-}
-
-/* GPIO chip data */
-static struct gpio_chip max3107_gpio_chip = {
- .owner = THIS_MODULE,
- .direction_input = max3107_gpio_direction_in,
- .direction_output = max3107_gpio_direction_out,
- .get = max3107_gpio_get,
- .set = max3107_gpio_set,
- .can_sleep = 1,
- .base = MAX3107_GPIO_BASE,
- .ngpio = MAX3107_GPIO_COUNT,
-};
-
-/**
- * max3107_aava_reset - reset on AAVA systems
- * @spi: The SPI device we are probing
- *
- * Reset the device ready for probing.
- */
-
-static int max3107_aava_reset(struct spi_device *spi)
-{
- /* Reset the chip */
- if (gpio_request(MAX3107_RESET_GPIO, "max3107")) {
- pr_err("Requesting RESET GPIO failed\n");
- return -EIO;
- }
- if (gpio_direction_output(MAX3107_RESET_GPIO, 0)) {
- pr_err("Setting RESET GPIO to 0 failed\n");
- gpio_free(MAX3107_RESET_GPIO);
- return -EIO;
- }
- msleep(MAX3107_RESET_DELAY);
- if (gpio_direction_output(MAX3107_RESET_GPIO, 1)) {
- pr_err("Setting RESET GPIO to 1 failed\n");
- gpio_free(MAX3107_RESET_GPIO);
- return -EIO;
- }
- gpio_free(MAX3107_RESET_GPIO);
- msleep(MAX3107_WAKEUP_DELAY);
- return 0;
-}
-
-static int max3107_aava_configure(struct max3107_port *s)
-{
- int retval;
-
- /* Initialize GPIO chip data */
- s->chip = max3107_gpio_chip;
- s->chip.label = s->spi->modalias;
- s->chip.dev = &s->spi->dev;
-
- /* Add GPIO chip */
- retval = gpiochip_add(&s->chip);
- if (retval) {
- dev_err(&s->spi->dev, "Adding GPIO chip failed\n");
- return retval;
- }
-
- /* Temporary fix for EV2 boot problems, set modem reset to 0 */
- max3107_gpio_direction_out(&s->chip, 3, 0);
- return 0;
-}
-
-#if 0
-/* This will get enabled once we have the board stuff merged for this
- specific case */
-
-static const struct baud_table brg13_ext[] = {
- { 300, MAX3107_BRG13_B300 },
- { 600, MAX3107_BRG13_B600 },
- { 1200, MAX3107_BRG13_B1200 },
- { 2400, MAX3107_BRG13_B2400 },
- { 4800, MAX3107_BRG13_B4800 },
- { 9600, MAX3107_BRG13_B9600 },
- { 19200, MAX3107_BRG13_B19200 },
- { 57600, MAX3107_BRG13_B57600 },
- { 115200, MAX3107_BRG13_B115200 },
- { 230400, MAX3107_BRG13_B230400 },
- { 460800, MAX3107_BRG13_B460800 },
- { 921600, MAX3107_BRG13_B921600 },
- { 0, 0 }
-};
-
-static void max3107_aava_init(struct max3107_port *s)
-{
- /*override for AAVA SC specific*/
- if (mrst_platform_id() == MRST_PLATFORM_AAVA_SC) {
- if (get_koski_build_id() <= KOSKI_EV2)
- if (s->ext_clk) {
- s->brg_cfg = MAX3107_BRG13_B9600;
- s->baud_tbl = (struct baud_table *)brg13_ext;
- }
- }
-}
-#endif
-
-static int __devexit max3107_aava_remove(struct spi_device *spi)
-{
- struct max3107_port *s = dev_get_drvdata(&spi->dev);
-
- /* Remove GPIO chip */
- if (gpiochip_remove(&s->chip))
- dev_warn(&spi->dev, "Removing GPIO chip failed\n");
-
- /* Then do the default remove */
- return max3107_remove(spi);
-}
-
-/* Platform data */
-static struct max3107_plat aava_plat_data = {
- .loopback = 0,
- .ext_clk = 1,
-/* .init = max3107_aava_init, */
- .configure = max3107_aava_configure,
- .hw_suspend = max3107_hw_susp,
- .polled_mode = 0,
- .poll_time = 0,
-};
-
-
-static int __devinit max3107_probe_aava(struct spi_device *spi)
-{
- int err = max3107_aava_reset(spi);
- if (err < 0)
- return err;
- return max3107_probe(spi, &aava_plat_data);
-}
-
-/* Spi driver data */
-static struct spi_driver max3107_driver = {
- .driver = {
- .name = "aava-max3107",
- .owner = THIS_MODULE,
- },
- .probe = max3107_probe_aava,
- .remove = __devexit_p(max3107_aava_remove),
- .suspend = max3107_suspend,
- .resume = max3107_resume,
-};
-
-/* Driver init function */
-static int __init max3107_init(void)
-{
- return spi_register_driver(&max3107_driver);
-}
-
-/* Driver exit function */
-static void __exit max3107_exit(void)
-{
- spi_unregister_driver(&max3107_driver);
-}
-
-module_init(max3107_init);
-module_exit(max3107_exit);
-
-MODULE_DESCRIPTION("MAX3107 driver");
-MODULE_AUTHOR("Aavamobile");
-MODULE_ALIAS("spi:aava-max3107");
-MODULE_LICENSE("GPL v2");
^ permalink raw reply related
* [PATCH 2/3] serial: Kill off NO_IRQ
From: Alan Cox @ 2012-01-26 17:44 UTC (permalink / raw)
To: greg, linux-serial
In-Reply-To: <20120126174121.16858.13827.stgit@bob.linux.org.uk>
From: Alan Cox <alan@linux.intel.com>
We transform the offenders into a test of irq <= 0 which will be ok while
the ARM people get their platform sorted. Once that is done (or in a while
if they don't do it anyway) then we will change them all to !irq checks.
For arch specific drivers that are already using NO_IRQ = 0 we just test
against zero so we don't need to re-review them later.
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/tty/hvc/hvc_beat.c | 2 +-
drivers/tty/hvc/hvc_rtas.c | 2 +-
drivers/tty/hvc/hvc_udbg.c | 2 +-
drivers/tty/hvc/hvc_xen.c | 2 +-
drivers/tty/hvc/hvcs.c | 4 ++--
drivers/tty/hvc/hvsi.c | 2 +-
drivers/tty/serial/21285.c | 4 ++--
drivers/tty/serial/8250/8250.c | 21 +++++++--------------
drivers/tty/serial/8250/m32r_sio.c | 11 ++---------
drivers/tty/serial/mpc52xx_uart.c | 4 ++--
drivers/tty/serial/mux.c | 2 +-
drivers/tty/serial/pmac_zilog.c | 2 +-
drivers/tty/serial/sunzilog.c | 10 +++++-----
drivers/tty/serial/ucc_uart.c | 2 +-
drivers/tty/serial/vr41xx_siu.c | 4 ++--
15 files changed, 30 insertions(+), 44 deletions(-)
diff --git a/drivers/tty/hvc/hvc_beat.c b/drivers/tty/hvc/hvc_beat.c
index 5fe4631..1560d23 100644
--- a/drivers/tty/hvc/hvc_beat.c
+++ b/drivers/tty/hvc/hvc_beat.c
@@ -113,7 +113,7 @@ static int __init hvc_beat_init(void)
if (!firmware_has_feature(FW_FEATURE_BEAT))
return -ENODEV;
- hp = hvc_alloc(0, NO_IRQ, &hvc_beat_get_put_ops, 16);
+ hp = hvc_alloc(0, 0, &hvc_beat_get_put_ops, 16);
if (IS_ERR(hp))
return PTR_ERR(hp);
hvc_beat_dev = hp;
diff --git a/drivers/tty/hvc/hvc_rtas.c b/drivers/tty/hvc/hvc_rtas.c
index 61c4a61..0069bb8 100644
--- a/drivers/tty/hvc/hvc_rtas.c
+++ b/drivers/tty/hvc/hvc_rtas.c
@@ -94,7 +94,7 @@ static int __init hvc_rtas_init(void)
/* Allocate an hvc_struct for the console device we instantiated
* earlier. Save off hp so that we can return it on exit */
- hp = hvc_alloc(hvc_rtas_cookie, NO_IRQ, &hvc_rtas_get_put_ops, 16);
+ hp = hvc_alloc(hvc_rtas_cookie, 0, &hvc_rtas_get_put_ops, 16);
if (IS_ERR(hp))
return PTR_ERR(hp);
diff --git a/drivers/tty/hvc/hvc_udbg.c b/drivers/tty/hvc/hvc_udbg.c
index b0957e6..4c9b13e 100644
--- a/drivers/tty/hvc/hvc_udbg.c
+++ b/drivers/tty/hvc/hvc_udbg.c
@@ -69,7 +69,7 @@ static int __init hvc_udbg_init(void)
BUG_ON(hvc_udbg_dev);
- hp = hvc_alloc(0, NO_IRQ, &hvc_udbg_ops, 16);
+ hp = hvc_alloc(0, 0, &hvc_udbg_ops, 16);
if (IS_ERR(hp))
return PTR_ERR(hp);
diff --git a/drivers/tty/hvc/hvc_xen.c b/drivers/tty/hvc/hvc_xen.c
index 52fdf60..a1b0a75 100644
--- a/drivers/tty/hvc/hvc_xen.c
+++ b/drivers/tty/hvc/hvc_xen.c
@@ -176,7 +176,7 @@ static int __init xen_hvc_init(void)
xencons_irq = bind_evtchn_to_irq(xen_start_info->console.domU.evtchn);
}
if (xencons_irq < 0)
- xencons_irq = 0; /* NO_IRQ */
+ xencons_irq = 0;
else
irq_set_noprobe(xencons_irq);
diff --git a/drivers/tty/hvc/hvcs.c b/drivers/tty/hvc/hvcs.c
index b9040be..df7e7a0 100644
--- a/drivers/tty/hvc/hvcs.c
+++ b/drivers/tty/hvc/hvcs.c
@@ -1203,7 +1203,7 @@ static void hvcs_close(struct tty_struct *tty, struct file *filp)
{
struct hvcs_struct *hvcsd;
unsigned long flags;
- int irq = NO_IRQ;
+ int irq;
/*
* Is someone trying to close the file associated with this device after
@@ -1264,7 +1264,7 @@ static void hvcs_hangup(struct tty_struct * tty)
struct hvcs_struct *hvcsd = tty->driver_data;
unsigned long flags;
int temp_open_count;
- int irq = NO_IRQ;
+ int irq;
spin_lock_irqsave(&hvcsd->lock, flags);
/* Preserve this so that we know how many kref refs to put */
diff --git a/drivers/tty/hvc/hvsi.c b/drivers/tty/hvc/hvsi.c
index cdfa3e0..1b5f28b 100644
--- a/drivers/tty/hvc/hvsi.c
+++ b/drivers/tty/hvc/hvsi.c
@@ -1237,7 +1237,7 @@ static int __init hvsi_console_init(void)
hp->state = HVSI_CLOSED;
hp->vtermno = *vtermno;
hp->virq = irq_create_mapping(NULL, irq[0]);
- if (hp->virq == NO_IRQ) {
+ if (hp->virq == 0) {
printk(KERN_ERR "%s: couldn't create irq mapping for 0x%x\n",
__func__, irq[0]);
continue;
diff --git a/drivers/tty/serial/21285.c b/drivers/tty/serial/21285.c
index 1b37626..f899996 100644
--- a/drivers/tty/serial/21285.c
+++ b/drivers/tty/serial/21285.c
@@ -331,7 +331,7 @@ static int serial21285_verify_port(struct uart_port *port, struct serial_struct
int ret = 0;
if (ser->type != PORT_UNKNOWN && ser->type != PORT_21285)
ret = -EINVAL;
- if (ser->irq != NO_IRQ)
+ if (ser->irq <= 0)
ret = -EINVAL;
if (ser->baud_base != port->uartclk / 16)
ret = -EINVAL;
@@ -360,7 +360,7 @@ static struct uart_ops serial21285_ops = {
static struct uart_port serial21285_port = {
.mapbase = 0x42000160,
.iotype = UPIO_MEM,
- .irq = NO_IRQ,
+ .irq = 0,
.fifosize = 16,
.ops = &serial21285_ops,
.flags = UPF_BOOT_AUTOCONF,
diff --git a/drivers/tty/serial/8250/8250.c b/drivers/tty/serial/8250/8250.c
index 9f50c4e..b0eb961 100644
--- a/drivers/tty/serial/8250/8250.c
+++ b/drivers/tty/serial/8250/8250.c
@@ -86,13 +86,6 @@ static unsigned int skip_txen_test; /* force skip of txen test at init time */
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
-/*
- * We default to IRQ0 for the "no irq" hack. Some
- * machine types want others as well - they're free
- * to redefine this in their header file.
- */
-#define is_real_interrupt(irq) ((irq) != 0)
-
#ifdef CONFIG_SERIAL_8250_DETECT_IRQ
#define CONFIG_SERIAL_DETECT_IRQ 1
#endif
@@ -1750,7 +1743,7 @@ static void serial8250_backup_timeout(unsigned long data)
* Must disable interrupts or else we risk racing with the interrupt
* based handler.
*/
- if (is_real_interrupt(up->port.irq)) {
+ if (up->port.irq) {
ier = serial_in(up, UART_IER);
serial_out(up, UART_IER, 0);
}
@@ -1775,7 +1768,7 @@ static void serial8250_backup_timeout(unsigned long data)
if (!(iir & UART_IIR_NO_INT))
serial8250_tx_chars(up);
- if (is_real_interrupt(up->port.irq))
+ if (up->port.irq)
serial_out(up, UART_IER, ier);
spin_unlock_irqrestore(&up->port.lock, flags);
@@ -2028,7 +2021,7 @@ static int serial8250_startup(struct uart_port *port)
serial_outp(up, UART_LCR, 0);
}
- if (is_real_interrupt(up->port.irq)) {
+ if (up->port.irq) {
unsigned char iir1;
/*
* Test for UARTs that do not reassert THRE when the
@@ -2083,7 +2076,7 @@ static int serial8250_startup(struct uart_port *port)
* hardware interrupt, we use a timer-based system. The original
* driver used to do this with IRQ0.
*/
- if (!is_real_interrupt(up->port.irq)) {
+ if (!up->port.irq) {
up->timer.data = (unsigned long)up;
mod_timer(&up->timer, jiffies + uart_poll_timeout(port));
} else {
@@ -2099,13 +2092,13 @@ static int serial8250_startup(struct uart_port *port)
spin_lock_irqsave(&up->port.lock, flags);
if (up->port.flags & UPF_FOURPORT) {
- if (!is_real_interrupt(up->port.irq))
+ if (!up->port.irq)
up->port.mctrl |= TIOCM_OUT1;
} else
/*
* Most PC uarts need OUT2 raised to enable interrupts.
*/
- if (is_real_interrupt(up->port.irq))
+ if (up->port.irq)
up->port.mctrl |= TIOCM_OUT2;
serial8250_set_mctrl(&up->port, up->port.mctrl);
@@ -2223,7 +2216,7 @@ static void serial8250_shutdown(struct uart_port *port)
del_timer_sync(&up->timer);
up->timer.function = serial8250_timeout;
- if (is_real_interrupt(up->port.irq))
+ if (up->port.irq)
serial_unlink_irq_chain(up);
}
diff --git a/drivers/tty/serial/8250/m32r_sio.c b/drivers/tty/serial/8250/m32r_sio.c
index 94a6792..e465dda 100644
--- a/drivers/tty/serial/8250/m32r_sio.c
+++ b/drivers/tty/serial/8250/m32r_sio.c
@@ -70,13 +70,6 @@
#define PASS_LIMIT 256
-/*
- * We default to IRQ0 for the "no irq" hack. Some
- * machine types want others as well - they're free
- * to redefine this in their header file.
- */
-#define is_real_interrupt(irq) ((irq) != 0)
-
#define BASE_BAUD 115200
/* Standard COM flags */
@@ -640,7 +633,7 @@ static int m32r_sio_startup(struct uart_port *port)
* hardware interrupt, we use a timer-based system. The original
* driver used to do this with IRQ0.
*/
- if (!is_real_interrupt(up->port.irq)) {
+ if (!up->port.irq) {
unsigned int timeout = up->port.timeout;
timeout = timeout > 6 ? (timeout / 2 - 2) : 1;
@@ -687,7 +680,7 @@ static void m32r_sio_shutdown(struct uart_port *port)
sio_init();
- if (!is_real_interrupt(up->port.irq))
+ if (!up->port.irq)
del_timer_sync(&up->timer);
else
serial_unlink_irq_chain(up);
diff --git a/drivers/tty/serial/mpc52xx_uart.c b/drivers/tty/serial/mpc52xx_uart.c
index 1093a88..e9a770d 100644
--- a/drivers/tty/serial/mpc52xx_uart.c
+++ b/drivers/tty/serial/mpc52xx_uart.c
@@ -507,7 +507,7 @@ static int __init mpc512x_psc_fifoc_init(void)
psc_fifoc_irq = irq_of_parse_and_map(np, 0);
of_node_put(np);
- if (psc_fifoc_irq == NO_IRQ) {
+ if (psc_fifoc_irq == 0) {
pr_err("%s: Can't get FIFOC irq\n", __func__);
iounmap(psc_fifoc);
return -ENODEV;
@@ -1354,7 +1354,7 @@ static int __devinit mpc52xx_uart_of_probe(struct platform_device *op)
}
psc_ops->get_irq(port, op->dev.of_node);
- if (port->irq == NO_IRQ) {
+ if (port->irq == 0) {
dev_dbg(&op->dev, "Could not get irq\n");
return -EINVAL;
}
diff --git a/drivers/tty/serial/mux.c b/drivers/tty/serial/mux.c
index 06f6aef..c61d950 100644
--- a/drivers/tty/serial/mux.c
+++ b/drivers/tty/serial/mux.c
@@ -499,7 +499,7 @@ static int __init mux_probe(struct parisc_device *dev)
port->membase = ioremap_nocache(port->mapbase, MUX_LINE_OFFSET);
port->iotype = UPIO_MEM;
port->type = PORT_MUX;
- port->irq = NO_IRQ;
+ port->irq = 0;
port->uartclk = 0;
port->fifosize = MUX_FIFO_SIZE;
port->ops = &mux_pops;
diff --git a/drivers/tty/serial/pmac_zilog.c b/drivers/tty/serial/pmac_zilog.c
index e9c2dfe4..08ebe90 100644
--- a/drivers/tty/serial/pmac_zilog.c
+++ b/drivers/tty/serial/pmac_zilog.c
@@ -1506,7 +1506,7 @@ no_dma:
* fixed up interrupt info, but we use the device-tree directly
* here due to early probing so we need the fixup too.
*/
- if (uap->port.irq == NO_IRQ &&
+ if (uap->port.irq == 0 &&
np->parent && np->parent->parent &&
of_device_is_compatible(np->parent->parent, "gatwick")) {
/* IRQs on gatwick are offset by 64 */
diff --git a/drivers/tty/serial/sunzilog.c b/drivers/tty/serial/sunzilog.c
index 8e916e7..5a47d1b 100644
--- a/drivers/tty/serial/sunzilog.c
+++ b/drivers/tty/serial/sunzilog.c
@@ -1397,7 +1397,7 @@ static void __devinit sunzilog_init_hw(struct uart_sunzilog_port *up)
#endif
}
-static int zilog_irq = -1;
+static int zilog_irq;
static int __devinit zs_probe(struct platform_device *op)
{
@@ -1425,7 +1425,7 @@ static int __devinit zs_probe(struct platform_device *op)
rp = sunzilog_chip_regs[inst];
- if (zilog_irq == -1)
+ if (!zilog_irq)
zilog_irq = op->archdata.irqs[0];
up = &sunzilog_port_table[inst * 2];
@@ -1580,7 +1580,7 @@ static int __init sunzilog_init(void)
if (err)
goto out_unregister_uart;
- if (zilog_irq != -1) {
+ if (!zilog_irq) {
struct uart_sunzilog_port *up = sunzilog_irq_chain;
err = request_irq(zilog_irq, sunzilog_interrupt, IRQF_SHARED,
"zs", sunzilog_irq_chain);
@@ -1621,7 +1621,7 @@ static void __exit sunzilog_exit(void)
{
platform_driver_unregister(&zs_driver);
- if (zilog_irq != -1) {
+ if (!zilog_irq) {
struct uart_sunzilog_port *up = sunzilog_irq_chain;
/* Disable Interrupts */
@@ -1637,7 +1637,7 @@ static void __exit sunzilog_exit(void)
}
free_irq(zilog_irq, sunzilog_irq_chain);
- zilog_irq = -1;
+ zilog_irq = 0;
}
if (sunzilog_reg.nr) {
diff --git a/drivers/tty/serial/ucc_uart.c b/drivers/tty/serial/ucc_uart.c
index 2ebe606..f99b0c9 100644
--- a/drivers/tty/serial/ucc_uart.c
+++ b/drivers/tty/serial/ucc_uart.c
@@ -1360,7 +1360,7 @@ static int ucc_uart_probe(struct platform_device *ofdev)
}
qe_port->port.irq = irq_of_parse_and_map(np, 0);
- if (qe_port->port.irq == NO_IRQ) {
+ if (qe_port->port.irq == 0) {
dev_err(&ofdev->dev, "could not map IRQ for UCC%u\n",
qe_port->ucc_num + 1);
ret = -EINVAL;
diff --git a/drivers/tty/serial/vr41xx_siu.c b/drivers/tty/serial/vr41xx_siu.c
index 83148e7..cf0d948 100644
--- a/drivers/tty/serial/vr41xx_siu.c
+++ b/drivers/tty/serial/vr41xx_siu.c
@@ -61,7 +61,7 @@
static struct uart_port siu_uart_ports[SIU_PORTS_MAX] = {
[0 ... SIU_PORTS_MAX-1] = {
.lock = __SPIN_LOCK_UNLOCKED(siu_uart_ports->lock),
- .irq = -1,
+ .irq = 0,
},
};
@@ -171,7 +171,7 @@ static inline unsigned int siu_check_type(struct uart_port *port)
{
if (port->line == 0)
return PORT_VR41XX_SIU;
- if (port->line == 1 && port->irq != -1)
+ if (port->line == 1 && port->irq)
return PORT_VR41XX_DSIU;
return PORT_UNKNOWN;
^ permalink raw reply related
* [PATCH 1/3] ftdi_sio: Add more identifiers
From: Alan Cox @ 2012-01-26 17:41 UTC (permalink / raw)
To: greg, linux-serial
From: Alan Cox <alan@linux.intel.com>
0x04d8, 0x000a: Hornby Elite
Signed-off-by: Alan Cox <alan@linux.intel.com>
---
drivers/usb/serial/ftdi_sio.c | 1 +
drivers/usb/serial/ftdi_sio_ids.h | 6 ++++++
2 files changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c
index 104aff5..ad654f8 100644
--- a/drivers/usb/serial/ftdi_sio.c
+++ b/drivers/usb/serial/ftdi_sio.c
@@ -797,6 +797,7 @@ static struct usb_device_id id_table_combined [] = {
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(ADI_VID, ADI_GNICEPLUS_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
+ { USB_DEVICE(HORNBY_VID, HORNBY_ELITE_PID) },
{ USB_DEVICE(JETI_VID, JETI_SPC1201_PID) },
{ USB_DEVICE(MARVELL_VID, MARVELL_SHEEVAPLUG_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
diff --git a/drivers/usb/serial/ftdi_sio_ids.h b/drivers/usb/serial/ftdi_sio_ids.h
index 09237ff..f994503 100644
--- a/drivers/usb/serial/ftdi_sio_ids.h
+++ b/drivers/usb/serial/ftdi_sio_ids.h
@@ -532,6 +532,12 @@
#define ADI_GNICEPLUS_PID 0xF001
/*
+ * Hornby Elite
+ */
+#define HORNBY_VID 0x04D8
+#define HORNBY_ELITE_PID 0x000A
+
+/*
* RATOC REX-USB60F
*/
#define RATOC_VENDOR_ID 0x0584
^ permalink raw reply related
* [PATCH 5/7 v2] serial: sh-sci: prepare for conversion to simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
drivers/tty/serial/sh-sci.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/tty/serial/sh-sci.c b/drivers/tty/serial/sh-sci.c
index 7508579..8b99aac 100644
--- a/drivers/tty/serial/sh-sci.c
+++ b/drivers/tty/serial/sh-sci.c
@@ -1574,9 +1574,9 @@ static bool filter(struct dma_chan *chan, void *slave)
struct sh_dmae_slave *param = slave;
dev_dbg(chan->device->dev, "%s: slave ID %d\n", __func__,
- param->slave_id);
+ param->simple_slave.slave_id);
- chan->private = param;
+ chan->private = ¶m->simple_slave;
return true;
}
@@ -1615,7 +1615,7 @@ static void sci_request_dma(struct uart_port *port)
param = &s->param_tx;
/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_TX */
- param->slave_id = s->cfg->dma_slave_tx;
+ param->simple_slave.slave_id = s->cfg->dma_slave_tx;
s->cookie_tx = -EINVAL;
chan = dma_request_channel(mask, filter, param);
@@ -1643,7 +1643,7 @@ static void sci_request_dma(struct uart_port *port)
param = &s->param_rx;
/* Slave ID, e.g., SHDMA_SLAVE_SCIF0_RX */
- param->slave_id = s->cfg->dma_slave_rx;
+ param->simple_slave.slave_id = s->cfg->dma_slave_rx;
chan = dma_request_channel(mask, filter, param);
dev_dbg(port->dev, "%s: RX: got channel %p\n", __func__, chan);
--
1.7.2.5
^ permalink raw reply related
* [PATCH 7/7 v2] dma: shdma: convert to the simple DMA library
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>
The simple DMA library has originally been extracted from the shdma driver,
which now can be converted to actually use it.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
v2: update IRQ handling to match v2 of the simple DMA library
drivers/dma/Kconfig | 1 +
drivers/dma/shdma.c | 1138 ++++++++++++------------------------------------
drivers/dma/shdma.h | 45 +-
include/linux/sh_dma.h | 39 +--
4 files changed, 308 insertions(+), 915 deletions(-)
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index f7c583e..2696552 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -157,6 +157,7 @@ config SH_DMAE
depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
depends on !SH_DMA_API
select DMA_ENGINE
+ select DMA_SIMPLE
help
Enable support for the Renesas SuperH DMA controllers.
diff --git a/drivers/dma/shdma.c b/drivers/dma/shdma.c
index 812fd76..08011e8 100644
--- a/drivers/dma/shdma.c
+++ b/drivers/dma/shdma.c
@@ -3,6 +3,7 @@
*
* base is drivers/dma/flsdma.c
*
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
* Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
* Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
* Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
@@ -32,18 +33,12 @@
#include <linux/rculist.h>
#include "shdma.h"
-/* DMA descriptor control */
-enum sh_dmae_desc_status {
- DESC_IDLE,
- DESC_PREPARED,
- DESC_SUBMITTED,
- DESC_COMPLETED, /* completed, have to call callback */
- DESC_WAITING, /* callback called, waiting for ack / re-submit */
-};
+#define SH_DMAE_DRV_NAME "sh-dma-engine"
-#define NR_DESCS_PER_CHANNEL 32
/* Default MEMCPY transfer size = 2^2 = 4 bytes */
#define LOG2_DEFAULT_XFER_SIZE 2
+#define SH_DMA_SLAVE_NUMBER 256
+#define SH_DMA_TCR_MAX (16 * 1024 * 1024 - 1)
/*
* Used for write-side mutual exclusion for the global device list,
@@ -52,18 +47,12 @@ enum sh_dmae_desc_status {
static DEFINE_SPINLOCK(sh_dmae_lock);
static LIST_HEAD(sh_dmae_devices);
-/* A bitmask with bits enough for enum sh_dmae_slave_chan_id */
-static unsigned long sh_dmae_slave_used[BITS_TO_LONGS(SH_DMA_SLAVE_NUMBER)];
-
-static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all);
-static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan);
-
static void chclr_write(struct sh_dmae_chan *sh_dc, u32 data)
{
struct sh_dmae_device *shdev = to_sh_dev(sh_dc);
__raw_writel(data, shdev->chan_reg +
- shdev->pdata->channel[sh_dc->id].chclr_offset);
+ shdev->pdata->channel[sh_dc->simple_chan.id].chclr_offset);
}
static void sh_dmae_writel(struct sh_dmae_chan *sh_dc, u32 data, u32 reg)
@@ -153,11 +142,11 @@ static int sh_dmae_rst(struct sh_dmae_device *shdev)
spin_unlock_irqrestore(&sh_dmae_lock, flags);
if (dmaor & (DMAOR_AE | DMAOR_NMIF)) {
- dev_warn(shdev->common.dev, "Can't initialize DMAOR.\n");
+ dev_warn(shdev->simple_dev.dma_dev.dev, "Can't initialize DMAOR.\n");
return -EIO;
}
if (shdev->pdata->dmaor_init & ~dmaor)
- dev_warn(shdev->common.dev,
+ dev_warn(shdev->simple_dev.dma_dev.dev,
"DMAOR=0x%x hasn't latched the initial value 0x%x.\n",
dmaor, shdev->pdata->dmaor_init);
return 0;
@@ -222,15 +211,6 @@ static void dmae_start(struct sh_dmae_chan *sh_chan)
chcr_write(sh_chan, chcr & ~CHCR_TE);
}
-static void dmae_halt(struct sh_dmae_chan *sh_chan)
-{
- struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
- u32 chcr = chcr_read(sh_chan);
-
- chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit);
- chcr_write(sh_chan, chcr);
-}
-
static void dmae_init(struct sh_dmae_chan *sh_chan)
{
/*
@@ -259,7 +239,7 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
{
struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
struct sh_dmae_pdata *pdata = shdev->pdata;
- const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->id];
+ const struct sh_dmae_channel *chan_pdata = &pdata->channel[sh_chan->simple_chan.id];
u16 __iomem *addr = shdev->dmars;
unsigned int shift = chan_pdata->dmars_bit;
@@ -280,720 +260,142 @@ static int dmae_set_dmars(struct sh_dmae_chan *sh_chan, u16 val)
return 0;
}
-static dma_cookie_t sh_dmae_tx_submit(struct dma_async_tx_descriptor *tx)
+static void sh_dmae_start_xfer(struct dma_simple_chan *schan,
+ struct dma_simple_desc *sdesc)
{
- struct sh_desc *desc = tx_to_sh_desc(tx), *chunk, *last = desc, *c;
- struct sh_dmae_chan *sh_chan = to_sh_chan(tx->chan);
- struct sh_dmae_slave *param = tx->chan->private;
- dma_async_tx_callback callback = tx->callback;
- dma_cookie_t cookie;
- bool power_up;
-
- spin_lock_irq(&sh_chan->desc_lock);
-
- if (list_empty(&sh_chan->ld_queue))
- power_up = true;
- else
- power_up = false;
-
- cookie = sh_chan->common.cookie;
- cookie++;
- if (cookie < 0)
- cookie = 1;
-
- sh_chan->common.cookie = cookie;
- tx->cookie = cookie;
-
- /* Mark all chunks of this descriptor as submitted, move to the queue */
- list_for_each_entry_safe(chunk, c, desc->node.prev, node) {
- /*
- * All chunks are on the global ld_free, so, we have to find
- * the end of the chain ourselves
- */
- if (chunk != desc && (chunk->mark == DESC_IDLE ||
- chunk->async_tx.cookie > 0 ||
- chunk->async_tx.cookie == -EBUSY ||
- &chunk->node == &sh_chan->ld_free))
- break;
- chunk->mark = DESC_SUBMITTED;
- /* Callback goes to the last chunk */
- chunk->async_tx.callback = NULL;
- chunk->cookie = cookie;
- list_move_tail(&chunk->node, &sh_chan->ld_queue);
- last = chunk;
- }
-
- last->async_tx.callback = callback;
- last->async_tx.callback_param = tx->callback_param;
-
- dev_dbg(sh_chan->dev, "submit #%d@%p on %d: %x[%d] -> %x\n",
- tx->cookie, &last->async_tx, sh_chan->id,
- desc->hw.sar, desc->hw.tcr, desc->hw.dar);
-
- if (power_up) {
- sh_chan->pm_state = DMAE_PM_BUSY;
-
- pm_runtime_get(sh_chan->dev);
-
- spin_unlock_irq(&sh_chan->desc_lock);
-
- pm_runtime_barrier(sh_chan->dev);
-
- spin_lock_irq(&sh_chan->desc_lock);
-
- /* Have we been reset, while waiting? */
- if (sh_chan->pm_state != DMAE_PM_ESTABLISHED) {
- dev_dbg(sh_chan->dev, "Bring up channel %d\n",
- sh_chan->id);
- if (param) {
- const struct sh_dmae_slave_config *cfg =
- param->config;
-
- dmae_set_dmars(sh_chan, cfg->mid_rid);
- dmae_set_chcr(sh_chan, cfg->chcr);
- } else {
- dmae_init(sh_chan);
- }
-
- if (sh_chan->pm_state == DMAE_PM_PENDING)
- sh_chan_xfer_ld_queue(sh_chan);
- sh_chan->pm_state = DMAE_PM_ESTABLISHED;
- }
- } else {
- sh_chan->pm_state = DMAE_PM_PENDING;
- }
-
- spin_unlock_irq(&sh_chan->desc_lock);
-
- return cookie;
+ struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+ simple_chan);
+ struct sh_dmae_desc *sh_desc = container_of(sdesc,
+ struct sh_dmae_desc, simple_desc);
+ dev_dbg(sh_chan->simple_chan.dev, "Queue #%d to %d: %u@%x -> %x\n",
+ sdesc->async_tx.cookie, sh_chan->simple_chan.id,
+ sh_desc->hw.tcr, sh_desc->hw.sar, sh_desc->hw.dar);
+ /* Get the ld start address from ld_queue */
+ dmae_set_reg(sh_chan, &sh_desc->hw);
+ dmae_start(sh_chan);
}
-/* Called with desc_lock held */
-static struct sh_desc *sh_dmae_get_desc(struct sh_dmae_chan *sh_chan)
+static bool sh_dmae_channel_busy(struct dma_simple_chan *schan)
{
- struct sh_desc *desc;
-
- list_for_each_entry(desc, &sh_chan->ld_free, node)
- if (desc->mark != DESC_PREPARED) {
- BUG_ON(desc->mark != DESC_IDLE);
- list_del(&desc->node);
- return desc;
- }
-
- return NULL;
+ struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+ simple_chan);
+ return dmae_is_busy(sh_chan);
}
-static const struct sh_dmae_slave_config *sh_dmae_find_slave(
- struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *param)
+static void sh_dmae_setup_xfer(struct dma_simple_chan *schan,
+ struct dma_simple_slave *sslave)
{
- struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
- struct sh_dmae_pdata *pdata = shdev->pdata;
- int i;
+ struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+ simple_chan);
- if (param->slave_id >= SH_DMA_SLAVE_NUMBER)
- return NULL;
-
- for (i = 0; i < pdata->slave_num; i++)
- if (pdata->slave[i].slave_id == param->slave_id)
- return pdata->slave + i;
-
- return NULL;
-}
+ if (sslave) {
+ struct sh_dmae_slave *slave = container_of(sslave,
+ struct sh_dmae_slave, simple_slave);
+ const struct sh_dmae_slave_config *cfg =
+ slave->config;
-static int sh_dmae_alloc_chan_resources(struct dma_chan *chan)
-{
- struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
- struct sh_desc *desc;
- struct sh_dmae_slave *param = chan->private;
- int ret;
-
- /*
- * This relies on the guarantee from dmaengine that alloc_chan_resources
- * never runs concurrently with itself or free_chan_resources.
- */
- if (param) {
- const struct sh_dmae_slave_config *cfg;
-
- cfg = sh_dmae_find_slave(sh_chan, param);
- if (!cfg) {
- ret = -EINVAL;
- goto efindslave;
- }
-
- if (test_and_set_bit(param->slave_id, sh_dmae_slave_used)) {
- ret = -EBUSY;
- goto etestused;
- }
-
- param->config = cfg;
- }
-
- while (sh_chan->descs_allocated < NR_DESCS_PER_CHANNEL) {
- desc = kzalloc(sizeof(struct sh_desc), GFP_KERNEL);
- if (!desc)
- break;
- dma_async_tx_descriptor_init(&desc->async_tx,
- &sh_chan->common);
- desc->async_tx.tx_submit = sh_dmae_tx_submit;
- desc->mark = DESC_IDLE;
-
- list_add(&desc->node, &sh_chan->ld_free);
- sh_chan->descs_allocated++;
- }
-
- if (!sh_chan->descs_allocated) {
- ret = -ENOMEM;
- goto edescalloc;
- }
-
- return sh_chan->descs_allocated;
-
-edescalloc:
- if (param)
- clear_bit(param->slave_id, sh_dmae_slave_used);
-etestused:
-efindslave:
- chan->private = NULL;
- return ret;
-}
-
-/*
- * sh_dma_free_chan_resources - Free all resources of the channel.
- */
-static void sh_dmae_free_chan_resources(struct dma_chan *chan)
-{
- struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
- struct sh_desc *desc, *_desc;
- LIST_HEAD(list);
-
- /* Protect against ISR */
- spin_lock_irq(&sh_chan->desc_lock);
- dmae_halt(sh_chan);
- spin_unlock_irq(&sh_chan->desc_lock);
-
- /* Now no new interrupts will occur */
-
- /* Prepared and not submitted descriptors can still be on the queue */
- if (!list_empty(&sh_chan->ld_queue))
- sh_dmae_chan_ld_cleanup(sh_chan, true);
-
- if (chan->private) {
- /* The caller is holding dma_list_mutex */
- struct sh_dmae_slave *param = chan->private;
- clear_bit(param->slave_id, sh_dmae_slave_used);
- chan->private = NULL;
- }
-
- spin_lock_irq(&sh_chan->desc_lock);
-
- list_splice_init(&sh_chan->ld_free, &list);
- sh_chan->descs_allocated = 0;
-
- spin_unlock_irq(&sh_chan->desc_lock);
-
- list_for_each_entry_safe(desc, _desc, &list, node)
- kfree(desc);
-}
-
-/**
- * sh_dmae_add_desc - get, set up and return one transfer descriptor
- * @sh_chan: DMA channel
- * @flags: DMA transfer flags
- * @dest: destination DMA address, incremented when direction equals
- * DMA_DEV_TO_MEM
- * @src: source DMA address, incremented when direction equals
- * DMA_MEM_TO_DEV
- * @len: DMA transfer length
- * @first: if NULL, set to the current descriptor and cookie set to -EBUSY
- * @direction: needed for slave DMA to decide which address to keep constant,
- * equals DMA_MEM_TO_MEM for MEMCPY
- * Returns 0 or an error
- * Locks: called with desc_lock held
- */
-static struct sh_desc *sh_dmae_add_desc(struct sh_dmae_chan *sh_chan,
- unsigned long flags, dma_addr_t *dest, dma_addr_t *src, size_t *len,
- struct sh_desc **first, enum dma_transfer_direction direction)
-{
- struct sh_desc *new;
- size_t copy_size;
-
- if (!*len)
- return NULL;
-
- /* Allocate the link descriptor from the free list */
- new = sh_dmae_get_desc(sh_chan);
- if (!new) {
- dev_err(sh_chan->dev, "No free link descriptor available\n");
- return NULL;
- }
-
- copy_size = min(*len, (size_t)SH_DMA_TCR_MAX + 1);
-
- new->hw.sar = *src;
- new->hw.dar = *dest;
- new->hw.tcr = copy_size;
-
- if (!*first) {
- /* First desc */
- new->async_tx.cookie = -EBUSY;
- *first = new;
+ dmae_set_dmars(sh_chan, cfg->mid_rid);
+ dmae_set_chcr(sh_chan, cfg->chcr);
} else {
- /* Other desc - invisible to the user */
- new->async_tx.cookie = -EINVAL;
+ dmae_init(sh_chan);
}
-
- dev_dbg(sh_chan->dev,
- "chaining (%u/%u)@%x -> %x with %p, cookie %d, shift %d\n",
- copy_size, *len, *src, *dest, &new->async_tx,
- new->async_tx.cookie, sh_chan->xmit_shift);
-
- new->mark = DESC_PREPARED;
- new->async_tx.flags = flags;
- new->direction = direction;
-
- *len -= copy_size;
- if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV)
- *src += copy_size;
- if (direction == DMA_MEM_TO_MEM || direction == DMA_DEV_TO_MEM)
- *dest += copy_size;
-
- return new;
}
-/*
- * sh_dmae_prep_sg - prepare transfer descriptors from an SG list
- *
- * Common routine for public (MEMCPY) and slave DMA. The MEMCPY case is also
- * converted to scatter-gather to guarantee consistent locking and a correct
- * list manipulation. For slave DMA direction carries the usual meaning, and,
- * logically, the SG list is RAM and the addr variable contains slave address,
- * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_MEM_TO_MEM
- * and the SG list contains only one element and points at the source buffer.
- */
-static struct dma_async_tx_descriptor *sh_dmae_prep_sg(struct sh_dmae_chan *sh_chan,
- struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr,
- enum dma_transfer_direction direction, unsigned long flags)
+static const struct sh_dmae_slave_config *dmae_find_slave(
+ struct sh_dmae_chan *sh_chan, struct sh_dmae_slave *slave)
{
- struct scatterlist *sg;
- struct sh_desc *first = NULL, *new = NULL /* compiler... */;
- LIST_HEAD(tx_list);
- int chunks = 0;
- unsigned long irq_flags;
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+ struct sh_dmae_pdata *pdata = shdev->pdata;
+ const struct sh_dmae_slave_config *cfg;
int i;
- if (!sg_len)
+ if (slave->simple_slave.slave_id >= SH_DMA_SLAVE_NUMBER)
return NULL;
- for_each_sg(sgl, sg, sg_len, i)
- chunks += (sg_dma_len(sg) + SH_DMA_TCR_MAX) /
- (SH_DMA_TCR_MAX + 1);
-
- /* Have to lock the whole loop to protect against concurrent release */
- spin_lock_irqsave(&sh_chan->desc_lock, irq_flags);
-
- /*
- * Chaining:
- * first descriptor is what user is dealing with in all API calls, its
- * cookie is at first set to -EBUSY, at tx-submit to a positive
- * number
- * if more than one chunk is needed further chunks have cookie = -EINVAL
- * the last chunk, if not equal to the first, has cookie = -ENOSPC
- * all chunks are linked onto the tx_list head with their .node heads
- * only during this function, then they are immediately spliced
- * back onto the free list in form of a chain
- */
- for_each_sg(sgl, sg, sg_len, i) {
- dma_addr_t sg_addr = sg_dma_address(sg);
- size_t len = sg_dma_len(sg);
-
- if (!len)
- goto err_get_desc;
-
- do {
- dev_dbg(sh_chan->dev, "Add SG #%d@%p[%d], dma %llx\n",
- i, sg, len, (unsigned long long)sg_addr);
-
- if (direction == DMA_DEV_TO_MEM)
- new = sh_dmae_add_desc(sh_chan, flags,
- &sg_addr, addr, &len, &first,
- direction);
- else
- new = sh_dmae_add_desc(sh_chan, flags,
- addr, &sg_addr, &len, &first,
- direction);
- if (!new)
- goto err_get_desc;
-
- new->chunks = chunks--;
- list_add_tail(&new->node, &tx_list);
- } while (len);
- }
-
- if (new != first)
- new->async_tx.cookie = -ENOSPC;
-
- /* Put them back on the free list, so, they don't get lost */
- list_splice_tail(&tx_list, &sh_chan->ld_free);
-
- spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
-
- return &first->async_tx;
-
-err_get_desc:
- list_for_each_entry(new, &tx_list, node)
- new->mark = DESC_IDLE;
- list_splice(&tx_list, &sh_chan->ld_free);
-
- spin_unlock_irqrestore(&sh_chan->desc_lock, irq_flags);
+ for (i = 0, cfg = pdata->slave; i < pdata->slave_num; i++, cfg++)
+ if (cfg->slave_id == slave->simple_slave.slave_id)
+ return cfg;
return NULL;
}
-static struct dma_async_tx_descriptor *sh_dmae_prep_memcpy(
- struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
- size_t len, unsigned long flags)
-{
- struct sh_dmae_chan *sh_chan;
- struct scatterlist sg;
-
- if (!chan || !len)
- return NULL;
-
- sh_chan = to_sh_chan(chan);
-
- sg_init_table(&sg, 1);
- sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_src)), len,
- offset_in_page(dma_src));
- sg_dma_address(&sg) = dma_src;
- sg_dma_len(&sg) = len;
-
- return sh_dmae_prep_sg(sh_chan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM,
- flags);
-}
-
-static struct dma_async_tx_descriptor *sh_dmae_prep_slave_sg(
- struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
- enum dma_transfer_direction direction, unsigned long flags)
+static int sh_dmae_set_slave(struct dma_simple_chan *schan,
+ struct dma_simple_slave *sslave)
{
- struct sh_dmae_slave *param;
- struct sh_dmae_chan *sh_chan;
- dma_addr_t slave_addr;
-
- if (!chan)
- return NULL;
-
- sh_chan = to_sh_chan(chan);
- param = chan->private;
-
- /* Someone calling slave DMA on a public channel? */
- if (!param || !sg_len) {
- dev_warn(sh_chan->dev, "%s: bad parameter: %p, %d, %d\n",
- __func__, param, sg_len, param ? param->slave_id : -1);
- return NULL;
- }
-
- slave_addr = param->config->addr;
-
- /*
- * if (param != NULL), this is a successfully requested slave channel,
- * therefore param->config != NULL too.
- */
- return sh_dmae_prep_sg(sh_chan, sgl, sg_len, &slave_addr,
- direction, flags);
-}
-
-static int sh_dmae_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
- unsigned long arg)
-{
- struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
- unsigned long flags;
-
- /* Only supports DMA_TERMINATE_ALL */
- if (cmd != DMA_TERMINATE_ALL)
- return -ENXIO;
-
- if (!chan)
- return -EINVAL;
-
- spin_lock_irqsave(&sh_chan->desc_lock, flags);
- dmae_halt(sh_chan);
-
- if (!list_empty(&sh_chan->ld_queue)) {
- /* Record partial transfer */
- struct sh_desc *desc = list_entry(sh_chan->ld_queue.next,
- struct sh_desc, node);
- desc->partial = (desc->hw.tcr - sh_dmae_readl(sh_chan, TCR)) <<
- sh_chan->xmit_shift;
- }
- spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
+ struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+ simple_chan);
+ struct sh_dmae_slave *slave = container_of(sslave, struct sh_dmae_slave,
+ simple_slave);
+ const struct sh_dmae_slave_config *cfg = dmae_find_slave(sh_chan, slave);
+ if (!cfg)
+ return -ENODEV;
- sh_dmae_chan_ld_cleanup(sh_chan, true);
+ slave->config = cfg;
return 0;
}
-static dma_async_tx_callback __ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
+static void dmae_halt(struct sh_dmae_chan *sh_chan)
{
- struct sh_desc *desc, *_desc;
- /* Is the "exposed" head of a chain acked? */
- bool head_acked = false;
- dma_cookie_t cookie = 0;
- dma_async_tx_callback callback = NULL;
- void *param = NULL;
- unsigned long flags;
-
- spin_lock_irqsave(&sh_chan->desc_lock, flags);
- list_for_each_entry_safe(desc, _desc, &sh_chan->ld_queue, node) {
- struct dma_async_tx_descriptor *tx = &desc->async_tx;
-
- BUG_ON(tx->cookie > 0 && tx->cookie != desc->cookie);
- BUG_ON(desc->mark != DESC_SUBMITTED &&
- desc->mark != DESC_COMPLETED &&
- desc->mark != DESC_WAITING);
-
- /*
- * queue is ordered, and we use this loop to (1) clean up all
- * completed descriptors, and to (2) update descriptor flags of
- * any chunks in a (partially) completed chain
- */
- if (!all && desc->mark == DESC_SUBMITTED &&
- desc->cookie != cookie)
- break;
-
- if (tx->cookie > 0)
- cookie = tx->cookie;
-
- if (desc->mark == DESC_COMPLETED && desc->chunks == 1) {
- if (sh_chan->completed_cookie != desc->cookie - 1)
- dev_dbg(sh_chan->dev,
- "Completing cookie %d, expected %d\n",
- desc->cookie,
- sh_chan->completed_cookie + 1);
- sh_chan->completed_cookie = desc->cookie;
- }
-
- /* Call callback on the last chunk */
- if (desc->mark == DESC_COMPLETED && tx->callback) {
- desc->mark = DESC_WAITING;
- callback = tx->callback;
- param = tx->callback_param;
- dev_dbg(sh_chan->dev, "descriptor #%d@%p on %d callback\n",
- tx->cookie, tx, sh_chan->id);
- BUG_ON(desc->chunks != 1);
- break;
- }
-
- if (tx->cookie > 0 || tx->cookie == -EBUSY) {
- if (desc->mark == DESC_COMPLETED) {
- BUG_ON(tx->cookie < 0);
- desc->mark = DESC_WAITING;
- }
- head_acked = async_tx_test_ack(tx);
- } else {
- switch (desc->mark) {
- case DESC_COMPLETED:
- desc->mark = DESC_WAITING;
- /* Fall through */
- case DESC_WAITING:
- if (head_acked)
- async_tx_ack(&desc->async_tx);
- }
- }
-
- dev_dbg(sh_chan->dev, "descriptor %p #%d completed.\n",
- tx, tx->cookie);
-
- if (((desc->mark == DESC_COMPLETED ||
- desc->mark == DESC_WAITING) &&
- async_tx_test_ack(&desc->async_tx)) || all) {
- /* Remove from ld_queue list */
- desc->mark = DESC_IDLE;
-
- list_move(&desc->node, &sh_chan->ld_free);
-
- if (list_empty(&sh_chan->ld_queue)) {
- dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
- pm_runtime_put(sh_chan->dev);
- }
- }
- }
-
- if (all && !callback)
- /*
- * Terminating and the loop completed normally: forgive
- * uncompleted cookies
- */
- sh_chan->completed_cookie = sh_chan->common.cookie;
-
- spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
-
- if (callback)
- callback(param);
+ struct sh_dmae_device *shdev = to_sh_dev(sh_chan);
+ u32 chcr = chcr_read(sh_chan);
- return callback;
+ chcr &= ~(CHCR_DE | CHCR_TE | shdev->chcr_ie_bit);
+ chcr_write(sh_chan, chcr);
}
-/*
- * sh_chan_ld_cleanup - Clean up link descriptors
- *
- * This function cleans up the ld_queue of DMA channel.
- */
-static void sh_dmae_chan_ld_cleanup(struct sh_dmae_chan *sh_chan, bool all)
+static int sh_dmae_desc_setup(struct dma_simple_chan *schan,
+ struct dma_simple_desc *sdesc,
+ dma_addr_t src, dma_addr_t dst, size_t *len)
{
- while (__ld_cleanup(sh_chan, all))
- ;
-}
+ struct sh_dmae_desc *sh_desc = container_of(sdesc,
+ struct sh_dmae_desc, simple_desc);
-/* Called under spin_lock_irq(&sh_chan->desc_lock) */
-static void sh_chan_xfer_ld_queue(struct sh_dmae_chan *sh_chan)
-{
- struct sh_desc *desc;
+ if (*len > schan->max_xfer_len)
+ *len = schan->max_xfer_len;
- /* DMA work check */
- if (dmae_is_busy(sh_chan))
- return;
-
- /* Find the first not transferred descriptor */
- list_for_each_entry(desc, &sh_chan->ld_queue, node)
- if (desc->mark == DESC_SUBMITTED) {
- dev_dbg(sh_chan->dev, "Queue #%d to %d: %u@%x -> %x\n",
- desc->async_tx.cookie, sh_chan->id,
- desc->hw.tcr, desc->hw.sar, desc->hw.dar);
- /* Get the ld start address from ld_queue */
- dmae_set_reg(sh_chan, &desc->hw);
- dmae_start(sh_chan);
- break;
- }
-}
+ sh_desc->hw.sar = src;
+ sh_desc->hw.dar = dst;
+ sh_desc->hw.tcr = *len;
-static void sh_dmae_memcpy_issue_pending(struct dma_chan *chan)
-{
- struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
-
- spin_lock_irq(&sh_chan->desc_lock);
- if (sh_chan->pm_state == DMAE_PM_ESTABLISHED)
- sh_chan_xfer_ld_queue(sh_chan);
- else
- sh_chan->pm_state = DMAE_PM_PENDING;
- spin_unlock_irq(&sh_chan->desc_lock);
+ return 0;
}
-static enum dma_status sh_dmae_tx_status(struct dma_chan *chan,
- dma_cookie_t cookie,
- struct dma_tx_state *txstate)
+static void sh_dmae_halt(struct dma_simple_chan *schan)
{
- struct sh_dmae_chan *sh_chan = to_sh_chan(chan);
- dma_cookie_t last_used;
- dma_cookie_t last_complete;
- enum dma_status status;
- unsigned long flags;
-
- sh_dmae_chan_ld_cleanup(sh_chan, false);
-
- /* First read completed cookie to avoid a skew */
- last_complete = sh_chan->completed_cookie;
- rmb();
- last_used = chan->cookie;
- BUG_ON(last_complete < 0);
- dma_set_tx_state(txstate, last_complete, last_used, 0);
-
- spin_lock_irqsave(&sh_chan->desc_lock, flags);
-
- status = dma_async_is_complete(cookie, last_complete, last_used);
-
- /*
- * If we don't find cookie on the queue, it has been aborted and we have
- * to report error
- */
- if (status != DMA_SUCCESS) {
- struct sh_desc *desc;
- status = DMA_ERROR;
- list_for_each_entry(desc, &sh_chan->ld_queue, node)
- if (desc->cookie == cookie) {
- status = DMA_IN_PROGRESS;
- break;
- }
- }
-
- spin_unlock_irqrestore(&sh_chan->desc_lock, flags);
-
- return status;
+ struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+ simple_chan);
+ dmae_halt(sh_chan);
}
-static irqreturn_t sh_dmae_interrupt(int irq, void *data)
+static bool sh_dmae_chan_irq(struct dma_simple_chan *schan, int irq)
{
- irqreturn_t ret = IRQ_NONE;
- struct sh_dmae_chan *sh_chan = data;
- u32 chcr;
-
- spin_lock(&sh_chan->desc_lock);
+ struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan,
+ simple_chan);
- chcr = chcr_read(sh_chan);
-
- if (chcr & CHCR_TE) {
- /* DMA stop */
- dmae_halt(sh_chan);
-
- ret = IRQ_HANDLED;
- tasklet_schedule(&sh_chan->tasklet);
- }
+ if (!(chcr_read(sh_chan) & CHCR_TE))
+ return false;
- spin_unlock(&sh_chan->desc_lock);
+ /* DMA stop */
+ dmae_halt(sh_chan);
- return ret;
+ return true;
}
/* Called from error IRQ or NMI */
static bool sh_dmae_reset(struct sh_dmae_device *shdev)
{
- unsigned int handled = 0;
- int i;
+ bool ret;
/* halt the dma controller */
sh_dmae_ctl_stop(shdev);
/* We cannot detect, which channel caused the error, have to reset all */
- for (i = 0; i < SH_DMAC_MAX_CHANNELS; i++) {
- struct sh_dmae_chan *sh_chan = shdev->chan[i];
- struct sh_desc *desc;
- LIST_HEAD(dl);
-
- if (!sh_chan)
- continue;
-
- spin_lock(&sh_chan->desc_lock);
-
- /* Stop the channel */
- dmae_halt(sh_chan);
-
- list_splice_init(&sh_chan->ld_queue, &dl);
-
- if (!list_empty(&dl)) {
- dev_dbg(sh_chan->dev, "Bring down channel %d\n", sh_chan->id);
- pm_runtime_put(sh_chan->dev);
- }
- sh_chan->pm_state = DMAE_PM_ESTABLISHED;
-
- spin_unlock(&sh_chan->desc_lock);
-
- /* Complete all */
- list_for_each_entry(desc, &dl, node) {
- struct dma_async_tx_descriptor *tx = &desc->async_tx;
- desc->mark = DESC_IDLE;
- if (tx->callback)
- tx->callback(tx->callback_param);
- }
-
- spin_lock(&sh_chan->desc_lock);
- list_splice(&dl, &sh_chan->ld_free);
- spin_unlock(&sh_chan->desc_lock);
-
- handled++;
- }
+ ret = dma_simple_reset(&shdev->simple_dev);
sh_dmae_rst(shdev);
- return !!handled;
+ return ret;
}
static irqreturn_t sh_dmae_err(int irq, void *data)
@@ -1003,35 +405,24 @@ static irqreturn_t sh_dmae_err(int irq, void *data)
if (!(dmaor_read(shdev) & DMAOR_AE))
return IRQ_NONE;
- sh_dmae_reset(data);
+ sh_dmae_reset(shdev);
return IRQ_HANDLED;
}
-static void dmae_do_tasklet(unsigned long data)
+static bool sh_dmae_desc_completed(struct dma_simple_chan *schan,
+ struct dma_simple_desc *sdesc)
{
- struct sh_dmae_chan *sh_chan = (struct sh_dmae_chan *)data;
- struct sh_desc *desc;
+ struct sh_dmae_chan *sh_chan = container_of(schan,
+ struct sh_dmae_chan, simple_chan);
+ struct sh_dmae_desc *sh_desc = container_of(sdesc,
+ struct sh_dmae_desc, simple_desc);
u32 sar_buf = sh_dmae_readl(sh_chan, SAR);
u32 dar_buf = sh_dmae_readl(sh_chan, DAR);
- spin_lock_irq(&sh_chan->desc_lock);
- list_for_each_entry(desc, &sh_chan->ld_queue, node) {
- if (desc->mark == DESC_SUBMITTED &&
- ((desc->direction == DMA_DEV_TO_MEM &&
- (desc->hw.dar + desc->hw.tcr) == dar_buf) ||
- (desc->hw.sar + desc->hw.tcr) == sar_buf)) {
- dev_dbg(sh_chan->dev, "done #%d@%p dst %u\n",
- desc->async_tx.cookie, &desc->async_tx,
- desc->hw.dar);
- desc->mark = DESC_COMPLETED;
- break;
- }
- }
- /* Next desc */
- sh_chan_xfer_ld_queue(sh_chan);
- spin_unlock_irq(&sh_chan->desc_lock);
-
- sh_dmae_chan_ld_cleanup(sh_chan, false);
+ return (sdesc->direction == DMA_DEV_TO_MEM &&
+ (sh_desc->hw.dar + sh_desc->hw.tcr) == dar_buf) ||
+ (sdesc->direction != DMA_DEV_TO_MEM &&
+ (sh_desc->hw.sar + sh_desc->hw.tcr) == sar_buf);
}
static bool sh_dmae_nmi_notify(struct sh_dmae_device *shdev)
@@ -1085,96 +476,174 @@ static struct notifier_block sh_dmae_nmi_notifier __read_mostly = {
static int __devinit sh_dmae_chan_probe(struct sh_dmae_device *shdev, int id,
int irq, unsigned long flags)
{
- int err;
const struct sh_dmae_channel *chan_pdata = &shdev->pdata->channel[id];
- struct platform_device *pdev = to_platform_device(shdev->common.dev);
- struct sh_dmae_chan *new_sh_chan;
+ struct dma_simple_dev *sdev = &shdev->simple_dev;
+ struct platform_device *pdev = to_platform_device(sdev->dma_dev.dev);
+ struct sh_dmae_chan *sh_chan;
+ struct dma_simple_chan *schan;
+ int err;
- /* alloc channel */
- new_sh_chan = kzalloc(sizeof(struct sh_dmae_chan), GFP_KERNEL);
- if (!new_sh_chan) {
- dev_err(shdev->common.dev,
+ sh_chan = kzalloc(sizeof(struct sh_dmae_chan), GFP_KERNEL);
+ if (!sh_chan) {
+ dev_err(sdev->dma_dev.dev,
"No free memory for allocating dma channels!\n");
return -ENOMEM;
}
- new_sh_chan->pm_state = DMAE_PM_ESTABLISHED;
+ schan = &sh_chan->simple_chan;
+ schan->max_xfer_len = SH_DMA_TCR_MAX + 1;
- /* reference struct dma_device */
- new_sh_chan->common.device = &shdev->common;
+ dma_simple_chan_probe(sdev, schan, id);
- new_sh_chan->dev = shdev->common.dev;
- new_sh_chan->id = id;
- new_sh_chan->irq = irq;
- new_sh_chan->base = shdev->chan_reg + chan_pdata->offset / sizeof(u32);
-
- /* Init DMA tasklet */
- tasklet_init(&new_sh_chan->tasklet, dmae_do_tasklet,
- (unsigned long)new_sh_chan);
-
- spin_lock_init(&new_sh_chan->desc_lock);
-
- /* Init descripter manage list */
- INIT_LIST_HEAD(&new_sh_chan->ld_queue);
- INIT_LIST_HEAD(&new_sh_chan->ld_free);
-
- /* Add the channel to DMA device channel list */
- list_add_tail(&new_sh_chan->common.device_node,
- &shdev->common.channels);
- shdev->common.chancnt++;
+ sh_chan->base = shdev->chan_reg + chan_pdata->offset / sizeof(u32);
+ /* set up channel irq */
if (pdev->id >= 0)
- snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
- "sh-dmae%d.%d", pdev->id, new_sh_chan->id);
+ snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
+ "sh-dmae%d.%d", pdev->id, id);
else
- snprintf(new_sh_chan->dev_id, sizeof(new_sh_chan->dev_id),
- "sh-dma%d", new_sh_chan->id);
+ snprintf(sh_chan->dev_id, sizeof(sh_chan->dev_id),
+ "sh-dma%d", id);
- /* set up channel irq */
- err = request_irq(irq, &sh_dmae_interrupt, flags,
- new_sh_chan->dev_id, new_sh_chan);
+ err = dma_simple_request_irq(schan, irq, flags, sh_chan->dev_id);
if (err) {
- dev_err(shdev->common.dev, "DMA channel %d request_irq error "
- "with return %d\n", id, err);
+ dev_err(sdev->dma_dev.dev,
+ "DMA channel %d request_irq error %d\n",
+ id, err);
goto err_no_irq;
}
- shdev->chan[id] = new_sh_chan;
+ shdev->chan[id] = sh_chan;
return 0;
err_no_irq:
/* remove from dmaengine device node */
- list_del(&new_sh_chan->common.device_node);
- kfree(new_sh_chan);
+ dma_simple_chan_remove(schan);
+ kfree(sh_chan);
return err;
}
static void sh_dmae_chan_remove(struct sh_dmae_device *shdev)
{
+ struct dma_device *dma_dev = &shdev->simple_dev.dma_dev;
+ struct dma_simple_chan *schan;
int i;
- for (i = shdev->common.chancnt - 1 ; i >= 0 ; i--) {
- if (shdev->chan[i]) {
- struct sh_dmae_chan *sh_chan = shdev->chan[i];
+ dma_simple_for_each_chan(schan, &shdev->simple_dev, i) {
+ struct sh_dmae_chan *sh_chan = container_of(schan,
+ struct sh_dmae_chan, simple_chan);
+ BUG_ON(!schan);
- free_irq(sh_chan->irq, sh_chan);
+ dma_simple_free_irq(&sh_chan->simple_chan);
- list_del(&sh_chan->common.device_node);
- kfree(sh_chan);
- shdev->chan[i] = NULL;
+ dma_simple_chan_remove(schan);
+ kfree(sh_chan);
+ }
+ dma_dev->chancnt = 0;
+}
+
+static void sh_dmae_shutdown(struct platform_device *pdev)
+{
+ struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
+ sh_dmae_ctl_stop(shdev);
+}
+
+static int sh_dmae_runtime_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int sh_dmae_runtime_resume(struct device *dev)
+{
+ struct sh_dmae_device *shdev = dev_get_drvdata(dev);
+
+ return sh_dmae_rst(shdev);
+}
+
+#ifdef CONFIG_PM
+static int sh_dmae_suspend(struct device *dev)
+{
+ return 0;
+}
+
+static int sh_dmae_resume(struct device *dev)
+{
+ struct sh_dmae_device *shdev = dev_get_drvdata(dev);
+ int i, ret;
+
+ ret = sh_dmae_rst(shdev);
+ if (ret < 0)
+ dev_err(dev, "Failed to reset!\n");
+
+ for (i = 0; i < shdev->pdata->channel_num; i++) {
+ struct sh_dmae_chan *sh_chan = shdev->chan[i];
+ struct sh_dmae_slave *param = sh_chan->simple_chan.dma_chan.private;
+
+ if (!sh_chan->simple_chan.desc_num)
+ continue;
+
+ if (param) {
+ const struct sh_dmae_slave_config *cfg = param->config;
+ dmae_set_dmars(sh_chan, cfg->mid_rid);
+ dmae_set_chcr(sh_chan, cfg->chcr);
+ } else {
+ dmae_init(sh_chan);
}
}
- shdev->common.chancnt = 0;
+
+ return 0;
+}
+#else
+#define sh_dmae_suspend NULL
+#define sh_dmae_resume NULL
+#endif
+
+const struct dev_pm_ops sh_dmae_pm = {
+ .suspend = sh_dmae_suspend,
+ .resume = sh_dmae_resume,
+ .runtime_suspend = sh_dmae_runtime_suspend,
+ .runtime_resume = sh_dmae_runtime_resume,
+};
+
+static dma_addr_t sh_dmae_slave_addr(struct dma_simple_chan *schan)
+{
+ struct sh_dmae_slave *param = schan->dma_chan.private;
+
+ /*
+ * Implicit BUG_ON(!param)
+ * if (param != NULL), this is a successfully requested slave channel,
+ * therefore param->config != NULL too.
+ */
+ return param->config->addr;
+}
+
+static struct dma_simple_desc *sh_dmae_embedded_desc(void *buf, int i)
+{
+ return &((struct sh_dmae_desc *)buf)[i].simple_desc;
}
-static int __init sh_dmae_probe(struct platform_device *pdev)
+static const struct dma_simple_ops sh_dmae_simple_ops = {
+ .desc_completed = sh_dmae_desc_completed,
+ .halt_channel = sh_dmae_halt,
+ .channel_busy = sh_dmae_channel_busy,
+ .slave_addr = sh_dmae_slave_addr,
+ .desc_setup = sh_dmae_desc_setup,
+ .set_slave = sh_dmae_set_slave,
+ .setup_xfer = sh_dmae_setup_xfer,
+ .start_xfer = sh_dmae_start_xfer,
+ .embedded_desc = sh_dmae_embedded_desc,
+ .chan_irq = sh_dmae_chan_irq,
+};
+
+static int __devinit sh_dmae_probe(struct platform_device *pdev)
{
struct sh_dmae_pdata *pdata = pdev->dev.platform_data;
unsigned long irqflags = IRQF_DISABLED,
- chan_flag[SH_DMAC_MAX_CHANNELS] = {};
- int errirq, chan_irq[SH_DMAC_MAX_CHANNELS];
+ chan_flag[SH_DMAE_MAX_CHANNELS] = {};
+ int errirq, chan_irq[SH_DMAE_MAX_CHANNELS];
int err, i, irq_cnt = 0, irqres = 0, irq_cap = 0;
struct sh_dmae_device *shdev;
+ struct dma_device *dma_dev;
struct resource *chan, *dmars, *errirq_res, *chanirq_res;
/* get platform data */
@@ -1222,6 +691,8 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
goto ealloc;
}
+ dma_dev = &shdev->simple_dev.dma_dev;
+
shdev->chan_reg = ioremap(chan->start, resource_size(chan));
if (!shdev->chan_reg)
goto emapchan;
@@ -1231,8 +702,23 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
goto emapdmars;
}
+ if (!pdata->slave_only)
+ dma_cap_set(DMA_MEMCPY, dma_dev->cap_mask);
+ if (pdata->slave && pdata->slave_num)
+ dma_cap_set(DMA_SLAVE, dma_dev->cap_mask);
+
+ /* Default transfer size of 32 bytes requires 32-byte alignment */
+ dma_dev->copy_align = LOG2_DEFAULT_XFER_SIZE;
+
+ shdev->simple_dev.ops = &sh_dmae_simple_ops;
+ shdev->simple_dev.desc_size = sizeof(struct sh_dmae_desc);
+ err = dma_simple_init(&pdev->dev, &shdev->simple_dev,
+ pdata->channel_num);
+ if (err < 0)
+ goto esimple;
+
/* platform data */
- shdev->pdata = pdata;
+ shdev->pdata = pdev->dev.platform_data;
if (pdata->chcr_offset)
shdev->chcr_offset = pdata->chcr_offset;
@@ -1246,10 +732,10 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, shdev);
- shdev->common.dev = &pdev->dev;
-
pm_runtime_enable(&pdev->dev);
- pm_runtime_get_sync(&pdev->dev);
+ err = pm_runtime_get_sync(&pdev->dev);
+ if (err < 0)
+ dev_err(&pdev->dev, "%s(): GET = %d\n", __func__, err);
spin_lock_irq(&sh_dmae_lock);
list_add_tail_rcu(&shdev->node, &sh_dmae_devices);
@@ -1260,27 +746,6 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
if (err)
goto rst_err;
- INIT_LIST_HEAD(&shdev->common.channels);
-
- if (!pdata->slave_only)
- dma_cap_set(DMA_MEMCPY, shdev->common.cap_mask);
- if (pdata->slave && pdata->slave_num)
- dma_cap_set(DMA_SLAVE, shdev->common.cap_mask);
-
- shdev->common.device_alloc_chan_resources
- = sh_dmae_alloc_chan_resources;
- shdev->common.device_free_chan_resources = sh_dmae_free_chan_resources;
- shdev->common.device_prep_dma_memcpy = sh_dmae_prep_memcpy;
- shdev->common.device_tx_status = sh_dmae_tx_status;
- shdev->common.device_issue_pending = sh_dmae_memcpy_issue_pending;
-
- /* Compulsory for DMA_SLAVE fields */
- shdev->common.device_prep_slave_sg = sh_dmae_prep_slave_sg;
- shdev->common.device_control = sh_dmae_control;
-
- /* Default transfer size of 32 bytes requires 32-byte alignment */
- shdev->common.copy_align = LOG2_DEFAULT_XFER_SIZE;
-
#if defined(CONFIG_CPU_SH4) || defined(CONFIG_ARCH_SHMOBILE)
chanirq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
@@ -1312,7 +777,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
!platform_get_resource(pdev, IORESOURCE_IRQ, 1)) {
/* Special case - all multiplexed */
for (; irq_cnt < pdata->channel_num; irq_cnt++) {
- if (irq_cnt < SH_DMAC_MAX_CHANNELS) {
+ if (irq_cnt < SH_DMAE_MAX_CHANNELS) {
chan_irq[irq_cnt] = chanirq_res->start;
chan_flag[irq_cnt] = IRQF_SHARED;
} else {
@@ -1323,7 +788,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
} else {
do {
for (i = chanirq_res->start; i <= chanirq_res->end; i++) {
- if (irq_cnt >= SH_DMAC_MAX_CHANNELS) {
+ if (irq_cnt >= SH_DMAE_MAX_CHANNELS) {
irq_cap = 1;
break;
}
@@ -1339,7 +804,7 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
chan_irq[irq_cnt++] = i;
}
- if (irq_cnt >= SH_DMAC_MAX_CHANNELS)
+ if (irq_cnt >= SH_DMAE_MAX_CHANNELS)
break;
chanirq_res = platform_get_resource(pdev,
@@ -1357,14 +822,19 @@ static int __init sh_dmae_probe(struct platform_device *pdev)
if (irq_cap)
dev_notice(&pdev->dev, "Attempting to register %d DMA "
"channels when a maximum of %d are supported.\n",
- pdata->channel_num, SH_DMAC_MAX_CHANNELS);
+ pdata->channel_num, SH_DMAE_MAX_CHANNELS);
pm_runtime_put(&pdev->dev);
- dma_async_device_register(&shdev->common);
+ err = dma_async_device_register(&shdev->simple_dev.dma_dev);
+ if (err < 0)
+ goto edmadevreg;
return err;
+edmadevreg:
+ pm_runtime_get(&pdev->dev);
+
chan_probe_err:
sh_dmae_chan_remove(shdev);
@@ -1380,10 +850,11 @@ rst_err:
pm_runtime_put(&pdev->dev);
pm_runtime_disable(&pdev->dev);
+ platform_set_drvdata(pdev, NULL);
+ dma_simple_cleanup(&shdev->simple_dev);
+esimple:
if (dmars)
iounmap(shdev->dmars);
-
- platform_set_drvdata(pdev, NULL);
emapdmars:
iounmap(shdev->chan_reg);
synchronize_rcu();
@@ -1398,13 +869,14 @@ ermrdmars:
return err;
}
-static int __exit sh_dmae_remove(struct platform_device *pdev)
+static int __devexit sh_dmae_remove(struct platform_device *pdev)
{
struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
+ struct dma_device *dma_dev = &shdev->simple_dev.dma_dev;
struct resource *res;
int errirq = platform_get_irq(pdev, 0);
- dma_async_device_unregister(&shdev->common);
+ dma_async_device_unregister(dma_dev);
if (errirq > 0)
free_irq(errirq, shdev);
@@ -1413,11 +885,11 @@ static int __exit sh_dmae_remove(struct platform_device *pdev)
list_del_rcu(&shdev->node);
spin_unlock_irq(&sh_dmae_lock);
- /* channel data remove */
- sh_dmae_chan_remove(shdev);
-
pm_runtime_disable(&pdev->dev);
+ sh_dmae_chan_remove(shdev);
+ dma_simple_cleanup(&shdev->simple_dev);
+
if (shdev->dmars)
iounmap(shdev->dmars);
iounmap(shdev->chan_reg);
@@ -1437,77 +909,14 @@ static int __exit sh_dmae_remove(struct platform_device *pdev)
return 0;
}
-static void sh_dmae_shutdown(struct platform_device *pdev)
-{
- struct sh_dmae_device *shdev = platform_get_drvdata(pdev);
- sh_dmae_ctl_stop(shdev);
-}
-
-static int sh_dmae_runtime_suspend(struct device *dev)
-{
- return 0;
-}
-
-static int sh_dmae_runtime_resume(struct device *dev)
-{
- struct sh_dmae_device *shdev = dev_get_drvdata(dev);
-
- return sh_dmae_rst(shdev);
-}
-
-#ifdef CONFIG_PM
-static int sh_dmae_suspend(struct device *dev)
-{
- return 0;
-}
-
-static int sh_dmae_resume(struct device *dev)
-{
- struct sh_dmae_device *shdev = dev_get_drvdata(dev);
- int i, ret;
-
- ret = sh_dmae_rst(shdev);
- if (ret < 0)
- dev_err(dev, "Failed to reset!\n");
-
- for (i = 0; i < shdev->pdata->channel_num; i++) {
- struct sh_dmae_chan *sh_chan = shdev->chan[i];
- struct sh_dmae_slave *param = sh_chan->common.private;
-
- if (!sh_chan->descs_allocated)
- continue;
-
- if (param) {
- const struct sh_dmae_slave_config *cfg = param->config;
- dmae_set_dmars(sh_chan, cfg->mid_rid);
- dmae_set_chcr(sh_chan, cfg->chcr);
- } else {
- dmae_init(sh_chan);
- }
- }
-
- return 0;
-}
-#else
-#define sh_dmae_suspend NULL
-#define sh_dmae_resume NULL
-#endif
-
-const struct dev_pm_ops sh_dmae_pm = {
- .suspend = sh_dmae_suspend,
- .resume = sh_dmae_resume,
- .runtime_suspend = sh_dmae_runtime_suspend,
- .runtime_resume = sh_dmae_runtime_resume,
-};
-
static struct platform_driver sh_dmae_driver = {
- .remove = __exit_p(sh_dmae_remove),
- .shutdown = sh_dmae_shutdown,
- .driver = {
+ .driver = {
.owner = THIS_MODULE,
- .name = "sh-dma-engine",
.pm = &sh_dmae_pm,
+ .name = SH_DMAE_DRV_NAME,
},
+ .remove = __devexit_p(sh_dmae_remove),
+ .shutdown = sh_dmae_shutdown,
};
static int __init sh_dmae_init(void)
@@ -1531,5 +940,6 @@ module_exit(sh_dmae_exit);
MODULE_AUTHOR("Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>");
MODULE_DESCRIPTION("Renesas SH DMA Engine driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:sh-dma-engine");
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("0.1.0");
+MODULE_ALIAS("platform:" SH_DMAE_DRV_NAME);
diff --git a/drivers/dma/shdma.h b/drivers/dma/shdma.h
index 2b55a27..8bffc19 100644
--- a/drivers/dma/shdma.h
+++ b/drivers/dma/shdma.h
@@ -13,43 +13,27 @@
#ifndef __DMA_SHDMA_H
#define __DMA_SHDMA_H
+#include <linux/dma-simple.h>
#include <linux/dmaengine.h>
#include <linux/interrupt.h>
#include <linux/list.h>
-#define SH_DMAC_MAX_CHANNELS 20
-#define SH_DMA_SLAVE_NUMBER 256
-#define SH_DMA_TCR_MAX 0x00FFFFFF /* 16MB */
+#define SH_DMAE_MAX_CHANNELS 20
+#define SH_DMAE_TCR_MAX 0x00FFFFFF /* 16MB */
struct device;
-enum dmae_pm_state {
- DMAE_PM_ESTABLISHED,
- DMAE_PM_BUSY,
- DMAE_PM_PENDING,
-};
-
struct sh_dmae_chan {
- dma_cookie_t completed_cookie; /* The maximum cookie completed */
- spinlock_t desc_lock; /* Descriptor operation lock */
- struct list_head ld_queue; /* Link descriptors queue */
- struct list_head ld_free; /* Link descriptors free */
- struct dma_chan common; /* DMA common channel */
- struct device *dev; /* Channel device */
- struct tasklet_struct tasklet; /* Tasklet */
- int descs_allocated; /* desc count */
+ struct dma_simple_chan simple_chan;
int xmit_shift; /* log_2(bytes_per_xfer) */
- int irq;
- int id; /* Raw id of this channel */
u32 __iomem *base;
char dev_id[16]; /* unique name per DMAC of channel */
int pm_error;
- enum dmae_pm_state pm_state;
};
struct sh_dmae_device {
- struct dma_device common;
- struct sh_dmae_chan *chan[SH_DMAC_MAX_CHANNELS];
+ struct dma_simple_dev simple_dev;
+ struct sh_dmae_chan *chan[SH_DMAE_MAX_CHANNELS];
struct sh_dmae_pdata *pdata;
struct list_head node;
u32 __iomem *chan_reg;
@@ -58,10 +42,21 @@ struct sh_dmae_device {
u32 chcr_ie_bit;
};
-#define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, common)
+struct sh_dmae_regs {
+ u32 sar; /* SAR / source address */
+ u32 dar; /* DAR / destination address */
+ u32 tcr; /* TCR / transfer count */
+};
+
+struct sh_dmae_desc {
+ struct sh_dmae_regs hw;
+ struct dma_simple_desc simple_desc;
+};
+
+#define to_sh_chan(chan) container_of(chan, struct sh_dmae_chan, simple_chan)
#define to_sh_desc(lh) container_of(lh, struct sh_desc, node)
#define tx_to_sh_desc(tx) container_of(tx, struct sh_desc, async_tx)
-#define to_sh_dev(chan) container_of(chan->common.device,\
- struct sh_dmae_device, common)
+#define to_sh_dev(chan) container_of(chan->simple_chan.dma_chan.device,\
+ struct sh_dmae_device, simple_dev.dma_dev)
#endif /* __DMA_SHDMA_H */
diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h
index ed2aa1e..4f22466 100644
--- a/include/linux/sh_dma.h
+++ b/include/linux/sh_dma.h
@@ -13,34 +13,14 @@
#include <linux/dma-simple.h>
#include <linux/dmaengine.h>
#include <linux/list.h>
+#include <linux/types.h>
-/* Used by slave DMA clients to request DMA to/from a specific peripheral */
-struct sh_dmae_slave {
- union {
- unsigned int slave_id; /* Set by the platform */
- struct dma_simple_slave simple_slave;
- };
- struct device *dma_dev; /* Set by the platform */
- const struct sh_dmae_slave_config *config; /* Set by the driver */
-};
-
-struct sh_dmae_regs {
- u32 sar; /* SAR / source address */
- u32 dar; /* DAR / destination address */
- u32 tcr; /* TCR / transfer count */
-};
-
-struct sh_desc {
- struct sh_dmae_regs hw;
- struct list_head node;
- struct dma_async_tx_descriptor async_tx;
- enum dma_transfer_direction direction;
- dma_cookie_t cookie;
- size_t partial;
- int chunks;
- int mark;
-};
+struct device;
+/*
+ * Supplied by platforms to specify, how a DMA channel has to be configured for
+ * a certain peripheral
+ */
struct sh_dmae_slave_config {
unsigned int slave_id;
dma_addr_t addr;
@@ -48,6 +28,13 @@ struct sh_dmae_slave_config {
char mid_rid;
};
+/* Used by slave DMA clients to request DMA to/from a specific peripheral */
+struct sh_dmae_slave {
+ struct dma_simple_slave simple_slave; /* Set by the platform */
+ struct device *dma_dev; /* Set by the platform */
+ const struct sh_dmae_slave_config *config; /* Set by the driver */
+};
+
struct sh_dmae_channel {
unsigned int offset;
unsigned int dmars;
--
1.7.2.5
^ permalink raw reply related
* [PATCH 6/7 v2] ASoC: SIU: prepare for conversion to simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: alsa-devel, linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda,
linux-mmc, Paul Mundt, linux-serial
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
sound/soc/sh/siu_pcm.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/sound/soc/sh/siu_pcm.c b/sound/soc/sh/siu_pcm.c
index 0193e59..414bb35 100644
--- a/sound/soc/sh/siu_pcm.c
+++ b/sound/soc/sh/siu_pcm.c
@@ -330,12 +330,12 @@ static bool filter(struct dma_chan *chan, void *slave)
{
struct sh_dmae_slave *param = slave;
- pr_debug("%s: slave ID %d\n", __func__, param->slave_id);
+ pr_debug("%s: slave ID %d\n", __func__, param->simple_slave.slave_id);
if (unlikely(param->dma_dev != chan->device->dev))
return false;
- chan->private = param;
+ chan->private = ¶m->simple_slave;
return true;
}
@@ -360,12 +360,12 @@ static int siu_pcm_open(struct snd_pcm_substream *ss)
if (ss->stream == SNDRV_PCM_STREAM_PLAYBACK) {
siu_stream = &port_info->playback;
param = &siu_stream->param;
- param->slave_id = port ? pdata->dma_slave_tx_b :
+ param->simple_slave.slave_id = port ? pdata->dma_slave_tx_b :
pdata->dma_slave_tx_a;
} else {
siu_stream = &port_info->capture;
param = &siu_stream->param;
- param->slave_id = port ? pdata->dma_slave_rx_b :
+ param->simple_slave.slave_id = port ? pdata->dma_slave_rx_b :
pdata->dma_slave_rx_a;
}
--
1.7.2.5
^ permalink raw reply related
* [PATCH 4/7 v2] mmc: sh_mobile_sdhi: prepare for conversion to simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
drivers/mmc/host/sh_mobile_sdhi.c | 8 ++++----
1 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/drivers/mmc/host/sh_mobile_sdhi.c b/drivers/mmc/host/sh_mobile_sdhi.c
index f91c3aa..2c3a9d3 100644
--- a/drivers/mmc/host/sh_mobile_sdhi.c
+++ b/drivers/mmc/host/sh_mobile_sdhi.c
@@ -130,10 +130,10 @@ static int __devinit sh_mobile_sdhi_probe(struct platform_device *pdev)
mmc_data->cd_gpio = p->cd_gpio;
if (p->dma_slave_tx > 0 && p->dma_slave_rx > 0) {
- priv->param_tx.slave_id = p->dma_slave_tx;
- priv->param_rx.slave_id = p->dma_slave_rx;
- priv->dma_priv.chan_priv_tx = &priv->param_tx;
- priv->dma_priv.chan_priv_rx = &priv->param_rx;
+ priv->param_tx.simple_slave.slave_id = p->dma_slave_tx;
+ priv->param_rx.simple_slave.slave_id = p->dma_slave_rx;
+ priv->dma_priv.chan_priv_tx = &priv->param_tx.simple_slave;
+ priv->dma_priv.chan_priv_rx = &priv->param_rx.simple_slave;
priv->dma_priv.alignment_shift = 1; /* 2-byte alignment */
mmc_data->dma = &priv->dma_priv;
}
--
1.7.2.5
^ permalink raw reply related
* [PATCH 3/7 v2] mmc: sh_mmcif: remove unneeded struct sh_mmcif_dma, prepare for simple DMA
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>
Now that all users have been updated to use the embedded in struct
sh_mmcif_plat_data DMA slave IDs, struct sh_mmcif_dma is no longer needed
and can be removed. This also makes preparation for simple DMA conversion
easier.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
drivers/mmc/host/sh_mmcif.c | 24 ++++++++++--------------
include/linux/mmc/sh_mmcif.h | 8 +-------
2 files changed, 11 insertions(+), 21 deletions(-)
diff --git a/drivers/mmc/host/sh_mmcif.c b/drivers/mmc/host/sh_mmcif.c
index 2ad6c81..06a7cdc 100644
--- a/drivers/mmc/host/sh_mmcif.c
+++ b/drivers/mmc/host/sh_mmcif.c
@@ -384,31 +384,27 @@ static void sh_mmcif_request_dma(struct sh_mmcif_host *host,
host->dma_active = false;
/* We can only either use DMA for both Tx and Rx or not use it at all */
- if (pdata->dma) {
- dev_warn(&host->pd->dev,
- "Update your platform to use embedded DMA slave IDs\n");
- tx = &pdata->dma->chan_priv_tx;
- rx = &pdata->dma->chan_priv_rx;
- } else {
- tx = &host->dma_slave_tx;
- tx->slave_id = pdata->slave_id_tx;
- rx = &host->dma_slave_rx;
- rx->slave_id = pdata->slave_id_rx;
- }
- if (tx->slave_id > 0 && rx->slave_id > 0) {
+ tx = &host->dma_slave_tx;
+ tx->simple_slave.slave_id = pdata->slave_id_tx;
+ rx = &host->dma_slave_rx;
+ rx->simple_slave.slave_id = pdata->slave_id_rx;
+
+ if (tx->simple_slave.slave_id > 0 && rx->simple_slave.slave_id > 0) {
dma_cap_mask_t mask;
dma_cap_zero(mask);
dma_cap_set(DMA_SLAVE, mask);
- host->chan_tx = dma_request_channel(mask, sh_mmcif_filter, tx);
+ host->chan_tx = dma_request_channel(mask, sh_mmcif_filter,
+ &tx->simple_slave);
dev_dbg(&host->pd->dev, "%s: TX: got channel %p\n", __func__,
host->chan_tx);
if (!host->chan_tx)
return;
- host->chan_rx = dma_request_channel(mask, sh_mmcif_filter, rx);
+ host->chan_rx = dma_request_channel(mask, sh_mmcif_filter,
+ &rx->simple_slave);
dev_dbg(&host->pd->dev, "%s: RX: got channel %p\n", __func__,
host->chan_rx);
diff --git a/include/linux/mmc/sh_mmcif.h b/include/linux/mmc/sh_mmcif.h
index 04ff452..b36caa9 100644
--- a/include/linux/mmc/sh_mmcif.h
+++ b/include/linux/mmc/sh_mmcif.h
@@ -32,17 +32,11 @@
* 1111 : Peripheral clock (sup_pclk set '1')
*/
-struct sh_mmcif_dma {
- struct sh_dmae_slave chan_priv_tx;
- struct sh_dmae_slave chan_priv_rx;
-};
-
struct sh_mmcif_plat_data {
void (*set_pwr)(struct platform_device *pdev, int state);
void (*down_pwr)(struct platform_device *pdev);
int (*get_cd)(struct platform_device *pdef);
- struct sh_mmcif_dma *dma; /* Deprecated. Instead */
- unsigned int slave_id_tx; /* use embedded slave_id_[tr]x */
+ unsigned int slave_id_tx; /* embedded slave_id_[tr]x */
unsigned int slave_id_rx;
u8 sup_pclk; /* 1 :SH7757, 0: SH7724/SH7372 */
unsigned long caps;
--
1.7.2.5
^ permalink raw reply related
* [PATCH 2/7 v2] dma: shdma: prepare for simple DMA conversion
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda, linux-mmc,
alsa-devel, linux-serial, Paul Mundt
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>
By placing an anonymous union at the top of struct sh_dmae_slave we can
transparently prepare all drivers for the upcoming simple DMA conversion.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
include/linux/sh_dma.h | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/include/linux/sh_dma.h b/include/linux/sh_dma.h
index 425450b..ed2aa1e 100644
--- a/include/linux/sh_dma.h
+++ b/include/linux/sh_dma.h
@@ -10,12 +10,16 @@
#ifndef SH_DMA_H
#define SH_DMA_H
-#include <linux/list.h>
+#include <linux/dma-simple.h>
#include <linux/dmaengine.h>
+#include <linux/list.h>
/* Used by slave DMA clients to request DMA to/from a specific peripheral */
struct sh_dmae_slave {
- unsigned int slave_id; /* Set by the platform */
+ union {
+ unsigned int slave_id; /* Set by the platform */
+ struct dma_simple_slave simple_slave;
+ };
struct device *dma_dev; /* Set by the platform */
const struct sh_dmae_slave_config *config; /* Set by the driver */
};
--
1.7.2.5
^ permalink raw reply related
* [PATCH 1/7 v2] dmaengine: add a simple dma library
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: alsa-devel, linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda,
linux-mmc, Paul Mundt, linux-serial
In-Reply-To: <1327589784-4287-1-git-send-email-g.liakhovetski@gmx.de>
This patch adds a library of functions, helping to implement dmaengine
drivers for hardware, unable to handle scatter-gather lists natively.
The first version of this driver only supports memcpy and slave DMA
operation.
Signed-off-by: Guennadi Liakhovetski <g.liakhovetski@gmx.de>
---
v2:
1. switch from using a tasklet to threaded IRQ, which allowed to
2. remove lock / unlock inline functions
3. remove __devinit, __devexit annotations
drivers/dma/Kconfig | 3 +
drivers/dma/Makefile | 1 +
drivers/dma/dma-simple.c | 873 ++++++++++++++++++++++++++++++++++++++++++++
include/linux/dma-simple.h | 124 +++++++
4 files changed, 1001 insertions(+), 0 deletions(-)
create mode 100644 drivers/dma/dma-simple.c
create mode 100644 include/linux/dma-simple.h
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index f1a2749..f7c583e 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -149,6 +149,9 @@ config TXX9_DMAC
Support the TXx9 SoC internal DMA controller. This can be
integrated in chips such as the Toshiba TX4927/38/39.
+config DMA_SIMPLE
+ tristate
+
config SH_DMAE
tristate "Renesas SuperH DMAC support"
depends on (SUPERH && SH_DMA) || (ARM && ARCH_SHMOBILE)
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 009a222..d63f773 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -2,6 +2,7 @@ ccflags-$(CONFIG_DMADEVICES_DEBUG) := -DDEBUG
ccflags-$(CONFIG_DMADEVICES_VDEBUG) += -DVERBOSE_DEBUG
obj-$(CONFIG_DMA_ENGINE) += dmaengine.o
+obj-$(CONFIG_DMA_SIMPLE) += dma-simple.o
obj-$(CONFIG_NET_DMA) += iovlock.o
obj-$(CONFIG_INTEL_MID_DMAC) += intel_mid_dma.o
obj-$(CONFIG_DMATEST) += dmatest.o
diff --git a/drivers/dma/dma-simple.c b/drivers/dma/dma-simple.c
new file mode 100644
index 0000000..49d8f7d
--- /dev/null
+++ b/drivers/dma/dma-simple.c
@@ -0,0 +1,873 @@
+/*
+ * Simple dmaengine driver library
+ *
+ * extracted from shdma.c
+ *
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/dma-simple.h>
+#include <linux/dmaengine.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+/* DMA descriptor control */
+enum simple_desc_status {
+ DESC_IDLE,
+ DESC_PREPARED,
+ DESC_SUBMITTED,
+ DESC_COMPLETED, /* completed, have to call callback */
+ DESC_WAITING, /* callback called, waiting for ack / re-submit */
+};
+
+#define NR_DESCS_PER_CHANNEL 32
+
+#define to_simple_chan(c) container_of(c, struct dma_simple_chan, dma_chan)
+#define to_simple_dev(d) container_of(d, struct dma_simple_dev, dma_dev)
+
+/*
+ * For slave DMA we assume, that there is a finite number of DMA slaves in the
+ * system, and that each such slave can only use a finite number of channels.
+ * We use slave channel IDs to make sure, that no such slave channel ID is
+ * allocated more than once.
+ */
+static unsigned int slave_num = 256;
+module_param(slave_num, uint, 0444);
+
+/* A bitmask with slave_num bits */
+static unsigned long *simple_slave_used;
+
+/* Called under spin_lock_irq(&schan->chan_lock") */
+static void simple_chan_xfer_ld_queue(struct dma_simple_chan *schan)
+{
+ struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+ const struct dma_simple_ops *ops = sdev->ops;
+ struct dma_simple_desc *sdesc;
+
+ /* DMA work check */
+ if (ops->channel_busy(schan))
+ return;
+
+ /* Find the first not transferred descriptor */
+ list_for_each_entry(sdesc, &schan->ld_queue, node)
+ if (sdesc->mark == DESC_SUBMITTED) {
+ ops->start_xfer(schan, sdesc);
+ break;
+ }
+}
+
+static dma_cookie_t simple_tx_submit(struct dma_async_tx_descriptor *tx)
+{
+ struct dma_simple_desc *chunk, *c, *desc =
+ container_of(tx, struct dma_simple_desc, async_tx),
+ *last = desc;
+ struct dma_simple_chan *schan = to_simple_chan(tx->chan);
+ struct dma_simple_slave *slave = tx->chan->private;
+ dma_async_tx_callback callback = tx->callback;
+ dma_cookie_t cookie;
+ bool power_up;
+
+ spin_lock_irq(&schan->chan_lock);
+
+ power_up = list_empty(&schan->ld_queue);
+
+ cookie = schan->dma_chan.cookie + 1;
+ if (cookie < 0)
+ cookie = 1;
+
+ schan->dma_chan.cookie = cookie;
+ tx->cookie = cookie;
+
+ /* Mark all chunks of this descriptor as submitted, move to the queue */
+ list_for_each_entry_safe(chunk, c, desc->node.prev, node) {
+ /*
+ * All chunks are on the global ld_free, so, we have to find
+ * the end of the chain ourselves
+ */
+ if (chunk != desc && (chunk->mark == DESC_IDLE ||
+ chunk->async_tx.cookie > 0 ||
+ chunk->async_tx.cookie == -EBUSY ||
+ &chunk->node == &schan->ld_free))
+ break;
+ chunk->mark = DESC_SUBMITTED;
+ /* Callback goes to the last chunk */
+ chunk->async_tx.callback = NULL;
+ chunk->cookie = cookie;
+ list_move_tail(&chunk->node, &schan->ld_queue);
+ last = chunk;
+
+ dev_dbg(schan->dev, "submit #%d@%p on %d\n",
+ tx->cookie, &last->async_tx, schan->id);
+ }
+
+ last->async_tx.callback = callback;
+ last->async_tx.callback_param = tx->callback_param;
+
+ if (power_up) {
+ int ret;
+ schan->pm_state = DMA_SIMPLE_PM_BUSY;
+
+ ret = pm_runtime_get(schan->dev);
+
+ spin_unlock_irq(&schan->chan_lock);
+ if (ret < 0)
+ dev_err(schan->dev, "%s(): GET = %d\n", __func__, ret);
+
+ pm_runtime_barrier(schan->dev);
+
+ spin_lock_irq(&schan->chan_lock);
+
+ /* Have we been reset, while waiting? */
+ if (schan->pm_state != DMA_SIMPLE_PM_ESTABLISHED) {
+ struct dma_simple_dev *sdev =
+ to_simple_dev(schan->dma_chan.device);
+ const struct dma_simple_ops *ops = sdev->ops;
+ dev_dbg(schan->dev, "Bring up channel %d\n",
+ schan->id);
+ /*
+ * TODO: .xfer_setup() might fail on some platforms.
+ * Make it int then, on error remove chunks from the
+ * queue again
+ */
+ ops->setup_xfer(schan, slave);
+
+ if (schan->pm_state == DMA_SIMPLE_PM_PENDING)
+ simple_chan_xfer_ld_queue(schan);
+ schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+ }
+ } else {
+ /*
+ * Tell .device_issue_pending() not to run the queue, interrupts
+ * will do it anyway
+ */
+ schan->pm_state = DMA_SIMPLE_PM_PENDING;
+ }
+
+ spin_unlock_irq(&schan->chan_lock);
+
+ return cookie;
+}
+
+/* Called with desc_lock held */
+static struct dma_simple_desc *simple_get_desc(struct dma_simple_chan *schan)
+{
+ struct dma_simple_desc *sdesc;
+
+ list_for_each_entry(sdesc, &schan->ld_free, node)
+ if (sdesc->mark != DESC_PREPARED) {
+ BUG_ON(sdesc->mark != DESC_IDLE);
+ list_del(&sdesc->node);
+ return sdesc;
+ }
+
+ return NULL;
+}
+
+static int simple_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct dma_simple_chan *schan = to_simple_chan(chan);
+ struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+ const struct dma_simple_ops *ops = sdev->ops;
+ struct dma_simple_desc *desc;
+ struct dma_simple_slave *slave = chan->private;
+ int ret, i;
+
+ /*
+ * This relies on the guarantee from dmaengine that alloc_chan_resources
+ * never runs concurrently with itself or free_chan_resources.
+ */
+ if (slave) {
+ if (test_and_set_bit(slave->slave_id, simple_slave_used)) {
+ ret = -EBUSY;
+ goto etestused;
+ }
+
+ ret = ops->set_slave(schan, slave);
+ if (ret < 0)
+ goto esetslave;
+ }
+
+ schan->desc = kcalloc(NR_DESCS_PER_CHANNEL,
+ sdev->desc_size, GFP_KERNEL);
+ if (!schan->desc) {
+ ret = -ENOMEM;
+ goto edescalloc;
+ }
+ schan->desc_num = NR_DESCS_PER_CHANNEL;
+
+ for (i = 0; i < NR_DESCS_PER_CHANNEL; i++) {
+ desc = ops->embedded_desc(schan->desc, i);
+ dma_async_tx_descriptor_init(&desc->async_tx,
+ &schan->dma_chan);
+ desc->async_tx.tx_submit = simple_tx_submit;
+ desc->mark = DESC_IDLE;
+
+ list_add(&desc->node, &schan->ld_free);
+ }
+
+ return NR_DESCS_PER_CHANNEL;
+
+edescalloc:
+ if (slave)
+esetslave:
+ clear_bit(slave->slave_id, simple_slave_used);
+etestused:
+ chan->private = NULL;
+ return ret;
+}
+
+static dma_async_tx_callback __ld_cleanup(struct dma_simple_chan *schan, bool all)
+{
+ struct dma_simple_desc *desc, *_desc;
+ /* Is the "exposed" head of a chain acked? */
+ bool head_acked = false;
+ dma_cookie_t cookie = 0;
+ dma_async_tx_callback callback = NULL;
+ void *param = NULL;
+ unsigned long flags;
+
+ spin_lock_irqsave(&schan->chan_lock, flags);
+ list_for_each_entry_safe(desc, _desc, &schan->ld_queue, node) {
+ struct dma_async_tx_descriptor *tx = &desc->async_tx;
+
+ BUG_ON(tx->cookie > 0 && tx->cookie != desc->cookie);
+ BUG_ON(desc->mark != DESC_SUBMITTED &&
+ desc->mark != DESC_COMPLETED &&
+ desc->mark != DESC_WAITING);
+
+ /*
+ * queue is ordered, and we use this loop to (1) clean up all
+ * completed descriptors, and to (2) update descriptor flags of
+ * any chunks in a (partially) completed chain
+ */
+ if (!all && desc->mark == DESC_SUBMITTED &&
+ desc->cookie != cookie)
+ break;
+
+ if (tx->cookie > 0)
+ cookie = tx->cookie;
+
+ if (desc->mark == DESC_COMPLETED && desc->chunks == 1) {
+ if (schan->completed_cookie != desc->cookie - 1)
+ dev_dbg(schan->dev,
+ "Completing cookie %d, expected %d\n",
+ desc->cookie,
+ schan->completed_cookie + 1);
+ schan->completed_cookie = desc->cookie;
+ }
+
+ /* Call callback on the last chunk */
+ if (desc->mark == DESC_COMPLETED && tx->callback) {
+ desc->mark = DESC_WAITING;
+ callback = tx->callback;
+ param = tx->callback_param;
+ dev_dbg(schan->dev, "descriptor #%d@%p on %d callback\n",
+ tx->cookie, tx, schan->id);
+ BUG_ON(desc->chunks != 1);
+ break;
+ }
+
+ if (tx->cookie > 0 || tx->cookie == -EBUSY) {
+ if (desc->mark == DESC_COMPLETED) {
+ BUG_ON(tx->cookie < 0);
+ desc->mark = DESC_WAITING;
+ }
+ head_acked = async_tx_test_ack(tx);
+ } else {
+ switch (desc->mark) {
+ case DESC_COMPLETED:
+ desc->mark = DESC_WAITING;
+ /* Fall through */
+ case DESC_WAITING:
+ if (head_acked)
+ async_tx_ack(&desc->async_tx);
+ }
+ }
+
+ dev_dbg(schan->dev, "descriptor %p #%d completed.\n",
+ tx, tx->cookie);
+
+ if (((desc->mark == DESC_COMPLETED ||
+ desc->mark == DESC_WAITING) &&
+ async_tx_test_ack(&desc->async_tx)) || all) {
+ /* Remove from ld_queue list */
+ desc->mark = DESC_IDLE;
+
+ list_move(&desc->node, &schan->ld_free);
+
+ if (list_empty(&schan->ld_queue)) {
+ dev_dbg(schan->dev, "Bring down channel %d\n", schan->id);
+ pm_runtime_put(schan->dev);
+ schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+ }
+ }
+ }
+
+ if (all && !callback)
+ /*
+ * Terminating and the loop completed normally: forgive
+ * uncompleted cookies
+ */
+ schan->completed_cookie = schan->dma_chan.cookie;
+
+ spin_unlock_irqrestore(&schan->chan_lock, flags);
+
+ if (callback)
+ callback(param);
+
+ return callback;
+}
+
+/*
+ * simple_chan_ld_cleanup - Clean up link descriptors
+ *
+ * Clean up the ld_queue of DMA channel.
+ */
+static void simple_chan_ld_cleanup(struct dma_simple_chan *schan, bool all)
+{
+ while (__ld_cleanup(schan, all))
+ ;
+}
+
+/*
+ * simple_free_chan_resources - Free all resources of the channel.
+ */
+static void simple_free_chan_resources(struct dma_chan *chan)
+{
+ struct dma_simple_chan *schan = to_simple_chan(chan);
+ struct dma_simple_dev *sdev = to_simple_dev(chan->device);
+ const struct dma_simple_ops *ops = sdev->ops;
+ LIST_HEAD(list);
+
+ /* Protect against ISR */
+ spin_lock_irq(&schan->chan_lock);
+ ops->halt_channel(schan);
+ spin_unlock_irq(&schan->chan_lock);
+
+ /* Now no new interrupts will occur */
+
+ /* Prepared and not submitted descriptors can still be on the queue */
+ if (!list_empty(&schan->ld_queue))
+ simple_chan_ld_cleanup(schan, true);
+
+ if (chan->private) {
+ /* The caller is holding dma_list_mutex */
+ struct dma_simple_slave *slave = chan->private;
+ clear_bit(slave->slave_id, simple_slave_used);
+ chan->private = NULL;
+ }
+
+ spin_lock_irq(&schan->chan_lock);
+
+ list_splice_init(&schan->ld_free, &list);
+ schan->desc_num = 0;
+
+ spin_unlock_irq(&schan->chan_lock);
+
+ kfree(schan->desc);
+}
+
+/**
+ * simple_add_desc - get, set up and return one transfer descriptor
+ * @schan: DMA channel
+ * @flags: DMA transfer flags
+ * @dst: destination DMA address, incremented when direction equals
+ * DMA_DEV_TO_MEM or DMA_MEM_TO_MEM
+ * @src: source DMA address, incremented when direction equals
+ * DMA_MEM_TO_DEV or DMA_MEM_TO_MEM
+ * @len: DMA transfer length
+ * @first: if NULL, set to the current descriptor and cookie set to -EBUSY
+ * @direction: needed for slave DMA to decide which address to keep constant,
+ * equals DMA_MEM_TO_MEM for MEMCPY
+ * Returns 0 or an error
+ * Locks: called with desc_lock held
+ */
+static struct dma_simple_desc *simple_add_desc(struct dma_simple_chan *schan,
+ unsigned long flags, dma_addr_t *dst, dma_addr_t *src, size_t *len,
+ struct dma_simple_desc **first, enum dma_transfer_direction direction)
+{
+ struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+ const struct dma_simple_ops *ops = sdev->ops;
+ struct dma_simple_desc *new;
+ size_t copy_size = *len;
+
+ if (!copy_size)
+ return NULL;
+
+ /* Allocate the link descriptor from the free list */
+ new = simple_get_desc(schan);
+ if (!new) {
+ dev_err(schan->dev, "No free link descriptor available\n");
+ return NULL;
+ }
+
+ ops->desc_setup(schan, new, *src, *dst, ©_size);
+
+ if (!*first) {
+ /* First desc */
+ new->async_tx.cookie = -EBUSY;
+ *first = new;
+ } else {
+ /* Other desc - invisible to the user */
+ new->async_tx.cookie = -EINVAL;
+ }
+
+ dev_dbg(schan->dev,
+ "chaining (%u/%u)@%x -> %x with %p, cookie %d\n",
+ copy_size, *len, *src, *dst, &new->async_tx,
+ new->async_tx.cookie);
+
+ new->mark = DESC_PREPARED;
+ new->async_tx.flags = flags;
+ new->direction = direction;
+
+ *len -= copy_size;
+ if (direction == DMA_MEM_TO_MEM || direction == DMA_MEM_TO_DEV)
+ *src += copy_size;
+ if (direction == DMA_MEM_TO_MEM || direction == DMA_DEV_TO_MEM)
+ *dst += copy_size;
+
+ return new;
+}
+
+/*
+ * simple_prep_sg - prepare transfer descriptors from an SG list
+ *
+ * Common routine for public (MEMCPY) and slave DMA. The MEMCPY case is also
+ * converted to scatter-gather to guarantee consistent locking and a correct
+ * list manipulation. For slave DMA direction carries the usual meaning, and,
+ * logically, the SG list is RAM and the addr variable contains slave address,
+ * e.g., the FIFO I/O register. For MEMCPY direction equals DMA_MEM_TO_MEM
+ * and the SG list contains only one element and points at the source buffer.
+ */
+static struct dma_async_tx_descriptor *simple_prep_sg(struct dma_simple_chan *schan,
+ struct scatterlist *sgl, unsigned int sg_len, dma_addr_t *addr,
+ enum dma_transfer_direction direction, unsigned long flags)
+{
+ struct scatterlist *sg;
+ struct dma_simple_desc *first = NULL, *new = NULL /* compiler... */;
+ LIST_HEAD(tx_list);
+ int chunks = 0;
+ unsigned long irq_flags;
+ int i;
+
+ for_each_sg(sgl, sg, sg_len, i)
+ chunks += DIV_ROUND_UP(sg_dma_len(sg), schan->max_xfer_len);
+
+ /* Have to lock the whole loop to protect against concurrent release */
+ spin_lock_irqsave(&schan->chan_lock, irq_flags);
+
+ /*
+ * Chaining:
+ * first descriptor is what user is dealing with in all API calls, its
+ * cookie is at first set to -EBUSY, at tx-submit to a positive
+ * number
+ * if more than one chunk is needed further chunks have cookie = -EINVAL
+ * the last chunk, if not equal to the first, has cookie = -ENOSPC
+ * all chunks are linked onto the tx_list head with their .node heads
+ * only during this function, then they are immediately spliced
+ * back onto the free list in form of a chain
+ */
+ for_each_sg(sgl, sg, sg_len, i) {
+ dma_addr_t sg_addr = sg_dma_address(sg);
+ size_t len = sg_dma_len(sg);
+
+ if (!len)
+ goto err_get_desc;
+
+ do {
+ dev_dbg(schan->dev, "Add SG #%d@%p[%d], dma %llx\n",
+ i, sg, len, (unsigned long long)sg_addr);
+
+ if (direction == DMA_DEV_TO_MEM)
+ new = simple_add_desc(schan, flags,
+ &sg_addr, addr, &len, &first,
+ direction);
+ else
+ new = simple_add_desc(schan, flags,
+ addr, &sg_addr, &len, &first,
+ direction);
+ if (!new)
+ goto err_get_desc;
+
+ new->chunks = chunks--;
+ list_add_tail(&new->node, &tx_list);
+ } while (len);
+ }
+
+ if (new != first)
+ new->async_tx.cookie = -ENOSPC;
+
+ /* Put them back on the free list, so, they don't get lost */
+ list_splice_tail(&tx_list, &schan->ld_free);
+
+ spin_unlock_irqrestore(&schan->chan_lock, irq_flags);
+
+ return &first->async_tx;
+
+err_get_desc:
+ list_for_each_entry(new, &tx_list, node)
+ new->mark = DESC_IDLE;
+ list_splice(&tx_list, &schan->ld_free);
+
+ spin_unlock_irqrestore(&schan->chan_lock, irq_flags);
+
+ return NULL;
+}
+
+static struct dma_async_tx_descriptor *simple_prep_memcpy(
+ struct dma_chan *chan, dma_addr_t dma_dest, dma_addr_t dma_src,
+ size_t len, unsigned long flags)
+{
+ struct dma_simple_chan *schan = to_simple_chan(chan);
+ struct scatterlist sg;
+
+ if (!chan || !len)
+ return NULL;
+
+ BUG_ON(!schan->desc_num);
+
+ sg_init_table(&sg, 1);
+ sg_set_page(&sg, pfn_to_page(PFN_DOWN(dma_src)), len,
+ offset_in_page(dma_src));
+ sg_dma_address(&sg) = dma_src;
+ sg_dma_len(&sg) = len;
+
+ return simple_prep_sg(schan, &sg, 1, &dma_dest, DMA_MEM_TO_MEM, flags);
+}
+
+static struct dma_async_tx_descriptor *simple_prep_slave_sg(
+ struct dma_chan *chan, struct scatterlist *sgl, unsigned int sg_len,
+ enum dma_transfer_direction direction, unsigned long flags)
+{
+ struct dma_simple_chan *schan = to_simple_chan(chan);
+ struct dma_simple_dev *sdev = to_simple_dev(schan->dma_chan.device);
+ const struct dma_simple_ops *ops = sdev->ops;
+ struct dma_simple_slave *slave = chan->private;
+ dma_addr_t slave_addr;
+
+ if (!chan)
+ return NULL;
+
+ BUG_ON(!schan->desc_num);
+
+ /* Someone calling slave DMA on a generic channel? */
+ if (!slave || !sg_len) {
+ dev_warn(schan->dev, "%s: bad parameter: %p, %d, %d\n",
+ __func__, slave, sg_len, slave ? slave->slave_id : -1);
+ return NULL;
+ }
+
+ slave_addr = ops->slave_addr(schan);
+
+ return simple_prep_sg(schan, sgl, sg_len, &slave_addr,
+ direction, flags);
+}
+
+static int simple_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+ unsigned long arg)
+{
+ struct dma_simple_chan *schan = to_simple_chan(chan);
+ struct dma_simple_dev *sdev = to_simple_dev(chan->device);
+ const struct dma_simple_ops *ops = sdev->ops;
+ unsigned long flags;
+
+ /* Only supports DMA_TERMINATE_ALL */
+ if (cmd != DMA_TERMINATE_ALL)
+ return -ENXIO;
+
+ if (!chan)
+ return -EINVAL;
+
+ spin_lock_irqsave(&schan->chan_lock, flags);
+
+ ops->halt_channel(schan);
+
+ spin_unlock_irqrestore(&schan->chan_lock, flags);
+
+ simple_chan_ld_cleanup(schan, true);
+
+ return 0;
+}
+
+static void simple_issue_pending(struct dma_chan *chan)
+{
+ struct dma_simple_chan *schan = to_simple_chan(chan);
+
+ spin_lock_irq(&schan->chan_lock);
+ if (schan->pm_state == DMA_SIMPLE_PM_ESTABLISHED)
+ simple_chan_xfer_ld_queue(schan);
+ else
+ schan->pm_state = DMA_SIMPLE_PM_PENDING;
+ spin_unlock_irq(&schan->chan_lock);
+}
+
+static enum dma_status simple_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct dma_simple_chan *schan = to_simple_chan(chan);
+ dma_cookie_t last_used;
+ dma_cookie_t last_complete;
+ enum dma_status status;
+ unsigned long flags;
+
+ simple_chan_ld_cleanup(schan, false);
+
+ /* First read completed cookie to avoid a skew */
+ last_complete = schan->completed_cookie;
+ rmb();
+ last_used = chan->cookie;
+ BUG_ON(last_complete < 0);
+ dma_set_tx_state(txstate, last_complete, last_used, 0);
+
+ spin_lock_irqsave(&schan->chan_lock, flags);
+
+ status = dma_async_is_complete(cookie, last_complete, last_used);
+
+ /*
+ * If we don't find cookie on the queue, it has been aborted and we have
+ * to report error
+ */
+ if (status != DMA_SUCCESS) {
+ struct dma_simple_desc *sdesc;
+ status = DMA_ERROR;
+ list_for_each_entry(sdesc, &schan->ld_queue, node)
+ if (sdesc->cookie == cookie) {
+ status = DMA_IN_PROGRESS;
+ break;
+ }
+ }
+
+ spin_unlock_irqrestore(&schan->chan_lock, flags);
+
+ return status;
+}
+
+/* Called from error IRQ or NMI */
+bool dma_simple_reset(struct dma_simple_dev *sdev)
+{
+ const struct dma_simple_ops *ops = sdev->ops;
+ struct dma_simple_chan *schan;
+ unsigned int handled = 0;
+ int i;
+
+ /* Reset all channels */
+ dma_simple_for_each_chan(schan, sdev, i) {
+ struct dma_simple_desc *sdesc;
+ LIST_HEAD(dl);
+
+ if (!schan)
+ continue;
+
+ spin_lock(&schan->chan_lock);
+
+ /* Stop the channel */
+ ops->halt_channel(schan);
+
+ list_splice_init(&schan->ld_queue, &dl);
+
+ if (!list_empty(&dl)) {
+ dev_dbg(schan->dev, "Bring down channel %d\n", schan->id);
+ pm_runtime_put(schan->dev);
+ }
+ schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+
+ spin_unlock(&schan->chan_lock);
+
+ /* Complete all */
+ list_for_each_entry(sdesc, &dl, node) {
+ struct dma_async_tx_descriptor *tx = &sdesc->async_tx;
+ sdesc->mark = DESC_IDLE;
+ if (tx->callback)
+ tx->callback(tx->callback_param);
+ }
+
+ spin_lock(&schan->chan_lock);
+ list_splice(&dl, &schan->ld_free);
+ spin_unlock(&schan->chan_lock);
+
+ handled++;
+ }
+
+ return !!handled;
+}
+EXPORT_SYMBOL(dma_simple_reset);
+
+static irqreturn_t chan_irq(int irq, void *dev)
+{
+ struct dma_simple_chan *schan = dev;
+ const struct dma_simple_ops *ops =
+ to_simple_dev(schan->dma_chan.device)->ops;
+ irqreturn_t ret;
+
+ spin_lock(&schan->chan_lock);
+
+ ret = ops->chan_irq(schan, irq) ? IRQ_WAKE_THREAD : IRQ_NONE;
+
+ spin_unlock(&schan->chan_lock);
+
+ return ret;
+}
+
+static irqreturn_t chan_irqt(int irq, void *dev)
+{
+ struct dma_simple_chan *schan = dev;
+ const struct dma_simple_ops *ops =
+ to_simple_dev(schan->dma_chan.device)->ops;
+ struct dma_simple_desc *sdesc;
+
+ spin_lock_irq(&schan->chan_lock);
+ list_for_each_entry(sdesc, &schan->ld_queue, node) {
+ if (sdesc->mark == DESC_SUBMITTED &&
+ ops->desc_completed(schan, sdesc)) {
+ dev_dbg(schan->dev, "done #%d@%p\n",
+ sdesc->async_tx.cookie, &sdesc->async_tx);
+ sdesc->mark = DESC_COMPLETED;
+ break;
+ }
+ }
+ /* Next desc */
+ simple_chan_xfer_ld_queue(schan);
+ spin_unlock_irq(&schan->chan_lock);
+
+ simple_chan_ld_cleanup(schan, false);
+
+ return IRQ_HANDLED;
+}
+
+int dma_simple_request_irq(struct dma_simple_chan *schan, int irq,
+ unsigned long flags, const char *name)
+{
+ int ret = request_threaded_irq(irq, chan_irq, chan_irqt,
+ flags, name, schan);
+
+ schan->irq = ret < 0 ? ret : irq;
+
+ return ret;
+}
+EXPORT_SYMBOL(dma_simple_request_irq);
+
+void dma_simple_free_irq(struct dma_simple_chan *schan)
+{
+ if (schan->irq >= 0)
+ free_irq(schan->irq, schan);
+}
+EXPORT_SYMBOL(dma_simple_free_irq);
+
+void dma_simple_chan_probe(struct dma_simple_dev *sdev,
+ struct dma_simple_chan *schan, int id)
+{
+ schan->pm_state = DMA_SIMPLE_PM_ESTABLISHED;
+
+ /* reference struct dma_device */
+ schan->dma_chan.device = &sdev->dma_dev;
+
+ schan->dev = sdev->dma_dev.dev;
+ schan->id = id;
+
+ if (!schan->max_xfer_len)
+ schan->max_xfer_len = PAGE_SIZE;
+
+ spin_lock_init(&schan->chan_lock);
+
+ /* Init descripter manage list */
+ INIT_LIST_HEAD(&schan->ld_queue);
+ INIT_LIST_HEAD(&schan->ld_free);
+
+ /* Add the channel to DMA device channel list */
+ list_add_tail(&schan->dma_chan.device_node,
+ &sdev->dma_dev.channels);
+ sdev->schan[sdev->dma_dev.chancnt++] = schan;
+}
+EXPORT_SYMBOL(dma_simple_chan_probe);
+
+void dma_simple_chan_remove(struct dma_simple_chan *schan)
+{
+ list_del(&schan->dma_chan.device_node);
+}
+EXPORT_SYMBOL(dma_simple_chan_remove);
+
+int dma_simple_init(struct device *dev, struct dma_simple_dev *sdev,
+ int chan_num)
+{
+ struct dma_device *dma_dev = &sdev->dma_dev;
+
+ /*
+ * Require all call-backs for now, they can trivially be made optional
+ * later as required
+ */
+ if (!sdev->ops ||
+ !sdev->desc_size ||
+ !sdev->ops->embedded_desc ||
+ !sdev->ops->start_xfer ||
+ !sdev->ops->setup_xfer ||
+ !sdev->ops->set_slave ||
+ !sdev->ops->desc_setup ||
+ !sdev->ops->slave_addr ||
+ !sdev->ops->channel_busy ||
+ !sdev->ops->halt_channel ||
+ !sdev->ops->desc_completed)
+ return -EINVAL;
+
+ sdev->schan = kcalloc(chan_num, sizeof(*sdev->schan), GFP_KERNEL);
+ if (!sdev->schan)
+ return -ENOMEM;
+
+ INIT_LIST_HEAD(&dma_dev->channels);
+
+ /* Common and MEMCPY operations */
+ dma_dev->device_alloc_chan_resources
+ = simple_alloc_chan_resources;
+ dma_dev->device_free_chan_resources = simple_free_chan_resources;
+ dma_dev->device_prep_dma_memcpy = simple_prep_memcpy;
+ dma_dev->device_tx_status = simple_tx_status;
+ dma_dev->device_issue_pending = simple_issue_pending;
+
+ /* Compulsory for DMA_SLAVE fields */
+ dma_dev->device_prep_slave_sg = simple_prep_slave_sg;
+ dma_dev->device_control = simple_control;
+
+ dma_dev->dev = dev;
+
+ return 0;
+}
+EXPORT_SYMBOL(dma_simple_init);
+
+void dma_simple_cleanup(struct dma_simple_dev *sdev)
+{
+ kfree(sdev->schan);
+}
+EXPORT_SYMBOL(dma_simple_cleanup);
+
+static int __init dma_simple_enter(void)
+{
+ simple_slave_used = kzalloc(DIV_ROUND_UP(slave_num, BITS_PER_LONG) *
+ sizeof(long), GFP_KERNEL);
+ if (!simple_slave_used)
+ return -ENOMEM;
+ return 0;
+}
+module_init(dma_simple_enter);
+
+static void __exit dma_simple_exit(void)
+{
+ kfree(simple_slave_used);
+}
+module_exit(dma_simple_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Simple dmaengine driver library");
+MODULE_AUTHOR("Guennadi Liakhovetski <g.liakhovetski@gmx.de>");
diff --git a/include/linux/dma-simple.h b/include/linux/dma-simple.h
new file mode 100644
index 0000000..5336674
--- /dev/null
+++ b/include/linux/dma-simple.h
@@ -0,0 +1,124 @@
+/*
+ * Simple dmaengine driver library
+ *
+ * extracted from shdma.c and headers
+ *
+ * Copyright (C) 2011-2012 Guennadi Liakhovetski <g.liakhovetski@gmx.de>
+ * Copyright (C) 2009 Nobuhiro Iwamatsu <iwamatsu.nobuhiro@renesas.com>
+ * Copyright (C) 2009 Renesas Solutions, Inc. All rights reserved.
+ * Copyright (C) 2007 Freescale Semiconductor, Inc. All rights reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef DMA_SIMPLE_H
+#define DMA_SIMPLE_H
+
+#include <linux/dmaengine.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/types.h>
+
+/**
+ * dma_simple_pm_state - DMA channel PM state
+ * DMA_SIMPLE_PM_ESTABLISHED: either idle or during data transfer
+ * DMA_SIMPLE_PM_BUSY: during the transfer preparation, when we have to
+ * drop the lock temporarily
+ * DMA_SIMPLE_PM_PENDING: transfers pending
+ */
+enum dma_simple_pm_state {
+ DMA_SIMPLE_PM_ESTABLISHED,
+ DMA_SIMPLE_PM_BUSY,
+ DMA_SIMPLE_PM_PENDING,
+};
+
+struct device;
+
+/*
+ * Drivers, using this library are expected to embed struct dma_simple_dev,
+ * struct dma_simple_chan, struct dma_simple_desc, and struct dma_simple_slave
+ * in their respective device, channel, descriptor and slave objects.
+ */
+
+struct dma_simple_slave {
+ unsigned int slave_id;
+};
+
+struct dma_simple_desc {
+ struct list_head node;
+ struct dma_async_tx_descriptor async_tx;
+ enum dma_transfer_direction direction;
+ dma_cookie_t cookie;
+ int chunks;
+ int mark;
+};
+
+struct dma_simple_chan {
+ dma_cookie_t completed_cookie; /* The maximum cookie completed */
+ spinlock_t chan_lock; /* Channel operation lock */
+ struct list_head ld_queue; /* Link descriptors queue */
+ struct list_head ld_free; /* Free link descriptors */
+ struct dma_chan dma_chan; /* DMA channel */
+ struct device *dev; /* Channel device */
+ void *desc; /* buffer for descriptor array */
+ int desc_num; /* desc count */
+ size_t max_xfer_len; /* max transfer length */
+ int id; /* Raw id of this channel */
+ int irq; /* Channel IRQ */
+ enum dma_simple_pm_state pm_state;
+};
+
+/**
+ * struct dma_simple_ops - simple DMA driver operations
+ * desc_completed: return true, if this is the descriptor, that just has
+ * completed (atomic)
+ * halt_channel: stop DMA channel operation (atomic)
+ * channel_busy: return true, if the channel is busy (atomic)
+ * slave_addr: return slave DMA address
+ * desc_setup: set up the hardware specific descriptor portion (atomic)
+ * set_slave: bind channel to a slave
+ * setup_xfer: configure channel hardware for operation (atomic)
+ * start_xfer: start the DMA transfer (atomic)
+ * embedded_desc: return Nth struct dma_simple_desc pointer from the
+ * descriptor array
+ * chan_irq: process channel IRQ, return true if a transfer has
+ * completed (atomic)
+ */
+struct dma_simple_ops {
+ bool (*desc_completed)(struct dma_simple_chan *, struct dma_simple_desc *);
+ void (*halt_channel)(struct dma_simple_chan *);
+ bool (*channel_busy)(struct dma_simple_chan *);
+ dma_addr_t (*slave_addr)(struct dma_simple_chan *);
+ int (*desc_setup)(struct dma_simple_chan *, struct dma_simple_desc *,
+ dma_addr_t, dma_addr_t, size_t *);
+ int (*set_slave)(struct dma_simple_chan *, struct dma_simple_slave *);
+ void (*setup_xfer)(struct dma_simple_chan *, struct dma_simple_slave *);
+ void (*start_xfer)(struct dma_simple_chan *, struct dma_simple_desc *);
+ struct dma_simple_desc *(*embedded_desc)(void *, int);
+ bool (*chan_irq)(struct dma_simple_chan *, int);
+};
+
+struct dma_simple_dev {
+ struct dma_device dma_dev;
+ struct dma_simple_chan **schan;
+ const struct dma_simple_ops *ops;
+ size_t desc_size;
+};
+
+#define dma_simple_for_each_chan(c, d, i) for (i = 0, c = (d)->schan[0]; \
+ i < (d)->dma_dev.chancnt; c = (d)->schan[++i])
+
+int dma_simple_request_irq(struct dma_simple_chan *, int,
+ unsigned long, const char *);
+void dma_simple_free_irq(struct dma_simple_chan *);
+bool dma_simple_reset(struct dma_simple_dev *sdev);
+void dma_simple_chan_probe(struct dma_simple_dev *sdev,
+ struct dma_simple_chan *schan, int id);
+void dma_simple_chan_remove(struct dma_simple_chan *schan);
+int dma_simple_init(struct device *dev, struct dma_simple_dev *sdev,
+ int chan_num);
+void dma_simple_cleanup(struct dma_simple_dev *sdev);
+
+#endif
--
1.7.2.5
^ permalink raw reply related
* [PATCH 0/7 v2] extract a simple dmaengine library from shdma.c
From: Guennadi Liakhovetski @ 2012-01-26 14:56 UTC (permalink / raw)
To: linux-kernel
Cc: alsa-devel, linux-sh, Vinod Koul, Magnus Damm, Yoshihiro Shimoda,
linux-mmc, Paul Mundt, linux-serial
This is v2 of the simple DMA dmaengine library. It addresses Paul's
comments to v1 - thanks, adds some comments.
Guennadi Liakhovetski (7):
dmaengine: add a simple dma library
dma: shdma: prepare for simple DMA conversion
mmc: sh_mmcif: remove unneeded struct sh_mmcif_dma, prepare for
simple DMA
mmc: sh_mobile_sdhi: prepare for conversion to simple DMA
serial: sh-sci: prepare for conversion to simple DMA
ASoC: SIU: prepare for conversion to simple DMA
dma: shdma: convert to the simple DMA library
drivers/dma/Kconfig | 4 +
drivers/dma/Makefile | 1 +
drivers/dma/dma-simple.c | 873 ++++++++++++++++++++++++++++
drivers/dma/shdma.c | 1138 +++++++++----------------------------
drivers/dma/shdma.h | 45 +-
drivers/mmc/host/sh_mmcif.c | 24 +-
drivers/mmc/host/sh_mobile_sdhi.c | 8 +-
drivers/tty/serial/sh-sci.c | 8 +-
include/linux/dma-simple.h | 124 ++++
include/linux/mmc/sh_mmcif.h | 8 +-
include/linux/sh_dma.h | 39 +-
sound/soc/sh/siu_pcm.c | 8 +-
12 files changed, 1334 insertions(+), 946 deletions(-)
create mode 100644 drivers/dma/dma-simple.c
create mode 100644 include/linux/dma-simple.h
--
1.7.2.5
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply
* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Paul Walmsley @ 2012-01-26 4:31 UTC (permalink / raw)
To: Greg KH
Cc: khilman, govindraj.raja, tomi.valkeinen, linux-serial, linux-omap,
linux-arm-kernel
In-Reply-To: <20120126042155.GA3185@suse.de>
On Wed, 25 Jan 2012, Greg KH wrote:
> On Wed, Jan 25, 2012 at 08:02:09PM -0700, Paul Walmsley wrote:
> > On Tue, 24 Jan 2012, gregkh@suse.de wrote:
> > >
> > > This is a note to let you know that I've just added the patch titled
> > >
> > > tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
> > >
> > > to my tty git tree which can be found at
> > > git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> > > in the tty-linus branch.
> >
> > If it's not too late, I was wondering if you could drop this patch and the
> > subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts
> > don't wake the"), in favor of the second version of this series that was
> > just posted at
> >
> > http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
> >
> > If it is too late, we'll deal with it in 3.4.
>
> What is wrong with the patches that I applied?
A new workaround is used that reduces the number of interrupts to normal.
The commit messages are improved since we have a better idea of what was
wrong. There is also a new patch (patch 3) for a power management
regression in the driver.
> How about a fix-up patch on top of what I have applied instead of whole
> new ones?
That's fine, if that's your preference. It will be several patches,
though. And about 75% of the previous series would be reverted, since a
different workaround would be used.
Let me know if that is indeed what you'd like.
regards
- Paul
^ permalink raw reply
* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Greg KH @ 2012-01-26 4:21 UTC (permalink / raw)
To: Paul Walmsley
Cc: govindraj.r, khilman, tomi.valkeinen, linux-omap,
linux-arm-kernel, linux-serial
In-Reply-To: <alpine.DEB.2.00.1201251156361.5324@utopia.booyaka.com>
On Wed, Jan 25, 2012 at 08:02:09PM -0700, Paul Walmsley wrote:
> cc lists
>
> Hi Greg
>
> On Tue, 24 Jan 2012, gregkh@suse.de wrote:
> >
> > This is a note to let you know that I've just added the patch titled
> >
> > tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
> >
> > to my tty git tree which can be found at
> > git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> > in the tty-linus branch.
>
> If it's not too late, I was wondering if you could drop this patch and the
> subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts
> don't wake the"), in favor of the second version of this series that was
> just posted at
>
> http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
>
> If it is too late, we'll deal with it in 3.4.
What is wrong with the patches that I applied? How about a fix-up patch
on top of what I have applied instead of whole new ones?
greg k-h
^ permalink raw reply
* Re: patch "tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA" added to tty tree
From: Paul Walmsley @ 2012-01-26 3:02 UTC (permalink / raw)
To: gregkh
Cc: govindraj.r, khilman, tomi.valkeinen, linux-omap,
linux-arm-kernel, linux-serial
In-Reply-To: <13274430881471@kroah.org>
cc lists
Hi Greg
On Tue, 24 Jan 2012, gregkh@suse.de wrote:
>
> This is a note to let you know that I've just added the patch titled
>
> tty: serial: OMAP: ensure FIFO levels are set correctly in non-DMA
>
> to my tty git tree which can be found at
> git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty.git
> in the tty-linus branch.
If it's not too late, I was wondering if you could drop this patch and the
subsequent one ("tty: serial: OMAP: transmit FIFO threshold interrupts
don't wake the"), in favor of the second version of this series that was
just posted at
http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
If it is too late, we'll deal with it in 3.4.
thanks,
- Paul
^ permalink raw reply
* Re: 3.3-rc1 console lag (was: Re: [PATCH v8 00/20] OMAP2+: UART: Runtime adaptation + cleanup)
From: Paul Walmsley @ 2012-01-26 3:00 UTC (permalink / raw)
To: Ramirez Luna, Omar
Cc: Tony Lindgren, Kevin Hilman, Govindraj Raja, linux-omap,
linux-serial, linux-arm-kernel, Partha Basak, Vishwanath Sripathy,
Rajendra Nayak, Santosh Shilimkar
In-Reply-To: <CAB-zwWg4RS4J1YCcbfMWjLSCcV=oHqk9vaUKKKr0WjnTEuOmxQ@mail.gmail.com>
On Wed, 25 Jan 2012, Ramirez Luna, Omar wrote:
> Hi,
>
> On Fri, Nov 11, 2011 at 3:57 AM, Govindraj.R <govindraj.raja@ti.com> wrote:
> > Converting uart driver to adapt to pm runtime API's.
> > Code re-org + cleanup.
> > Moving some functionality from serial.c to omap-serial.c
> ...
> >
> > Ensure CONFIG_OMAP_PRM is set while testing irq_chaining with uart.
> > And for pm_qos usage ensure CONFIG_CPU_IDLE is selected other wise
> > console might be sluggish.
>
> There is console lag for omap2plus_defconfig given that
> CONFIG_CPU_IDLE is not enabled. Is the intention to force CPU_IDLE
> into the defconfig or find an alternative for the new pm_qos when cpu
> idle is disabled.
>
> Seen on beagle-xm and 3.3-rc1.
Try this
http://marc.info/?l=linux-arm-kernel&m=132754676814391&w=2
- Paul
^ permalink raw reply
* Re: [PATCH 2/3] tty: serial: OMAP: block idle while the UART is transferring data in PIO mode
From: Paul Walmsley @ 2012-01-26 2:58 UTC (permalink / raw)
To: linux-omap, linux-serial, linux-arm-kernel
Cc: govindraj.r, Kevin Hilman, Govindraj.R, Greg Kroah-Hartman,
Alan Cox, Tomi Valkeinen
In-Reply-To: <20120126025036.31613.67819.stgit@dusk>
Hi
The subject line on this patch should have been
"[PATCH v2 2/3] tty: serial: OMAP: block idle while the UART is
transferring data in PIO mode"
- Paul
^ permalink raw reply
* [PATCH v2 3/3] tty: serial: omap-serial: wakeup latency constraint is in microseconds, not milliseconds
From: Paul Walmsley @ 2012-01-26 2:50 UTC (permalink / raw)
To: linux-omap, linux-serial, linux-arm-kernel
Cc: Kevin Hilman, Greg Kroah-Hartman, Govindraj.R, Tomi Valkeinen,
Alan Cox
In-Reply-To: <20120126024903.31613.24730.stgit@dusk>
The receive FIFO wakeup latency estimate in the omap-serial driver is
three orders of magnitude too small. This effectively prevents the
MPU from going to a low-power state when CONFIG_CPU_IDLE=y. This is a
major power management regression and masks some other FIFO-related
bugs in the driver.
Fix by correcting the most egregious problem in the RX wakeup latency
estimate. There are several other flaws in the estimator; these will
be fixed by a separate patch series intended for 3.4.
The difference in low-power states with this patch can be observed via
debugfs in pm_debug/count.
This estimate does not have any effect when CONFIG_CPU_IDLE=n.
Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Kevin Hilman <khilman@ti.com>
Cc: Govindraj.R <govindraj.raja@ti.com>
Cc: Tomi Valkeinen <tomi.valkeinen@ti.com>
Cc: Alan Cox <alan@linux.intel.com>
Cc: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/tty/serial/omap-serial.c | 3 +--
1 files changed, 1 insertions(+), 2 deletions(-)
diff --git a/drivers/tty/serial/omap-serial.c b/drivers/tty/serial/omap-serial.c
index 11fa156..72fa783 100644
--- a/drivers/tty/serial/omap-serial.c
+++ b/drivers/tty/serial/omap-serial.c
@@ -740,8 +740,7 @@ serial_omap_set_termios(struct uart_port *port, struct ktermios *termios,
quot = serial_omap_get_divisor(port, baud);
/* calculate wakeup latency constraint */
- up->calc_latency = (1000000 * up->port.fifosize) /
- (1000 * baud / 8);
+ up->calc_latency = (USEC_PER_SEC * up->port.fifosize) / (baud / 8);
up->latency = up->calc_latency;
schedule_work(&up->qos_work);
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox