* [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels
@ 2012-09-24 9:18 Bastian Hecht
2012-09-24 9:18 ` [PATCH v2 2/2] mtd: sh_flctl: Use DMA for data fifo FLTDFIFO when available Bastian Hecht
2012-09-27 15:05 ` [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels Guennadi Liakhovetski
0 siblings, 2 replies; 7+ messages in thread
From: Bastian Hecht @ 2012-09-24 9:18 UTC (permalink / raw)
To: linux-mtd; +Cc: Magnus Damm, Guennadi Liakhovetski, linux-sh
The code probes if DMA channels can get allocated and tears them down at
removal/failure if needed.
Based on Guennadi Liakhovetski's code from the sh_mmcif driver.
Signed-off-by: Bastian Hecht <hechtb@gmail.com>
---
v2: incorporated Vikram Narayanan's thoughts
- added a missing "static".
- reordered things to get rid of a forward declaration
Based on l2-mtd with reverted commit e26c113b4130aefa1d8446602bb5b05cfd646bfe.
drivers/mtd/nand/sh_flctl.c | 74 ++++++++++++++++++++++++++++++++++++++++++
include/linux/mtd/sh_flctl.h | 10 ++++++
2 files changed, 84 insertions(+), 0 deletions(-)
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 4fbfe96..9659483 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -106,6 +106,76 @@ static void wait_completion(struct sh_flctl *flctl)
writeb(0x0, FLTRCR(flctl));
}
+static void flctl_release_dma(struct sh_flctl *flctl)
+{
+ if (flctl->chan_fifo0_rx) {
+ dma_release_channel(flctl->chan_fifo0_rx);
+ flctl->chan_fifo0_rx = NULL;
+ }
+ if (flctl->chan_fifo0_tx) {
+ dma_release_channel(flctl->chan_fifo0_tx);
+ flctl->chan_fifo0_tx = NULL;
+ }
+}
+
+static void flctl_setup_dma(struct sh_flctl *flctl)
+{
+ dma_cap_mask_t mask;
+ struct dma_slave_config cfg;
+ struct platform_device *pdev = flctl->pdev;
+ struct sh_flctl_platform_data *pdata = pdev->dev.platform_data;
+ int ret;
+
+ if (!pdata)
+ return;
+
+ if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0)
+ return;
+
+ /* We can only either use DMA for both Tx and Rx or not use it at all */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter,
+ (void *)pdata->slave_id_fifo0_tx);
+ dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__,
+ flctl->chan_fifo0_tx);
+
+ if (!flctl->chan_fifo0_tx)
+ return;
+
+ cfg.slave_id = pdata->slave_id_fifo0_tx;
+ cfg.direction = DMA_MEM_TO_DEV;
+ cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl);
+ cfg.src_addr = 0;
+ ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
+ if (ret < 0)
+ goto err;
+
+ flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter,
+ (void *)pdata->slave_id_fifo0_rx);
+ dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__,
+ flctl->chan_fifo0_rx);
+
+ if (!flctl->chan_fifo0_rx)
+ goto err;
+
+ cfg.slave_id = pdata->slave_id_fifo0_rx;
+ cfg.direction = DMA_DEV_TO_MEM;
+ cfg.dst_addr = 0;
+ cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl);
+ ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
+ if (ret < 0)
+ goto err;
+
+ init_completion(&flctl->dma_complete);
+
+ return;
+
+err:
+ flctl_release_dma(flctl);
+}
+
static void set_addr(struct mtd_info *mtd, int column, int page_addr)
{
struct sh_flctl *flctl = mtd_to_flctl(mtd);
@@ -930,6 +1000,8 @@ static int __devinit flctl_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
pm_runtime_resume(&pdev->dev);
+ flctl_setup_dma(flctl);
+
ret = nand_scan_ident(flctl_mtd, 1, NULL);
if (ret)
goto err_chip;
@@ -947,6 +1019,7 @@ static int __devinit flctl_probe(struct platform_device *pdev)
return 0;
err_chip:
+ flctl_release_dma(flctl);
pm_runtime_disable(&pdev->dev);
free_irq(irq, flctl);
err_flste:
@@ -960,6 +1033,7 @@ static int __devexit flctl_remove(struct platform_device *pdev)
{
struct sh_flctl *flctl = platform_get_drvdata(pdev);
+ flctl_release_dma(flctl);
nand_release(&flctl->mtd);
pm_runtime_disable(&pdev->dev);
free_irq(platform_get_irq(pdev, 0), flctl);
diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
index 01e4b15..20d3f48 100644
--- a/include/linux/mtd/sh_flctl.h
+++ b/include/linux/mtd/sh_flctl.h
@@ -20,10 +20,12 @@
#ifndef __SH_FLCTL_H__
#define __SH_FLCTL_H__
+#include <linux/dmaengine.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/pm_qos.h>
+#include <linux/sh_dma.h>
/* FLCTL registers */
#define FLCMNCR(f) (f->reg + 0x0)
@@ -161,6 +163,11 @@ struct sh_flctl {
unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */
unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */
unsigned qos_request:1; /* QoS request to prevent deep power shutdown */
+
+ /* DMA related objects */
+ struct dma_chan *chan_fifo0_rx;
+ struct dma_chan *chan_fifo0_tx;
+ struct completion dma_complete;
};
struct sh_flctl_platform_data {
@@ -170,6 +177,9 @@ struct sh_flctl_platform_data {
unsigned has_hwecc:1;
unsigned use_holden:1;
+
+ unsigned int slave_id_fifo0_tx;
+ unsigned int slave_id_fifo0_rx;
};
static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo)
--
1.7.5.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH v2 2/2] mtd: sh_flctl: Use DMA for data fifo FLTDFIFO when available
2012-09-24 9:18 [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels Bastian Hecht
@ 2012-09-24 9:18 ` Bastian Hecht
2012-09-27 15:28 ` Guennadi Liakhovetski
2012-09-27 15:05 ` [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels Guennadi Liakhovetski
1 sibling, 1 reply; 7+ messages in thread
From: Bastian Hecht @ 2012-09-24 9:18 UTC (permalink / raw)
To: linux-mtd; +Cc: Magnus Damm, Guennadi Liakhovetski, linux-sh
Map and unmap DMA buffers, trigger the DMA and wait for the completion.
On failure we fallback to PIO mode.
Signed-off-by: Bastian Hecht <hechtb@gmail.com>
---
log v2: dropped a forward declaration
drivers/mtd/nand/sh_flctl.c | 97 +++++++++++++++++++++++++++++++++++++++++-
include/linux/mtd/sh_flctl.h | 1 +
2 files changed, 96 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
index 9659483..0d90af8 100644
--- a/drivers/mtd/nand/sh_flctl.c
+++ b/drivers/mtd/nand/sh_flctl.c
@@ -24,6 +24,8 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/platform_device.h>
@@ -106,6 +108,13 @@ static void wait_completion(struct sh_flctl *flctl)
writeb(0x0, FLTRCR(flctl));
}
+static void flctl_dma_complete(void *param)
+{
+ struct sh_flctl *flctl = param;
+
+ complete(&flctl->dma_complete);
+}
+
static void flctl_release_dma(struct sh_flctl *flctl)
{
if (flctl->chan_fifo0_rx) {
@@ -331,6 +340,69 @@ static void wait_wecfifo_ready(struct sh_flctl *flctl)
timeout_error(flctl, __func__);
}
+static void flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
+ int len, enum dma_data_direction dir)
+{
+ struct dma_async_tx_descriptor *desc = NULL;
+ struct dma_chan *chan;
+ enum dma_transfer_direction tr_dir;
+ dma_addr_t dma_addr;
+ dma_cookie_t cookie = -EINVAL;
+ uint32_t reg;
+ int ret;
+
+ if (dir = DMA_FROM_DEVICE) {
+ chan = flctl->chan_fifo0_rx;
+ tr_dir = DMA_DEV_TO_MEM;
+ } else {
+ chan = flctl->chan_fifo0_tx;
+ tr_dir = DMA_MEM_TO_DEV;
+ }
+
+ dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
+
+ if (dma_addr)
+ desc = dmaengine_prep_slave_single(chan, dma_addr, len,
+ tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+
+ if (desc) {
+ reg = readl(FLINTDMACR(flctl));
+ reg |= DREQ0EN;
+ writel(reg, FLINTDMACR(flctl));
+
+ desc->callback = flctl_dma_complete;
+ desc->callback_param = flctl;
+ cookie = dmaengine_submit(desc);
+
+ dma_async_issue_pending(chan);
+ }
+
+ if (!desc) {
+ /* DMA failed, fall back to PIO */
+ flctl_release_dma(flctl);
+ dev_warn(&flctl->pdev->dev,
+ "DMA failed, falling back to PIO\n");
+ goto out;
+ }
+
+ ret + wait_for_completion_timeout(&flctl->dma_complete,
+ msecs_to_jiffies(3000));
+
+ if (ret <= 0) {
+ chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
+ dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
+ }
+
+out:
+ reg = readl(FLINTDMACR(flctl));
+ reg &= ~DREQ0EN;
+ writel(reg, FLINTDMACR(flctl));
+
+ dma_unmap_single(chan->device->dev, dma_addr, len, dir);
+ init_completion(&flctl->dma_complete);
+}
+
static void read_datareg(struct sh_flctl *flctl, int offset)
{
unsigned long data;
@@ -349,6 +421,16 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
len_4align = (rlen + 3) / 4;
+ /* initiate DMA transfer */
+ if (flctl->chan_fifo0_rx && rlen >= 32) {
+ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM);
+ for (i = 0; i < len_4align; i++)
+ buf[i] = be32_to_cpu(buf[i]);
+
+ return;
+ }
+
+ /* do polling transfer */
for (i = 0; i < len_4align; i++) {
wait_rfifo_ready(flctl);
buf[i] = readl(FLDTFIFO(flctl));
@@ -378,13 +460,24 @@ static enum flctl_ecc_res_t read_ecfiforeg
static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
{
int i, len_4align;
- unsigned long *data = (unsigned long *)&flctl->done_buff[offset];
+ unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
void *fifo_addr = (void *)FLDTFIFO(flctl);
len_4align = (rlen + 3) / 4;
+
+ /* initiate DMA transfer */
+ if (flctl->chan_fifo0_tx && rlen >= 32) {
+ for (i = 0; i < len_4align; i++)
+ buf[i] = cpu_to_be32(buf[i]);
+
+ flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV);
+ return;
+ }
+
+ /* do polling transfer */
for (i = 0; i < len_4align; i++) {
wait_wfifo_ready(flctl);
- writel(cpu_to_be32(data[i]), fifo_addr);
+ writel(cpu_to_be32(buf[i]), fifo_addr);
}
}
diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
index 20d3f48..d55ec25 100644
--- a/include/linux/mtd/sh_flctl.h
+++ b/include/linux/mtd/sh_flctl.h
@@ -109,6 +109,7 @@
#define ESTERINTE (0x1 << 24) /* ECC error interrupt enable */
#define AC1CLR (0x1 << 19) /* ECC FIFO clear */
#define AC0CLR (0x1 << 18) /* Data FIFO clear */
+#define DREQ0EN (0x1 << 16) /* FLDTFIFODMA Request Enable */
#define ECERB (0x1 << 9) /* ECC error */
#define STERB (0x1 << 8) /* Status error */
#define STERINTE (0x1 << 4) /* Status error enable */
--
1.7.5.4
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels
2012-09-24 9:18 [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels Bastian Hecht
2012-09-24 9:18 ` [PATCH v2 2/2] mtd: sh_flctl: Use DMA for data fifo FLTDFIFO when available Bastian Hecht
@ 2012-09-27 15:05 ` Guennadi Liakhovetski
2012-09-28 9:45 ` Bastian Hecht
1 sibling, 1 reply; 7+ messages in thread
From: Guennadi Liakhovetski @ 2012-09-27 15:05 UTC (permalink / raw)
To: Bastian Hecht; +Cc: Magnus Damm, linux-mtd, linux-sh
Hi Bastian
On Mon, 24 Sep 2012, Bastian Hecht wrote:
> The code probes if DMA channels can get allocated and tears them down at
> removal/failure if needed.
I'm not sure it makes sense to break this code into two patches - the
first one makes no sense without the second one.
>
> Based on Guennadi Liakhovetski's code from the sh_mmcif driver.
>
> Signed-off-by: Bastian Hecht <hechtb@gmail.com>
> ---
> v2: incorporated Vikram Narayanan's thoughts
> - added a missing "static".
> - reordered things to get rid of a forward declaration
>
> Based on l2-mtd with reverted commit e26c113b4130aefa1d8446602bb5b05cfd646bfe.
>
> drivers/mtd/nand/sh_flctl.c | 74 ++++++++++++++++++++++++++++++++++++++++++
> include/linux/mtd/sh_flctl.h | 10 ++++++
> 2 files changed, 84 insertions(+), 0 deletions(-)
>
> diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
> index 4fbfe96..9659483 100644
> --- a/drivers/mtd/nand/sh_flctl.c
> +++ b/drivers/mtd/nand/sh_flctl.c
> @@ -106,6 +106,76 @@ static void wait_completion(struct sh_flctl *flctl)
> writeb(0x0, FLTRCR(flctl));
> }
>
> +static void flctl_release_dma(struct sh_flctl *flctl)
> +{
> + if (flctl->chan_fifo0_rx) {
> + dma_release_channel(flctl->chan_fifo0_rx);
You add dmaengine API calls to the file, so, you have to include
header(s). dma_release_channel() requires dmaengine.h.
> + flctl->chan_fifo0_rx = NULL;
> + }
> + if (flctl->chan_fifo0_tx) {
> + dma_release_channel(flctl->chan_fifo0_tx);
> + flctl->chan_fifo0_tx = NULL;
> + }
> +}
> +
> +static void flctl_setup_dma(struct sh_flctl *flctl)
> +{
> + dma_cap_mask_t mask;
> + struct dma_slave_config cfg;
> + struct platform_device *pdev = flctl->pdev;
> + struct sh_flctl_platform_data *pdata = pdev->dev.platform_data;
> + int ret;
> +
> + if (!pdata)
> + return;
> +
> + if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0)
> + return;
> +
> + /* We can only either use DMA for both Tx and Rx or not use it at all */
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_SLAVE, mask);
> +
> + flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter,
shdma_chan_filter() requires sh_dma.h. And no, including these headers in
your header is not quite correct.
> + (void *)pdata->slave_id_fifo0_tx);
> + dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__,
> + flctl->chan_fifo0_tx);
> +
> + if (!flctl->chan_fifo0_tx)
> + return;
> +
> + cfg.slave_id = pdata->slave_id_fifo0_tx;
> + cfg.direction = DMA_MEM_TO_DEV;
> + cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl);
> + cfg.src_addr = 0;
> + ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
Ok, you copied my mistake here:-) "cfg" is allocated on stack and never
initialised. You set those its members, that are needed by the sh-dma
driver, but it can change at any time to begin using further fields from
struct dma_slave_config, so, it has to be zeroed first. E.g., you could do
+ struct dma_slave_config cfg = {.device_fc = false,};
> + if (ret < 0)
> + goto err;
> +
> + flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter,
> + (void *)pdata->slave_id_fifo0_rx);
> + dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__,
> + flctl->chan_fifo0_rx);
> +
> + if (!flctl->chan_fifo0_rx)
> + goto err;
> +
> + cfg.slave_id = pdata->slave_id_fifo0_rx;
> + cfg.direction = DMA_DEV_TO_MEM;
> + cfg.dst_addr = 0;
> + cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl);
> + ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
> + if (ret < 0)
> + goto err;
> +
> + init_completion(&flctl->dma_complete);
> +
> + return;
> +
> +err:
> + flctl_release_dma(flctl);
> +}
> +
> static void set_addr(struct mtd_info *mtd, int column, int page_addr)
> {
> struct sh_flctl *flctl = mtd_to_flctl(mtd);
> @@ -930,6 +1000,8 @@ static int __devinit flctl_probe(struct platform_device *pdev)
> pm_runtime_enable(&pdev->dev);
> pm_runtime_resume(&pdev->dev);
>
> + flctl_setup_dma(flctl);
> +
> ret = nand_scan_ident(flctl_mtd, 1, NULL);
> if (ret)
> goto err_chip;
> @@ -947,6 +1019,7 @@ static int __devinit flctl_probe(struct platform_device *pdev)
> return 0;
>
> err_chip:
> + flctl_release_dma(flctl);
> pm_runtime_disable(&pdev->dev);
> free_irq(irq, flctl);
> err_flste:
> @@ -960,6 +1033,7 @@ static int __devexit flctl_remove(struct platform_device *pdev)
> {
> struct sh_flctl *flctl = platform_get_drvdata(pdev);
>
> + flctl_release_dma(flctl);
> nand_release(&flctl->mtd);
> pm_runtime_disable(&pdev->dev);
> free_irq(platform_get_irq(pdev, 0), flctl);
> diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
> index 01e4b15..20d3f48 100644
> --- a/include/linux/mtd/sh_flctl.h
> +++ b/include/linux/mtd/sh_flctl.h
> @@ -20,10 +20,12 @@
> #ifndef __SH_FLCTL_H__
> #define __SH_FLCTL_H__
>
> +#include <linux/dmaengine.h>
> #include <linux/mtd/mtd.h>
> #include <linux/mtd/nand.h>
> #include <linux/mtd/partitions.h>
> #include <linux/pm_qos.h>
> +#include <linux/sh_dma.h>
Here headers aren't actually needed - you can just forward-declare "struct
dma_chan." OTOH, what _is_ indeed needed is <linux/completion.h>.
>
> /* FLCTL registers */
> #define FLCMNCR(f) (f->reg + 0x0)
> @@ -161,6 +163,11 @@ struct sh_flctl {
> unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */
> unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */
> unsigned qos_request:1; /* QoS request to prevent deep power shutdown */
> +
> + /* DMA related objects */
> + struct dma_chan *chan_fifo0_rx;
> + struct dma_chan *chan_fifo0_tx;
> + struct completion dma_complete;
> };
>
> struct sh_flctl_platform_data {
> @@ -170,6 +177,9 @@ struct sh_flctl_platform_data {
>
> unsigned has_hwecc:1;
> unsigned use_holden:1;
> +
> + unsigned int slave_id_fifo0_tx;
> + unsigned int slave_id_fifo0_rx;
> };
>
> static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo)
> --
> 1.7.5.4
>
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 2/2] mtd: sh_flctl: Use DMA for data fifo FLTDFIFO when available
2012-09-24 9:18 ` [PATCH v2 2/2] mtd: sh_flctl: Use DMA for data fifo FLTDFIFO when available Bastian Hecht
@ 2012-09-27 15:28 ` Guennadi Liakhovetski
0 siblings, 0 replies; 7+ messages in thread
From: Guennadi Liakhovetski @ 2012-09-27 15:28 UTC (permalink / raw)
To: Bastian Hecht; +Cc: Magnus Damm, Linus Walleij, linux-mtd, linux-sh
On Mon, 24 Sep 2012, Bastian Hecht wrote:
> Map and unmap DMA buffers, trigger the DMA and wait for the completion.
> On failure we fallback to PIO mode.
>
> Signed-off-by: Bastian Hecht <hechtb@gmail.com>
> ---
> log v2: dropped a forward declaration
>
> drivers/mtd/nand/sh_flctl.c | 97 +++++++++++++++++++++++++++++++++++++++++-
> include/linux/mtd/sh_flctl.h | 1 +
> 2 files changed, 96 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
> index 9659483..0d90af8 100644
> --- a/drivers/mtd/nand/sh_flctl.c
> +++ b/drivers/mtd/nand/sh_flctl.c
> @@ -24,6 +24,8 @@
> #include <linux/module.h>
> #include <linux/kernel.h>
> #include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
As I suggested in my comments to patch 1/2, I would merge these patches.
In any case the headers are needed already in the first patch, including
sh_dma.h.
Thanks
Guennadi
> #include <linux/interrupt.h>
> #include <linux/io.h>
> #include <linux/platform_device.h>
> @@ -106,6 +108,13 @@ static void wait_completion(struct sh_flctl *flctl)
> writeb(0x0, FLTRCR(flctl));
> }
>
> +static void flctl_dma_complete(void *param)
> +{
> + struct sh_flctl *flctl = param;
> +
> + complete(&flctl->dma_complete);
> +}
> +
> static void flctl_release_dma(struct sh_flctl *flctl)
> {
> if (flctl->chan_fifo0_rx) {
> @@ -331,6 +340,69 @@ static void wait_wecfifo_ready(struct sh_flctl *flctl)
> timeout_error(flctl, __func__);
> }
>
> +static void flctl_dma_fifo0_transfer(struct sh_flctl *flctl, unsigned long *buf,
> + int len, enum dma_data_direction dir)
> +{
> + struct dma_async_tx_descriptor *desc = NULL;
> + struct dma_chan *chan;
> + enum dma_transfer_direction tr_dir;
> + dma_addr_t dma_addr;
> + dma_cookie_t cookie = -EINVAL;
> + uint32_t reg;
> + int ret;
> +
> + if (dir = DMA_FROM_DEVICE) {
> + chan = flctl->chan_fifo0_rx;
> + tr_dir = DMA_DEV_TO_MEM;
> + } else {
> + chan = flctl->chan_fifo0_tx;
> + tr_dir = DMA_MEM_TO_DEV;
> + }
> +
> + dma_addr = dma_map_single(chan->device->dev, buf, len, dir);
> +
> + if (dma_addr)
> + desc = dmaengine_prep_slave_single(chan, dma_addr, len,
> + tr_dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
> +
> + if (desc) {
> + reg = readl(FLINTDMACR(flctl));
> + reg |= DREQ0EN;
> + writel(reg, FLINTDMACR(flctl));
> +
> + desc->callback = flctl_dma_complete;
> + desc->callback_param = flctl;
> + cookie = dmaengine_submit(desc);
> +
> + dma_async_issue_pending(chan);
> + }
> +
> + if (!desc) {
> + /* DMA failed, fall back to PIO */
> + flctl_release_dma(flctl);
> + dev_warn(&flctl->pdev->dev,
> + "DMA failed, falling back to PIO\n");
> + goto out;
> + }
> +
> + ret > + wait_for_completion_timeout(&flctl->dma_complete,
> + msecs_to_jiffies(3000));
> +
> + if (ret <= 0) {
> + chan->device->device_control(chan, DMA_TERMINATE_ALL, 0);
> + dev_err(&flctl->pdev->dev, "wait_for_completion_timeout\n");
> + }
> +
> +out:
> + reg = readl(FLINTDMACR(flctl));
> + reg &= ~DREQ0EN;
> + writel(reg, FLINTDMACR(flctl));
> +
> + dma_unmap_single(chan->device->dev, dma_addr, len, dir);
> + init_completion(&flctl->dma_complete);
> +}
> +
> static void read_datareg(struct sh_flctl *flctl, int offset)
> {
> unsigned long data;
> @@ -349,6 +421,16 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
>
> len_4align = (rlen + 3) / 4;
>
> + /* initiate DMA transfer */
> + if (flctl->chan_fifo0_rx && rlen >= 32) {
> + flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_DEV_TO_MEM);
> + for (i = 0; i < len_4align; i++)
> + buf[i] = be32_to_cpu(buf[i]);
> +
> + return;
> + }
> +
> + /* do polling transfer */
> for (i = 0; i < len_4align; i++) {
> wait_rfifo_ready(flctl);
> buf[i] = readl(FLDTFIFO(flctl));
> @@ -378,13 +460,24 @@ static enum flctl_ecc_res_t read_ecfiforeg
> static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
> {
> int i, len_4align;
> - unsigned long *data = (unsigned long *)&flctl->done_buff[offset];
> + unsigned long *buf = (unsigned long *)&flctl->done_buff[offset];
> void *fifo_addr = (void *)FLDTFIFO(flctl);
>
> len_4align = (rlen + 3) / 4;
> +
> + /* initiate DMA transfer */
> + if (flctl->chan_fifo0_tx && rlen >= 32) {
> + for (i = 0; i < len_4align; i++)
> + buf[i] = cpu_to_be32(buf[i]);
> +
> + flctl_dma_fifo0_transfer(flctl, buf, rlen, DMA_MEM_TO_DEV);
> + return;
> + }
> +
> + /* do polling transfer */
> for (i = 0; i < len_4align; i++) {
> wait_wfifo_ready(flctl);
> - writel(cpu_to_be32(data[i]), fifo_addr);
> + writel(cpu_to_be32(buf[i]), fifo_addr);
> }
> }
>
> diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
> index 20d3f48..d55ec25 100644
> --- a/include/linux/mtd/sh_flctl.h
> +++ b/include/linux/mtd/sh_flctl.h
> @@ -109,6 +109,7 @@
> #define ESTERINTE (0x1 << 24) /* ECC error interrupt enable */
> #define AC1CLR (0x1 << 19) /* ECC FIFO clear */
> #define AC0CLR (0x1 << 18) /* Data FIFO clear */
> +#define DREQ0EN (0x1 << 16) /* FLDTFIFODMA Request Enable */
> #define ECERB (0x1 << 9) /* ECC error */
> #define STERB (0x1 << 8) /* Status error */
> #define STERINTE (0x1 << 4) /* Status error enable */
> --
> 1.7.5.4
>
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels
2012-09-27 15:05 ` [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels Guennadi Liakhovetski
@ 2012-09-28 9:45 ` Bastian Hecht
2012-09-28 9:54 ` Guennadi Liakhovetski
0 siblings, 1 reply; 7+ messages in thread
From: Bastian Hecht @ 2012-09-28 9:45 UTC (permalink / raw)
To: Guennadi Liakhovetski; +Cc: Vikram Narayanan, Magnus Damm, linux-mtd, linux-sh
Thanks for the reviews, Guennadi! Thanks too to Vikram - I've forgot
to add you as recipient for the v2, so I pull you in here now.
2012/9/27 Guennadi Liakhovetski <g.liakhovetski@gmx.de>:
> Hi Bastian
>
> On Mon, 24 Sep 2012, Bastian Hecht wrote:
>
>> The code probes if DMA channels can get allocated and tears them down at
>> removal/failure if needed.
>
> I'm not sure it makes sense to break this code into two patches - the
> first one makes no sense without the second one.
Ok, will merge them.
>>
>> Based on Guennadi Liakhovetski's code from the sh_mmcif driver.
>>
>> Signed-off-by: Bastian Hecht <hechtb@gmail.com>
>> ---
>> v2: incorporated Vikram Narayanan's thoughts
>> - added a missing "static".
>> - reordered things to get rid of a forward declaration
>>
>> Based on l2-mtd with reverted commit e26c113b4130aefa1d8446602bb5b05cfd646bfe.
>>
>> drivers/mtd/nand/sh_flctl.c | 74 ++++++++++++++++++++++++++++++++++++++++++
>> include/linux/mtd/sh_flctl.h | 10 ++++++
>> 2 files changed, 84 insertions(+), 0 deletions(-)
>>
>> diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c
>> index 4fbfe96..9659483 100644
>> --- a/drivers/mtd/nand/sh_flctl.c
>> +++ b/drivers/mtd/nand/sh_flctl.c
>> @@ -106,6 +106,76 @@ static void wait_completion(struct sh_flctl *flctl)
>> writeb(0x0, FLTRCR(flctl));
>> }
>>
>> +static void flctl_release_dma(struct sh_flctl *flctl)
>> +{
>> + if (flctl->chan_fifo0_rx) {
>> + dma_release_channel(flctl->chan_fifo0_rx);
>
> You add dmaengine API calls to the file, so, you have to include
> header(s). dma_release_channel() requires dmaengine.h.
Ok.
>> + flctl->chan_fifo0_rx = NULL;
>> + }
>> + if (flctl->chan_fifo0_tx) {
>> + dma_release_channel(flctl->chan_fifo0_tx);
>> + flctl->chan_fifo0_tx = NULL;
>> + }
>> +}
>> +
>> +static void flctl_setup_dma(struct sh_flctl *flctl)
>> +{
>> + dma_cap_mask_t mask;
>> + struct dma_slave_config cfg;
>> + struct platform_device *pdev = flctl->pdev;
>> + struct sh_flctl_platform_data *pdata = pdev->dev.platform_data;
>> + int ret;
>> +
>> + if (!pdata)
>> + return;
>> +
>> + if (pdata->slave_id_fifo0_tx <= 0 || pdata->slave_id_fifo0_rx <= 0)
>> + return;
>> +
>> + /* We can only either use DMA for both Tx and Rx or not use it at all */
>> + dma_cap_zero(mask);
>> + dma_cap_set(DMA_SLAVE, mask);
>> +
>> + flctl->chan_fifo0_tx = dma_request_channel(mask, shdma_chan_filter,
>
> shdma_chan_filter() requires sh_dma.h. And no, including these headers in
> your header is not quite correct.
Ok.
>> + (void *)pdata->slave_id_fifo0_tx);
>> + dev_dbg(&pdev->dev, "%s: TX: got channel %p\n", __func__,
>> + flctl->chan_fifo0_tx);
>> +
>> + if (!flctl->chan_fifo0_tx)
>> + return;
>> +
>> + cfg.slave_id = pdata->slave_id_fifo0_tx;
>> + cfg.direction = DMA_MEM_TO_DEV;
>> + cfg.dst_addr = (dma_addr_t)FLDTFIFO(flctl);
>> + cfg.src_addr = 0;
>> + ret = dmaengine_slave_config(flctl->chan_fifo0_tx, &cfg);
>
> Ok, you copied my mistake here:-) "cfg" is allocated on stack and never
> initialised. You set those its members, that are needed by the sh-dma
> driver, but it can change at any time to begin using further fields from
> struct dma_slave_config, so, it has to be zeroed first. E.g., you could do
>
> + struct dma_slave_config cfg = {.device_fc = false,};
I think I'll use memset. Taking a member like .device_fc may be a bit
misleading. We are not interested in it and leave it on the default
value. And we have no other field we could initialize that won't be
changed in the function.
>> + if (ret < 0)
>> + goto err;
>> +
>> + flctl->chan_fifo0_rx = dma_request_channel(mask, shdma_chan_filter,
>> + (void *)pdata->slave_id_fifo0_rx);
>> + dev_dbg(&pdev->dev, "%s: RX: got channel %p\n", __func__,
>> + flctl->chan_fifo0_rx);
>> +
>> + if (!flctl->chan_fifo0_rx)
>> + goto err;
>> +
>> + cfg.slave_id = pdata->slave_id_fifo0_rx;
>> + cfg.direction = DMA_DEV_TO_MEM;
>> + cfg.dst_addr = 0;
>> + cfg.src_addr = (dma_addr_t)FLDTFIFO(flctl);
>> + ret = dmaengine_slave_config(flctl->chan_fifo0_rx, &cfg);
>> + if (ret < 0)
>> + goto err;
>> +
>> + init_completion(&flctl->dma_complete);
>> +
>> + return;
>> +
>> +err:
>> + flctl_release_dma(flctl);
>> +}
>> +
>> static void set_addr(struct mtd_info *mtd, int column, int page_addr)
>> {
>> struct sh_flctl *flctl = mtd_to_flctl(mtd);
>> @@ -930,6 +1000,8 @@ static int __devinit flctl_probe(struct platform_device *pdev)
>> pm_runtime_enable(&pdev->dev);
>> pm_runtime_resume(&pdev->dev);
>>
>> + flctl_setup_dma(flctl);
>> +
>> ret = nand_scan_ident(flctl_mtd, 1, NULL);
>> if (ret)
>> goto err_chip;
>> @@ -947,6 +1019,7 @@ static int __devinit flctl_probe(struct platform_device *pdev)
>> return 0;
>>
>> err_chip:
>> + flctl_release_dma(flctl);
>> pm_runtime_disable(&pdev->dev);
>> free_irq(irq, flctl);
>> err_flste:
>> @@ -960,6 +1033,7 @@ static int __devexit flctl_remove(struct platform_device *pdev)
>> {
>> struct sh_flctl *flctl = platform_get_drvdata(pdev);
>>
>> + flctl_release_dma(flctl);
>> nand_release(&flctl->mtd);
>> pm_runtime_disable(&pdev->dev);
>> free_irq(platform_get_irq(pdev, 0), flctl);
>> diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
>> index 01e4b15..20d3f48 100644
>> --- a/include/linux/mtd/sh_flctl.h
>> +++ b/include/linux/mtd/sh_flctl.h
>> @@ -20,10 +20,12 @@
>> #ifndef __SH_FLCTL_H__
>> #define __SH_FLCTL_H__
>>
>> +#include <linux/dmaengine.h>
>> #include <linux/mtd/mtd.h>
>> #include <linux/mtd/nand.h>
>> #include <linux/mtd/partitions.h>
>> #include <linux/pm_qos.h>
>> +#include <linux/sh_dma.h>
>
> Here headers aren't actually needed - you can just forward-declare "struct
> dma_chan." OTOH, what _is_ indeed needed is <linux/completion.h>.
Oh yes, I'll add <linux/completion.h>.
About the forward declration: Do I really need it? We use a struct
pointer and I am unsure if the compiler just needs to know the name
and nothing more. I've tested it and included sh_flctl.h (without any
DMA headers) in some driver that has no idea about DMA.
gcc didn't complain. Adding a non-pointer member
+ struct sh_dma test;
lead to an error, so struct sh_dma is really unknown.
>>
>> /* FLCTL registers */
>> #define FLCMNCR(f) (f->reg + 0x0)
>> @@ -161,6 +163,11 @@ struct sh_flctl {
>> unsigned hwecc:1; /* Hardware ECC (0 = disabled, 1 = enabled) */
>> unsigned holden:1; /* Hardware has FLHOLDCR and HOLDEN is set */
>> unsigned qos_request:1; /* QoS request to prevent deep power shutdown */
>> +
>> + /* DMA related objects */
>> + struct dma_chan *chan_fifo0_rx;
>> + struct dma_chan *chan_fifo0_tx;
>> + struct completion dma_complete;
>> };
>>
>> struct sh_flctl_platform_data {
>> @@ -170,6 +177,9 @@ struct sh_flctl_platform_data {
>>
>> unsigned has_hwecc:1;
>> unsigned use_holden:1;
>> +
>> + unsigned int slave_id_fifo0_tx;
>> + unsigned int slave_id_fifo0_rx;
>> };
>>
>> static inline struct sh_flctl *mtd_to_flctl(struct mtd_info *mtdinfo)
>> --
>> 1.7.5.4
>>
>
> Thanks
> Guennadi
> ---
> Guennadi Liakhovetski, Ph.D.
> Freelance Open-Source Software Developer
> http://www.open-technology.de/
> --
> To unsubscribe from this list: send the line "unsubscribe linux-sh" 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 [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels
2012-09-28 9:45 ` Bastian Hecht
@ 2012-09-28 9:54 ` Guennadi Liakhovetski
2012-09-29 11:54 ` Bastian Hecht
0 siblings, 1 reply; 7+ messages in thread
From: Guennadi Liakhovetski @ 2012-09-28 9:54 UTC (permalink / raw)
To: Bastian Hecht; +Cc: Vikram Narayanan, Magnus Damm, linux-mtd, linux-sh
On Fri, 28 Sep 2012, Bastian Hecht wrote:
[snip]
> >> diff --git a/include/linux/mtd/sh_flctl.h b/include/linux/mtd/sh_flctl.h
> >> index 01e4b15..20d3f48 100644
> >> --- a/include/linux/mtd/sh_flctl.h
> >> +++ b/include/linux/mtd/sh_flctl.h
> >> @@ -20,10 +20,12 @@
> >> #ifndef __SH_FLCTL_H__
> >> #define __SH_FLCTL_H__
> >>
> >> +#include <linux/dmaengine.h>
> >> #include <linux/mtd/mtd.h>
> >> #include <linux/mtd/nand.h>
> >> #include <linux/mtd/partitions.h>
> >> #include <linux/pm_qos.h>
> >> +#include <linux/sh_dma.h>
> >
> > Here headers aren't actually needed - you can just forward-declare "struct
> > dma_chan." OTOH, what _is_ indeed needed is <linux/completion.h>.
>
> Oh yes, I'll add <linux/completion.h>.
> About the forward declration: Do I really need it? We use a struct
> pointer and I am unsure if the compiler just needs to know the name
> and nothing more. I've tested it and included sh_flctl.h (without any
> DMA headers) in some driver that has no idea about DMA.
> gcc didn't complain.
I'm not sure about this, I think, sometimes there might be cases, when gcc
doesn't complain. Or (more likely) the header is pulled in via another
one, or some other header has this forward-declaration. But in any case I
wouldn't rely on either of those and declare what's needed explicitly.
> Adding a non-pointer member
> + struct sh_dma test;
> lead to an error, so struct sh_dma is really unknown.
Of course, the compiler has to know the size of the struct to allocate
space for it:-)
Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels
2012-09-28 9:54 ` Guennadi Liakhovetski
@ 2012-09-29 11:54 ` Bastian Hecht
0 siblings, 0 replies; 7+ messages in thread
From: Bastian Hecht @ 2012-09-29 11:54 UTC (permalink / raw)
To: Guennadi Liakhovetski; +Cc: Vikram Narayanan, Magnus Damm, linux-mtd, linux-sh
2012/9/28 Guennadi Liakhovetski <g.liakhovetski@gmx.de>:
> On Fri, 28 Sep 2012, Bastian Hecht wrote:
>
> [snip]
>
>> Oh yes, I'll add <linux/completion.h>.
>> About the forward declration: Do I really need it? We use a struct
>> pointer and I am unsure if the compiler just needs to know the name
>> and nothing more. I've tested it and included sh_flctl.h (without any
>> DMA headers) in some driver that has no idea about DMA.
>> gcc didn't complain.
>
> I'm not sure about this, I think, sometimes there might be cases, when gcc
> doesn't complain. Or (more likely) the header is pulled in via another
> one, or some other header has this forward-declaration. But in any case I
> wouldn't rely on either of those and declare what's needed explicitly.
>
>> Adding a non-pointer member
>> + struct sh_dma test;
>> lead to an error, so struct sh_dma is really unknown.
>
> Of course, the compiler has to know the size of the struct to allocate
> space for it:-)
>
Ah sure - I thought I catch any header pull-ins by this test but yeah
- it can be just forward declarations too...
So I will post a merged v3 under the new title: "mtd: sh_flctl: Add
DMA capabilty".
It includes the critics from here plus 2 other things:
- "Fall back to PIO" now actually falls back for the current read
operation and not just for future reads
- I removed the init_completion() after each DMA read. The completion
gets set up at probe time and what I read on the internet and in
researched in the sources we don't need to reset it then after each
usage.
thanks,
Bastian
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2012-09-29 11:54 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2012-09-24 9:18 [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels Bastian Hecht
2012-09-24 9:18 ` [PATCH v2 2/2] mtd: sh_flctl: Use DMA for data fifo FLTDFIFO when available Bastian Hecht
2012-09-27 15:28 ` Guennadi Liakhovetski
2012-09-27 15:05 ` [PATCH v2 1/2] mtd: sh_flctl: Setup and release DMA channels Guennadi Liakhovetski
2012-09-28 9:45 ` Bastian Hecht
2012-09-28 9:54 ` Guennadi Liakhovetski
2012-09-29 11:54 ` Bastian Hecht
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).