* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 7:20 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-17 7:20 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet
Cc: nicolas.ferre, hong.xu
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 160 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 158 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..ba59913 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,21 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ int dma_ready;
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +163,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +177,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +191,123 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK))
+ goto err_dma;
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0)
+ goto err_dma;
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ if (is_read)
+ phys_addr = dma_map_single(dma_dev->dev, p, len,
+ DMA_FROM_DEVICE);
+ else
+ phys_addr = dma_map_single(dma_dev->dev, p, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ if (is_read)
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
+err_dma:
+ /* Fall back to CPU I/O */
+ return -1;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (host->dma_ready && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (host->dma_ready && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +528,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -516,6 +648,24 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ host->dma_ready = 0;
+ } else
+ host->dma_ready = 1;
+ }
+ if (host->dma_ready)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
@@ -555,6 +705,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +730,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 7:20 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-17 7:20 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet
Cc: hong.xu, nicolas.ferre
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 160 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 158 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..ba59913 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,21 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ int dma_ready;
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +163,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +177,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +191,123 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK))
+ goto err_dma;
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0)
+ goto err_dma;
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ if (is_read)
+ phys_addr = dma_map_single(dma_dev->dev, p, len,
+ DMA_FROM_DEVICE);
+ else
+ phys_addr = dma_map_single(dma_dev->dev, p, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ if (is_read)
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
+err_dma:
+ /* Fall back to CPU I/O */
+ return -1;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (host->dma_ready && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (host->dma_ready && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +528,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -516,6 +648,24 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ host->dma_ready = 0;
+ } else
+ host->dma_ready = 1;
+ }
+ if (host->dma_ready)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
@@ -555,6 +705,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +730,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-17 7:20 ` Hong Xu
(?)
@ 2011-01-17 20:15 ` Ryan Mallon
-1 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 20:15 UTC (permalink / raw)
To: Hong Xu
Cc: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, nicolas.ferre
On 01/17/2011 08:20 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Hi Hong,
A few comments below.
~Ryan
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> ---
> drivers/mtd/nand/atmel_nand.c | 160 ++++++++++++++++++++++++++++++++++++++++-
> 1 files changed, 158 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..ba59913 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,21 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + int dma_ready;
The name dma_ready implies that it is used as a flag to indicate when
the dma engine is ready to do a transfer, but you are using it to
indicate that the host supports dma. Would be more clear to call it
'use_dma'.
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +163,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +177,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +191,123 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
You can make the function below a bit more concise:
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
int dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf >= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf & PAGE_MASK) !=
> + ((size_t)(buf + len - 1) & PAGE_MASK))
Print a warning here? The other failures all have warning messages.
> + goto err_dma;
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0)
> + goto err_dma;
> + p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + if (is_read)
> + phys_addr = dma_map_single(dma_dev->dev, p, len,
> + DMA_FROM_DEVICE);
> + else
> + phys_addr = dma_map_single(dma_dev->dev, p, len,
> + DMA_TO_DEVICE);
phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_dma;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> + return 0;
> +
> +err:
> + if (is_read)
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
> + else
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_dma:
> + /* Fall back to CPU I/O */
> + return -1;
Return a proper error code.
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (host->dma_ready && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (host->dma_ready && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
In atmel_nand_probe you have the following:
if (host->board->bus_width_16) { /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
nand_chip->read_buf = atmel_read_buf16;
nand_chip->write_buf = atmel_write_buf16;
} else {
nand_chip->read_buf = atmel_read_buf;
nand_chip->write_buf = atmel_write_buf;
}
So DMA is not supported when the bus width is 16? Possibly you just want
to use atmel_[read|write]_buf in all cases?
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +528,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
host->io_phys can be replaced by a stack variable in atmel_nand_probe,
or just (dma_addr_t)mem->start, since it is only used in this function.
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -516,6 +648,24 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> }
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
Nitpick: don't have blank lines between a function call and its error check.
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + host->dma_ready = 0;
> + } else
> + host->dma_ready = 1;
Nitpick: the else should have braces since the if statement does.
> + }
> + if (host->dma_ready)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* second phase scan */
> if (nand_scan_tail(mtd)) {
> res = -ENXIO;
> @@ -555,6 +705,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +730,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 20:15 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 20:15 UTC (permalink / raw)
To: Hong Xu; +Cc: nicolas.ferre, linux-kernel, linux-mtd, jamie, linux-arm-kernel
On 01/17/2011 08:20 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Hi Hong,
A few comments below.
~Ryan
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> ---
> drivers/mtd/nand/atmel_nand.c | 160 ++++++++++++++++++++++++++++++++++++++++-
> 1 files changed, 158 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..ba59913 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,21 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + int dma_ready;
The name dma_ready implies that it is used as a flag to indicate when
the dma engine is ready to do a transfer, but you are using it to
indicate that the host supports dma. Would be more clear to call it
'use_dma'.
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +163,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +177,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +191,123 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
You can make the function below a bit more concise:
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
int dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf >= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf & PAGE_MASK) !=
> + ((size_t)(buf + len - 1) & PAGE_MASK))
Print a warning here? The other failures all have warning messages.
> + goto err_dma;
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0)
> + goto err_dma;
> + p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + if (is_read)
> + phys_addr = dma_map_single(dma_dev->dev, p, len,
> + DMA_FROM_DEVICE);
> + else
> + phys_addr = dma_map_single(dma_dev->dev, p, len,
> + DMA_TO_DEVICE);
phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_dma;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> + return 0;
> +
> +err:
> + if (is_read)
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
> + else
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_dma:
> + /* Fall back to CPU I/O */
> + return -1;
Return a proper error code.
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (host->dma_ready && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (host->dma_ready && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
In atmel_nand_probe you have the following:
if (host->board->bus_width_16) { /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
nand_chip->read_buf = atmel_read_buf16;
nand_chip->write_buf = atmel_write_buf16;
} else {
nand_chip->read_buf = atmel_read_buf;
nand_chip->write_buf = atmel_write_buf;
}
So DMA is not supported when the bus width is 16? Possibly you just want
to use atmel_[read|write]_buf in all cases?
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +528,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
host->io_phys can be replaced by a stack variable in atmel_nand_probe,
or just (dma_addr_t)mem->start, since it is only used in this function.
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -516,6 +648,24 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> }
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
Nitpick: don't have blank lines between a function call and its error check.
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + host->dma_ready = 0;
> + } else
> + host->dma_ready = 1;
Nitpick: the else should have braces since the if statement does.
> + }
> + if (host->dma_ready)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* second phase scan */
> if (nand_scan_tail(mtd)) {
> res = -ENXIO;
> @@ -555,6 +705,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +730,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 20:15 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 20:15 UTC (permalink / raw)
To: linux-arm-kernel
On 01/17/2011 08:20 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Hi Hong,
A few comments below.
~Ryan
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> ---
> drivers/mtd/nand/atmel_nand.c | 160 ++++++++++++++++++++++++++++++++++++++++-
> 1 files changed, 158 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..ba59913 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,21 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + int dma_ready;
The name dma_ready implies that it is used as a flag to indicate when
the dma engine is ready to do a transfer, but you are using it to
indicate that the host supports dma. Would be more clear to call it
'use_dma'.
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +163,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +177,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +191,123 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
You can make the function below a bit more concise:
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
int dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf >= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf & PAGE_MASK) !=
> + ((size_t)(buf + len - 1) & PAGE_MASK))
Print a warning here? The other failures all have warning messages.
> + goto err_dma;
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0)
> + goto err_dma;
> + p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + if (is_read)
> + phys_addr = dma_map_single(dma_dev->dev, p, len,
> + DMA_FROM_DEVICE);
> + else
> + phys_addr = dma_map_single(dma_dev->dev, p, len,
> + DMA_TO_DEVICE);
phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_dma;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> + return 0;
> +
> +err:
> + if (is_read)
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
> + else
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_dma:
> + /* Fall back to CPU I/O */
> + return -1;
Return a proper error code.
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (host->dma_ready && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (host->dma_ready && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
In atmel_nand_probe you have the following:
if (host->board->bus_width_16) { /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
nand_chip->read_buf = atmel_read_buf16;
nand_chip->write_buf = atmel_write_buf16;
} else {
nand_chip->read_buf = atmel_read_buf;
nand_chip->write_buf = atmel_write_buf;
}
So DMA is not supported when the bus width is 16? Possibly you just want
to use atmel_[read|write]_buf in all cases?
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +528,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
host->io_phys can be replaced by a stack variable in atmel_nand_probe,
or just (dma_addr_t)mem->start, since it is only used in this function.
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -516,6 +648,24 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> }
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
Nitpick: don't have blank lines between a function call and its error check.
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + host->dma_ready = 0;
> + } else
> + host->dma_ready = 1;
Nitpick: the else should have braces since the if statement does.
> + }
> + if (host->dma_ready)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* second phase scan */
> if (nand_scan_tail(mtd)) {
> res = -ENXIO;
> @@ -555,6 +705,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +730,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-17 7:20 ` Hong Xu
(?)
@ 2011-01-17 21:35 ` Ryan Mallon
-1 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 21:35 UTC (permalink / raw)
To: Hong Xu
Cc: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, nicolas.ferre
On 01/17/2011 08:20 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> ---
One more comment:
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + host->dma_ready = 0;
Because host is kzalloc'ed above, you do not need to set host->dma_ready
to zero here.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 21:35 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 21:35 UTC (permalink / raw)
To: Hong Xu; +Cc: nicolas.ferre, linux-kernel, linux-mtd, jamie, linux-arm-kernel
On 01/17/2011 08:20 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> ---
One more comment:
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + host->dma_ready = 0;
Because host is kzalloc'ed above, you do not need to set host->dma_ready
to zero here.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 21:35 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 21:35 UTC (permalink / raw)
To: linux-arm-kernel
On 01/17/2011 08:20 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
> ---
One more comment:
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + host->dma_ready = 0;
Because host is kzalloc'ed above, you do not need to set host->dma_ready
to zero here.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-17 20:15 ` Ryan Mallon
(?)
@ 2011-01-17 22:42 ` Ryan Mallon
-1 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 22:42 UTC (permalink / raw)
To: Hong Xu
Cc: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, nicolas.ferre
On 01/18/2011 09:15 AM, Ryan Mallon wrote:
> On 01/17/2011 08:20 PM, Hong Xu wrote:
>> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
>> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
I'm trying to patch this into a 2.6.33 kernel running on a custom
SAM9G45 based board, but I get a failure requesting the DMA channel:
root@snapper:~$ dmesg | grep -i dma
[ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
[ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
[ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ), 8 channels
It looks like the registration for the DMA controller happens after the
NAND driver probe and so the request is failing. I had a quick look, but
I can't see anything that would change this in more recent kernels. Any
ideas?
Also, I think you want to add the following for the atmel_nand Kconfig:
select AT_HDMAC
or make the DMA parts of the driver ifdef CONFIG_AT_HDMAC (if the above
won't work for some reason) since you will get build errors on the
dmaengine functions otherwise.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 22:42 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 22:42 UTC (permalink / raw)
To: Hong Xu; +Cc: nicolas.ferre, linux-kernel, linux-mtd, jamie, linux-arm-kernel
On 01/18/2011 09:15 AM, Ryan Mallon wrote:
> On 01/17/2011 08:20 PM, Hong Xu wrote:
>> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
>> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
I'm trying to patch this into a 2.6.33 kernel running on a custom
SAM9G45 based board, but I get a failure requesting the DMA channel:
root@snapper:~$ dmesg | grep -i dma
[ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
[ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
[ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ), 8 channels
It looks like the registration for the DMA controller happens after the
NAND driver probe and so the request is failing. I had a quick look, but
I can't see anything that would change this in more recent kernels. Any
ideas?
Also, I think you want to add the following for the atmel_nand Kconfig:
select AT_HDMAC
or make the DMA parts of the driver ifdef CONFIG_AT_HDMAC (if the above
won't work for some reason) since you will get build errors on the
dmaengine functions otherwise.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 22:42 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-17 22:42 UTC (permalink / raw)
To: linux-arm-kernel
On 01/18/2011 09:15 AM, Ryan Mallon wrote:
> On 01/17/2011 08:20 PM, Hong Xu wrote:
>> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
>> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
I'm trying to patch this into a 2.6.33 kernel running on a custom
SAM9G45 based board, but I get a failure requesting the DMA channel:
root at snapper:~$ dmesg | grep -i dma
[ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
[ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
[ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ), 8 channels
It looks like the registration for the DMA controller happens after the
NAND driver probe and so the request is failing. I had a quick look, but
I can't see anything that would change this in more recent kernels. Any
ideas?
Also, I think you want to add the following for the atmel_nand Kconfig:
select AT_HDMAC
or make the DMA parts of the driver ifdef CONFIG_AT_HDMAC (if the above
won't work for some reason) since you will get build errors on the
dmaengine functions otherwise.
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-17 22:42 ` Ryan Mallon
(?)
@ 2011-01-18 1:43 ` Xu, Hong
-1 siblings, 0 replies; 46+ messages in thread
From: Xu, Hong @ 2011-01-18 1:43 UTC (permalink / raw)
To: Ryan Mallon
Cc: linux-kernel, linux-mtd, linux-arm-kernel, jamie, jacmet, Nicolas Ferre
Hi Ryan,
> -----Original Message-----
> From: Ryan Mallon [mailto:ryan@bluewatersys.com]
> Sent: Tuesday, January 18, 2011 6:42 AM
> To: Xu, Hong
> Cc: linux-mtd@lists.infradead.org; linux-arm-kernel@lists.infradead.org;
> linux-kernel@vger.kernel.org; jamie@jamieiles.com; jacmet@sunsite.dk;
> Ferre, Nicolas
> Subject: Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
>
> root@snapper:~$ dmesg | grep -i dma
> [ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
> [ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
> [ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ),
> 8 channels
>
>
> It looks like the registration for the DMA controller happens after the
> NAND driver probe and so the request is failing. I had a quick look, but
> I can't see anything that would change this in more recent kernels. Any
> ideas?
>
You got the point. A [Git Pull] has been sent, see
http://article.gmane.org/gmane.linux.kernel/1088240/match=dmaengine+update
In this patch, subsys_initcall will be used so DMA engine will be initialized before NAND driver.
Thanks.
BR,
Hong
^ permalink raw reply [flat|nested] 46+ messages in thread
* RE: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 1:43 ` Xu, Hong
0 siblings, 0 replies; 46+ messages in thread
From: Xu, Hong @ 2011-01-18 1:43 UTC (permalink / raw)
To: Ryan Mallon
Cc: Nicolas Ferre, linux-kernel, linux-mtd, jamie, linux-arm-kernel
Hi Ryan,
> -----Original Message-----
> From: Ryan Mallon [mailto:ryan@bluewatersys.com]
> Sent: Tuesday, January 18, 2011 6:42 AM
> To: Xu, Hong
> Cc: linux-mtd@lists.infradead.org; linux-arm-kernel@lists.infradead.org;
> linux-kernel@vger.kernel.org; jamie@jamieiles.com; jacmet@sunsite.dk;
> Ferre, Nicolas
> Subject: Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
>
> root@snapper:~$ dmesg | grep -i dma
> [ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
> [ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
> [ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ),
> 8 channels
>
>
> It looks like the registration for the DMA controller happens after the
> NAND driver probe and so the request is failing. I had a quick look, but
> I can't see anything that would change this in more recent kernels. Any
> ideas?
>
You got the point. A [Git Pull] has been sent, see
http://article.gmane.org/gmane.linux.kernel/1088240/match=dmaengine+update
In this patch, subsys_initcall will be used so DMA engine will be initialized before NAND driver.
Thanks.
BR,
Hong
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 1:43 ` Xu, Hong
0 siblings, 0 replies; 46+ messages in thread
From: Xu, Hong @ 2011-01-18 1:43 UTC (permalink / raw)
To: linux-arm-kernel
Hi Ryan,
> -----Original Message-----
> From: Ryan Mallon [mailto:ryan at bluewatersys.com]
> Sent: Tuesday, January 18, 2011 6:42 AM
> To: Xu, Hong
> Cc: linux-mtd at lists.infradead.org; linux-arm-kernel at lists.infradead.org;
> linux-kernel at vger.kernel.org; jamie at jamieiles.com; jacmet at sunsite.dk;
> Ferre, Nicolas
> Subject: Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
>
> root at snapper:~$ dmesg | grep -i dma
> [ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
> [ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
> [ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ),
> 8 channels
>
>
> It looks like the registration for the DMA controller happens after the
> NAND driver probe and so the request is failing. I had a quick look, but
> I can't see anything that would change this in more recent kernels. Any
> ideas?
>
You got the point. A [Git Pull] has been sent, see
http://article.gmane.org/gmane.linux.kernel/1088240/match=dmaengine+update
In this patch, subsys_initcall will be used so DMA engine will be initialized before NAND driver.
Thanks.
BR,
Hong
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-18 1:43 ` Xu, Hong
(?)
@ 2011-01-18 2:44 ` Ryan Mallon
-1 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 2:44 UTC (permalink / raw)
To: Xu, Hong
Cc: linux-kernel, linux-mtd, linux-arm-kernel, jamie, jacmet, Nicolas Ferre
On 01/18/2011 02:43 PM, Xu, Hong wrote:
> Hi Ryan,
>> -----Original Message-----
>> From: Ryan Mallon [mailto:ryan@bluewatersys.com]
>> Sent: Tuesday, January 18, 2011 6:42 AM
>> To: Xu, Hong
>> Cc: linux-mtd@lists.infradead.org; linux-arm-kernel@lists.infradead.org;
>> linux-kernel@vger.kernel.org; jamie@jamieiles.com; jacmet@sunsite.dk;
>> Ferre, Nicolas
>> Subject: Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
>>
>> root@snapper:~$ dmesg | grep -i dma
>> [ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
>> [ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
>> [ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ),
>> 8 channels
>>
>>
>> It looks like the registration for the DMA controller happens after the
>> NAND driver probe and so the request is failing. I had a quick look, but
>> I can't see anything that would change this in more recent kernels. Any
>> ideas?
>>
> You got the point. A [Git Pull] has been sent, see
> http://article.gmane.org/gmane.linux.kernel/1088240/match=dmaengine+update
>
> In this patch, subsys_initcall will be used so DMA engine will be initialized before NAND driver.
> Thanks.
Thanks, that fixed it.
I have tried this under 2.6.33 on a custom SAM9G45 based board with a
512MiB NAND part, with 2kB pages and 128kB blocks using both raw MTD
access and the YAFFS2 filesystem and it appears to work correctly.
Tested-by: Ryan Mallon <ryan@bluewatersys.com>
Can you please update for the changes I suggested.
Thanks,
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 2:44 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 2:44 UTC (permalink / raw)
To: Xu, Hong; +Cc: Nicolas Ferre, linux-kernel, linux-mtd, jamie, linux-arm-kernel
On 01/18/2011 02:43 PM, Xu, Hong wrote:
> Hi Ryan,
>> -----Original Message-----
>> From: Ryan Mallon [mailto:ryan@bluewatersys.com]
>> Sent: Tuesday, January 18, 2011 6:42 AM
>> To: Xu, Hong
>> Cc: linux-mtd@lists.infradead.org; linux-arm-kernel@lists.infradead.org;
>> linux-kernel@vger.kernel.org; jamie@jamieiles.com; jacmet@sunsite.dk;
>> Ferre, Nicolas
>> Subject: Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
>>
>> root@snapper:~$ dmesg | grep -i dma
>> [ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
>> [ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
>> [ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ),
>> 8 channels
>>
>>
>> It looks like the registration for the DMA controller happens after the
>> NAND driver probe and so the request is failing. I had a quick look, but
>> I can't see anything that would change this in more recent kernels. Any
>> ideas?
>>
> You got the point. A [Git Pull] has been sent, see
> http://article.gmane.org/gmane.linux.kernel/1088240/match=dmaengine+update
>
> In this patch, subsys_initcall will be used so DMA engine will be initialized before NAND driver.
> Thanks.
Thanks, that fixed it.
I have tried this under 2.6.33 on a custom SAM9G45 based board with a
512MiB NAND part, with 2kB pages and 128kB blocks using both raw MTD
access and the YAFFS2 filesystem and it appears to work correctly.
Tested-by: Ryan Mallon <ryan@bluewatersys.com>
Can you please update for the changes I suggested.
Thanks,
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 2:44 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 2:44 UTC (permalink / raw)
To: linux-arm-kernel
On 01/18/2011 02:43 PM, Xu, Hong wrote:
> Hi Ryan,
>> -----Original Message-----
>> From: Ryan Mallon [mailto:ryan at bluewatersys.com]
>> Sent: Tuesday, January 18, 2011 6:42 AM
>> To: Xu, Hong
>> Cc: linux-mtd at lists.infradead.org; linux-arm-kernel at lists.infradead.org;
>> linux-kernel at vger.kernel.org; jamie at jamieiles.com; jacmet at sunsite.dk;
>> Ferre, Nicolas
>> Subject: Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
>>
>> root at snapper:~$ dmesg | grep -i dma
>> [ 0.850000] atmel_nand atmel_nand: Failed to request DMA channel
>> [ 0.860000] atmel_nand atmel_nand: No DMA support for NAND access.
>> [ 1.530000] at_hdmac at_hdmac: Atmel AHB DMA Controller ( cpy slave ),
>> 8 channels
>>
>>
>> It looks like the registration for the DMA controller happens after the
>> NAND driver probe and so the request is failing. I had a quick look, but
>> I can't see anything that would change this in more recent kernels. Any
>> ideas?
>>
> You got the point. A [Git Pull] has been sent, see
> http://article.gmane.org/gmane.linux.kernel/1088240/match=dmaengine+update
>
> In this patch, subsys_initcall will be used so DMA engine will be initialized before NAND driver.
> Thanks.
Thanks, that fixed it.
I have tried this under 2.6.33 on a custom SAM9G45 based board with a
512MiB NAND part, with 2kB pages and 128kB blocks using both raw MTD
access and the YAFFS2 filesystem and it appears to work correctly.
Tested-by: Ryan Mallon <ryan@bluewatersys.com>
Can you please update for the changes I suggested.
Thanks,
~Ryan
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 2:56 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 2:56 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, ryan
Cc: nicolas.ferre, hong.xu
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..02ad055 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ enum dma_data_direction dir = is_read? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer doesn't fit in one page:");
+ goto err_dma;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page:");
+ goto err_dma;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single:");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_dma:
+ dev_warn(host->dev, " Fall back to CPU I/O\n");
+ return -EIO;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 2:56 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 2:56 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, ryan
Cc: hong.xu, nicolas.ferre
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..02ad055 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ enum dma_data_direction dir = is_read? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer doesn't fit in one page:");
+ goto err_dma;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page:");
+ goto err_dma;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single:");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_dma:
+ dev_warn(host->dev, " Fall back to CPU I/O\n");
+ return -EIO;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-18 2:56 ` Hong Xu
(?)
@ 2011-01-18 3:08 ` Ryan Mallon
-1 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 3:08 UTC (permalink / raw)
To: Hong Xu
Cc: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, nicolas.ferre
On 01/18/2011 03:56 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
Couple more notes below. I think the normal exit path for
atmel_nand_dma_op needs to call dma_unmap_single. The other suggestions
below are just stylistic.
Also, you still need a change to Kconfig to add the dependency on the
AT_HDMAC driver otherwise users without that option selected will get
build errors.
~Ryan
> ---
> drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 157 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..02ad055 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
Nitpick, tab delimit these two fields to line up with everything else.
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
> + enum dma_data_direction dir = is_read? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf >= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf & PAGE_MASK) !=
> + ((size_t)(buf + len - 1) & PAGE_MASK)) {
> + dev_warn(host->dev, "Buffer doesn't fit in one page:");
> + goto err_dma;
> + }
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0) {
> + dev_err(host->dev, "Failed to vmalloc_to_page:");
> + goto err_dma;
> + }
> + p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> +
Nitpick, no blank line between function and error check.
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single:");
> + goto err_dma;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
Is dma_unmap_single required here?
> + return 0;
> +
> +err:
> + dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_dma:
> + dev_warn(host->dev, " Fall back to CPU I/O\n");
I'm not sure this will give the output you want, since you are calling
dev_err with no newline, followed by dev_warn, will it print something
like this:
"Failed to dma_map_single:<4> Fall back to CPU I/O"
Or does printk handle this auto-magically now? I do recall reading
something about printk's behaviour changing in this regard.
> + return -EIO;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>
> nand_chip->chip_delay = 20; /* 20us command delay time */
>
> - if (host->board->bus_width_16) { /* 16-bit bus width */
> + if (host->board->bus_width_16) /* 16-bit bus width */
> nand_chip->options |= NAND_BUSWIDTH_16;
> - nand_chip->read_buf = atmel_read_buf16;
> - nand_chip->write_buf = atmel_write_buf16;
> - } else {
> - nand_chip->read_buf = atmel_read_buf;
> - nand_chip->write_buf = atmel_write_buf;
> - }
> +
> + nand_chip->read_buf = atmel_read_buf;
> + nand_chip->write_buf = atmel_write_buf;
>
> platform_set_drvdata(pdev, host);
> atmel_nand_enable(host);
> @@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> nand_chip->options |= NAND_USE_FLASH_BBT;
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* first scan to find the device and get the page size */
> if (nand_scan_ident(mtd, 1, NULL)) {
> res = -ENXIO;
> @@ -555,6 +697,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 3:08 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 3:08 UTC (permalink / raw)
To: Hong Xu; +Cc: nicolas.ferre, linux-kernel, linux-mtd, jamie, linux-arm-kernel
On 01/18/2011 03:56 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
Couple more notes below. I think the normal exit path for
atmel_nand_dma_op needs to call dma_unmap_single. The other suggestions
below are just stylistic.
Also, you still need a change to Kconfig to add the dependency on the
AT_HDMAC driver otherwise users without that option selected will get
build errors.
~Ryan
> ---
> drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 157 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..02ad055 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
Nitpick, tab delimit these two fields to line up with everything else.
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
> + enum dma_data_direction dir = is_read? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf >= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf & PAGE_MASK) !=
> + ((size_t)(buf + len - 1) & PAGE_MASK)) {
> + dev_warn(host->dev, "Buffer doesn't fit in one page:");
> + goto err_dma;
> + }
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0) {
> + dev_err(host->dev, "Failed to vmalloc_to_page:");
> + goto err_dma;
> + }
> + p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> +
Nitpick, no blank line between function and error check.
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single:");
> + goto err_dma;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
Is dma_unmap_single required here?
> + return 0;
> +
> +err:
> + dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_dma:
> + dev_warn(host->dev, " Fall back to CPU I/O\n");
I'm not sure this will give the output you want, since you are calling
dev_err with no newline, followed by dev_warn, will it print something
like this:
"Failed to dma_map_single:<4> Fall back to CPU I/O"
Or does printk handle this auto-magically now? I do recall reading
something about printk's behaviour changing in this regard.
> + return -EIO;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>
> nand_chip->chip_delay = 20; /* 20us command delay time */
>
> - if (host->board->bus_width_16) { /* 16-bit bus width */
> + if (host->board->bus_width_16) /* 16-bit bus width */
> nand_chip->options |= NAND_BUSWIDTH_16;
> - nand_chip->read_buf = atmel_read_buf16;
> - nand_chip->write_buf = atmel_write_buf16;
> - } else {
> - nand_chip->read_buf = atmel_read_buf;
> - nand_chip->write_buf = atmel_write_buf;
> - }
> +
> + nand_chip->read_buf = atmel_read_buf;
> + nand_chip->write_buf = atmel_write_buf;
>
> platform_set_drvdata(pdev, host);
> atmel_nand_enable(host);
> @@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> nand_chip->options |= NAND_USE_FLASH_BBT;
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* first scan to find the device and get the page size */
> if (nand_scan_ident(mtd, 1, NULL)) {
> res = -ENXIO;
> @@ -555,6 +697,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan@bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 3:08 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 3:08 UTC (permalink / raw)
To: linux-arm-kernel
On 01/18/2011 03:56 PM, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
Couple more notes below. I think the normal exit path for
atmel_nand_dma_op needs to call dma_unmap_single. The other suggestions
below are just stylistic.
Also, you still need a change to Kconfig to add the dependency on the
AT_HDMAC driver otherwise users without that option selected will get
build errors.
~Ryan
> ---
> drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 157 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..02ad055 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
Nitpick, tab delimit these two fields to line up with everything else.
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
> + enum dma_data_direction dir = is_read? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf >= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf & PAGE_MASK) !=
> + ((size_t)(buf + len - 1) & PAGE_MASK)) {
> + dev_warn(host->dev, "Buffer doesn't fit in one page:");
> + goto err_dma;
> + }
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0) {
> + dev_err(host->dev, "Failed to vmalloc_to_page:");
> + goto err_dma;
> + }
> + p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> +
Nitpick, no blank line between function and error check.
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single:");
> + goto err_dma;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
Is dma_unmap_single required here?
> + return 0;
> +
> +err:
> + dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_dma:
> + dev_warn(host->dev, " Fall back to CPU I/O\n");
I'm not sure this will give the output you want, since you are calling
dev_err with no newline, followed by dev_warn, will it print something
like this:
"Failed to dma_map_single:<4> Fall back to CPU I/O"
Or does printk handle this auto-magically now? I do recall reading
something about printk's behaviour changing in this regard.
> + return -EIO;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>
> nand_chip->chip_delay = 20; /* 20us command delay time */
>
> - if (host->board->bus_width_16) { /* 16-bit bus width */
> + if (host->board->bus_width_16) /* 16-bit bus width */
> nand_chip->options |= NAND_BUSWIDTH_16;
> - nand_chip->read_buf = atmel_read_buf16;
> - nand_chip->write_buf = atmel_write_buf16;
> - } else {
> - nand_chip->read_buf = atmel_read_buf;
> - nand_chip->write_buf = atmel_write_buf;
> - }
> +
> + nand_chip->read_buf = atmel_read_buf;
> + nand_chip->write_buf = atmel_write_buf;
>
> platform_set_drvdata(pdev, host);
> atmel_nand_enable(host);
> @@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> nand_chip->options |= NAND_USE_FLASH_BBT;
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* first scan to find the device and get the page size */
> if (nand_scan_ident(mtd, 1, NULL)) {
> res = -ENXIO;
> @@ -555,6 +697,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
--
Bluewater Systems Ltd - ARM Technology Solution Centre
Ryan Mallon 5 Amuri Park, 404 Barbadoes St
ryan at bluewatersys.com PO Box 13 889, Christchurch 8013
http://www.bluewatersys.com New Zealand
Phone: +64 3 3779127 Freecall: Australia 1800 148 751
Fax: +64 3 3779135 USA 1800 261 2934
^ permalink raw reply [flat|nested] 46+ messages in thread
* 答复: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-18 3:08 ` Ryan Mallon
(?)
(?)
@ 2011-01-18 6:17 ` Xu, Hong
-1 siblings, 0 replies; 46+ messages in thread
From: Xu, Hong @ 2011-01-18 6:17 UTC (permalink / raw)
To: linux-arm-kernel
Hi Ryan,
Thanks for your careful review.
> -----Original Message-----
> From: Ryan Mallon [mailto:ryan at bluewatersys.com]
> Sent: Tuesday, January 18, 2011 11:08 AM
> To: Xu, Hong
> Cc: linux-mtd at lists.infradead.org; linux-arm-kernel at lists.infradead.org;
> linux-kernel at vger.kernel.org; jamie at jamieiles.com; jacmet at sunsite.dk;
> Ferre, Nicolas
> Subject: Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
>
> On 01/18/2011 03:56 PM, Hong Xu wrote:
> > Some SAM9 chips have the ability to perform DMA between CPU and SMC
> controller.
> > This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
> >
> > Signed-off-by: Hong Xu <hong.xu@atmel.com>
>
> Couple more notes below. I think the normal exit path for
> atmel_nand_dma_op needs to call dma_unmap_single. The other suggestions
> below are just stylistic.
>
> Also, you still need a change to Kconfig to add the dependency on the
> AT_HDMAC driver otherwise users without that option selected will get
> build errors.
>
Not all SAM9 chips have AT_HDMAC. I tried building the system successfully without
AT_HDMAC and even without DMADEVICES :-)
A new patch will be sent soon regarding to your other comments.
BR,
Hong
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20110118/f0bd08b8/attachment-0001.html>
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 6:36 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 6:36 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, ryan
Cc: nicolas.ferre, hong.xu
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..6fae04b 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ int err = -EIO;
+ enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer not fit in one page\n");
+ goto err_buf;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page\n");
+ goto err_buf;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
+ DMA_COMPL_SKIP_DEST_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_buf;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err_dma;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err_dma;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+ wait_for_completion(&host->comp);
+
+ err = 0;
+
+err_dma:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_buf:
+ if (err != 0)
+ dev_warn(host->dev, "Fall back to CPU I/O\n");
+ return err;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 6:36 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 6:36 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, ryan
Cc: hong.xu, nicolas.ferre
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..6fae04b 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ int err = -EIO;
+ enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer not fit in one page\n");
+ goto err_buf;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page\n");
+ goto err_buf;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
+ DMA_COMPL_SKIP_DEST_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_buf;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err_dma;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err_dma;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+ wait_for_completion(&host->comp);
+
+ err = 0;
+
+err_dma:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_buf:
+ if (err != 0)
+ dev_warn(host->dev, "Fall back to CPU I/O\n");
+ return err;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 6:36 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 6:36 UTC (permalink / raw)
To: linux-arm-kernel
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..6fae04b 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ int err = -EIO;
+ enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer not fit in one page\n");
+ goto err_buf;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page\n");
+ goto err_buf;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
+ DMA_COMPL_SKIP_DEST_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_buf;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err_dma;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err_dma;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+ wait_for_completion(&host->comp);
+
+ err = 0;
+
+err_dma:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_buf:
+ if (err != 0)
+ dev_warn(host->dev, "Fall back to CPU I/O\n");
+ return err;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-18 6:36 ` Hong Xu
(?)
@ 2011-01-18 9:06 ` Ryan Mallon
-1 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 9:06 UTC (permalink / raw)
To: Hong Xu
Cc: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, nicolas.ferre
On 18/01/11 19:36, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu<hong.xu@atmel.com>
Tested-by: Ryan Mallon <ryan@bluewatersys.com>
Acked-by: Ryan Mallon <ryan@bluewatersys.com>
> ---
> drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 157 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..6fae04b 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
> + int err = -EIO;
> + enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf>= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf& PAGE_MASK) !=
> + ((size_t)(buf + len - 1)& PAGE_MASK)) {
> + dev_warn(host->dev, "Buffer not fit in one page\n");
> + goto err_buf;
> + }
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0) {
> + dev_err(host->dev, "Failed to vmalloc_to_page\n");
> + goto err_buf;
> + }
> + p = page_address(pg) + ((size_t)buf& ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
> + DMA_COMPL_SKIP_DEST_UNMAP;
> +
> + phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_buf;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err_dma;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param =&host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err_dma;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> + wait_for_completion(&host->comp);
> +
> + err = 0;
> +
> +err_dma:
> + dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_buf:
> + if (err != 0)
> + dev_warn(host->dev, "Fall back to CPU I/O\n");
> + return err;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma&& len>= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma&& len>= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>
> nand_chip->chip_delay = 20; /* 20us command delay time */
>
> - if (host->board->bus_width_16) { /* 16-bit bus width */
> + if (host->board->bus_width_16) /* 16-bit bus width */
> nand_chip->options |= NAND_BUSWIDTH_16;
> - nand_chip->read_buf = atmel_read_buf16;
> - nand_chip->write_buf = atmel_write_buf16;
> - } else {
> - nand_chip->read_buf = atmel_read_buf;
> - nand_chip->write_buf = atmel_write_buf;
> - }
> +
> + nand_chip->read_buf = atmel_read_buf;
> + nand_chip->write_buf = atmel_write_buf;
>
> platform_set_drvdata(pdev, host);
> atmel_nand_enable(host);
> @@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> nand_chip->options |= NAND_USE_FLASH_BBT;
> }
>
> + if (cpu_has_dma()&& use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* first scan to find the device and get the page size */
> if (nand_scan_ident(mtd, 1, NULL)) {
> res = -ENXIO;
> @@ -555,6 +697,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 9:06 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-01-18 9:06 UTC (permalink / raw)
To: Hong Xu; +Cc: nicolas.ferre, linux-kernel, linux-mtd, jamie, linux-arm-kernel
On 18/01/11 19:36, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu<hong.xu@atmel.com>
Tested-by: Ryan Mallon <ryan@bluewatersys.com>
Acked-by: Ryan Mallon <ryan@bluewatersys.com>
> ---
> drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 157 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..6fae04b 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
> + int err = -EIO;
> + enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf>= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf& PAGE_MASK) !=
> + ((size_t)(buf + len - 1)& PAGE_MASK)) {
> + dev_warn(host->dev, "Buffer not fit in one page\n");
> + goto err_buf;
> + }
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0) {
> + dev_err(host->dev, "Failed to vmalloc_to_page\n");
> + goto err_buf;
> + }
> + p = page_address(pg) + ((size_t)buf& ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
> + DMA_COMPL_SKIP_DEST_UNMAP;
> +
> + phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_buf;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err_dma;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param =&host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err_dma;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> + wait_for_completion(&host->comp);
> +
> + err = 0;
> +
> +err_dma:
> + dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_buf:
> + if (err != 0)
> + dev_warn(host->dev, "Fall back to CPU I/O\n");
> + return err;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma&& len>= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma&& len>= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>
> nand_chip->chip_delay = 20; /* 20us command delay time */
>
> - if (host->board->bus_width_16) { /* 16-bit bus width */
> + if (host->board->bus_width_16) /* 16-bit bus width */
> nand_chip->options |= NAND_BUSWIDTH_16;
> - nand_chip->read_buf = atmel_read_buf16;
> - nand_chip->write_buf = atmel_write_buf16;
> - } else {
> - nand_chip->read_buf = atmel_read_buf;
> - nand_chip->write_buf = atmel_write_buf;
> - }
> +
> + nand_chip->read_buf = atmel_read_buf;
> + nand_chip->write_buf = atmel_write_buf;
>
> platform_set_drvdata(pdev, host);
> atmel_nand_enable(host);
> @@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> nand_chip->options |= NAND_USE_FLASH_BBT;
> }
>
> + if (cpu_has_dma()&& use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* first scan to find the device and get the page size */
> if (nand_scan_ident(mtd, 1, NULL)) {
> res = -ENXIO;
> @@ -555,6 +697,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-18 6:36 ` Hong Xu
(?)
@ 2011-01-21 11:23 ` Artem Bityutskiy
-1 siblings, 0 replies; 46+ messages in thread
From: Artem Bityutskiy @ 2011-01-21 11:23 UTC (permalink / raw)
To: Hong Xu
Cc: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, ryan,
nicolas.ferre
On Tue, 2011-01-18 at 14:36 +0800, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
Pushed to l2-mtd-2.6.git, thanks.
--
Best Regards,
Artem Bityutskiy (Битюцкий Артём)
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-21 11:23 ` Artem Bityutskiy
0 siblings, 0 replies; 46+ messages in thread
From: Artem Bityutskiy @ 2011-01-21 11:23 UTC (permalink / raw)
To: Hong Xu
Cc: nicolas.ferre, linux-kernel, ryan, linux-mtd, jamie, linux-arm-kernel
On Tue, 2011-01-18 at 14:36 +0800, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
Pushed to l2-mtd-2.6.git, thanks.
--
Best Regards,
Artem Bityutskiy (Битюцкий Артём)
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-21 11:23 ` Artem Bityutskiy
0 siblings, 0 replies; 46+ messages in thread
From: Artem Bityutskiy @ 2011-01-21 11:23 UTC (permalink / raw)
To: linux-arm-kernel
On Tue, 2011-01-18 at 14:36 +0800, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu <hong.xu@atmel.com>
Pushed to l2-mtd-2.6.git, thanks.
--
Best Regards,
Artem Bityutskiy (???????? ?????)
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 9:06 ` Ryan Mallon
0 siblings, 0 replies; 46+ messages in thread
From: Ryan Mallon @ 2011-07-08 15:36 UTC (permalink / raw)
To: linux-arm-kernel
On 18/01/11 19:36, Hong Xu wrote:
> Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
> This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
>
> Signed-off-by: Hong Xu<hong.xu@atmel.com>
Tested-by: Ryan Mallon <ryan@bluewatersys.com>
Acked-by: Ryan Mallon <ryan@bluewatersys.com>
> ---
> drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
> 1 files changed, 157 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..6fae04b 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,9 @@
> #define no_ecc 0
> #endif
>
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +92,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + dma_addr_t io_phys;
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> + void *p = buf;
> + int err = -EIO;
> + enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
> +
> + if (buf>= high_memory) {
> + struct page *pg;
> +
> + if (((size_t)buf& PAGE_MASK) !=
> + ((size_t)(buf + len - 1)& PAGE_MASK)) {
> + dev_warn(host->dev, "Buffer not fit in one page\n");
> + goto err_buf;
> + }
> +
> + pg = vmalloc_to_page(buf);
> + if (pg == 0) {
> + dev_err(host->dev, "Failed to vmalloc_to_page\n");
> + goto err_buf;
> + }
> + p = page_address(pg) + ((size_t)buf& ~PAGE_MASK);
> + }
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP |
> + DMA_COMPL_SKIP_DEST_UNMAP;
> +
> + phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
> + if (dma_mapping_error(dma_dev->dev, phys_addr)) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_buf;
> + }
> +
> + if (is_read) {
> + dma_src_addr = host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err_dma;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param =&host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err_dma;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> + wait_for_completion(&host->comp);
> +
> + err = 0;
> +
> +err_dma:
> + dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
> +err_buf:
> + if (err != 0)
> + dev_warn(host->dev, "Fall back to CPU I/O\n");
> + return err;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma&& len>= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma&& len>= mtd->oobsize)
> + if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
> + return;
> +
> + if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (dma_addr_t)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
>
> nand_chip->chip_delay = 20; /* 20us command delay time */
>
> - if (host->board->bus_width_16) { /* 16-bit bus width */
> + if (host->board->bus_width_16) /* 16-bit bus width */
> nand_chip->options |= NAND_BUSWIDTH_16;
> - nand_chip->read_buf = atmel_read_buf16;
> - nand_chip->write_buf = atmel_write_buf16;
> - } else {
> - nand_chip->read_buf = atmel_read_buf;
> - nand_chip->write_buf = atmel_write_buf;
> - }
> +
> + nand_chip->read_buf = atmel_read_buf;
> + nand_chip->write_buf = atmel_write_buf;
>
> platform_set_drvdata(pdev, host);
> atmel_nand_enable(host);
> @@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> nand_chip->options |= NAND_USE_FLASH_BBT;
> }
>
> + if (cpu_has_dma()&& use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* first scan to find the device and get the page size */
> if (nand_scan_ident(mtd, 1, NULL)) {
> res = -ENXIO;
> @@ -555,6 +697,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-17 7:20 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-07-08 16:43 UTC (permalink / raw)
To: linux-arm-kernel
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 160 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 158 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..ba59913 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,21 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ int dma_ready;
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +163,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +177,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +191,123 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK))
+ goto err_dma;
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0)
+ goto err_dma;
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ if (is_read)
+ phys_addr = dma_map_single(dma_dev->dev, p, len,
+ DMA_FROM_DEVICE);
+ else
+ phys_addr = dma_map_single(dma_dev->dev, p, len,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ if (is_read)
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
+err_dma:
+ /* Fall back to CPU I/O */
+ return -1;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (host->dma_ready && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (host->dma_ready && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +528,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -516,6 +648,24 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ host->dma_ready = 0;
+ } else
+ host->dma_ready = 1;
+ }
+ if (host->dma_ready)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
@@ -555,6 +705,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +730,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 2:56 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-07-08 17:32 UTC (permalink / raw)
To: linux-arm-kernel
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..02ad055 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ enum dma_data_direction dir = is_read? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer doesn't fit in one page:");
+ goto err_dma;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page:");
+ goto err_dma;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single:");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_dma:
+ dev_warn(host->dev, " Fall back to CPU I/O\n");
+ return -EIO;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
______________________________________________________
Linux MTD discussion mailing list
http://lists.infradead.org/mailman/listinfo/linux-mtd/
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 3:02 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 3:02 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, ryan
Cc: nicolas.ferre, hong.xu
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..248e0db 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer doesn't fit in one page:");
+ goto err_dma;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page:");
+ goto err_dma;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single:");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_dma:
+ dev_warn(host->dev, " Fall back to CPU I/O\n");
+ return -EIO;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 3:02 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 3:02 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel, jamie, jacmet, ryan
Cc: hong.xu, nicolas.ferre
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..248e0db 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer doesn't fit in one page:");
+ goto err_dma;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page:");
+ goto err_dma;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single:");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_dma:
+ dev_warn(host->dev, " Fall back to CPU I/O\n");
+ return -EIO;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-18 3:02 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-18 3:02 UTC (permalink / raw)
To: linux-arm-kernel
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 166 ++++++++++++++++++++++++++++++++++++++--
1 files changed, 157 insertions(+), 9 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..248e0db 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,9 @@
#define no_ecc 0
#endif
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +92,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ dma_addr_t io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +162,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +176,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +190,121 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static int atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+ void *p = buf;
+ enum dma_data_direction dir = is_read ? DMA_FROM_DEVICE : DMA_TO_DEVICE;
+
+ if (buf >= high_memory) {
+ struct page *pg;
+
+ if (((size_t)buf & PAGE_MASK) !=
+ ((size_t)(buf + len - 1) & PAGE_MASK)) {
+ dev_warn(host->dev, "Buffer doesn't fit in one page:");
+ goto err_dma;
+ }
+
+ pg = vmalloc_to_page(buf);
+ if (pg == 0) {
+ dev_err(host->dev, "Failed to vmalloc_to_page:");
+ goto err_dma;
+ }
+ p = page_address(pg) + ((size_t)buf & ~PAGE_MASK);
+ }
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ phys_addr = dma_map_single(dma_dev->dev, p, len, dir);
+
+ if (dma_mapping_error(dma_dev->dev, phys_addr)) {
+ dev_err(host->dev, "Failed to dma_map_single:");
+ goto err_dma;
+ }
+
+ if (is_read) {
+ dma_src_addr = host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+ return 0;
+
+err:
+ dma_unmap_single(dma_dev->dev, phys_addr, len, dir);
+err_dma:
+ dev_warn(host->dev, " Fall back to CPU I/O\n");
+ return -EIO;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, buf, len, 1) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= mtd->oobsize)
+ if (atmel_nand_dma_op(mtd, (void *)buf, len, 0) == 0)
+ return;
+
+ if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +525,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (dma_addr_t)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -448,14 +577,11 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->chip_delay = 20; /* 20us command delay time */
- if (host->board->bus_width_16) { /* 16-bit bus width */
+ if (host->board->bus_width_16) /* 16-bit bus width */
nand_chip->options |= NAND_BUSWIDTH_16;
- nand_chip->read_buf = atmel_read_buf16;
- nand_chip->write_buf = atmel_write_buf16;
- } else {
- nand_chip->read_buf = atmel_read_buf;
- nand_chip->write_buf = atmel_write_buf;
- }
+
+ nand_chip->read_buf = atmel_read_buf;
+ nand_chip->write_buf = atmel_write_buf;
platform_set_drvdata(pdev, host);
atmel_nand_enable(host);
@@ -473,6 +599,22 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
nand_chip->options |= NAND_USE_FLASH_BBT;
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* first scan to find the device and get the page size */
if (nand_scan_ident(mtd, 1, NULL)) {
res = -ENXIO;
@@ -555,6 +697,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +722,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-14 9:34 ` Hong Xu
(?)
@ 2011-01-14 11:43 ` Peter Korsgaard
-1 siblings, 0 replies; 46+ messages in thread
From: Peter Korsgaard @ 2011-01-14 11:43 UTC (permalink / raw)
To: Hong Xu; +Cc: linux-mtd, linux-arm-kernel, linux-kernel, nicolas.ferre
>>>>> "Hong" == Hong Xu <hong.xu@atmel.com> writes:
Hong> Some SAM9 chips have the ability to perform DMA between CPU and
Hong> SMC controller. This patch adds DMA support for SAM9RL, SAM9G45,
Hong> SSAM9G46,AM9M10, SAM9M11.
Hong> Signed-off-by: Hong Xu <hong.xu@atmel.com>
But mtd buffers are not guaranteed to be DMA safe (E.G. might come from
vmalloc), so you'll need to walk the page tables.
--
Bye, Peter Korsgaard
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-14 11:43 ` Peter Korsgaard
0 siblings, 0 replies; 46+ messages in thread
From: Peter Korsgaard @ 2011-01-14 11:43 UTC (permalink / raw)
To: Hong Xu; +Cc: nicolas.ferre, linux-mtd, linux-kernel, linux-arm-kernel
>>>>> "Hong" == Hong Xu <hong.xu@atmel.com> writes:
Hong> Some SAM9 chips have the ability to perform DMA between CPU and
Hong> SMC controller. This patch adds DMA support for SAM9RL, SAM9G45,
Hong> SSAM9G46,AM9M10, SAM9M11.
Hong> Signed-off-by: Hong Xu <hong.xu@atmel.com>
But mtd buffers are not guaranteed to be DMA safe (E.G. might come from
vmalloc), so you'll need to walk the page tables.
--
Bye, Peter Korsgaard
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-14 11:43 ` Peter Korsgaard
0 siblings, 0 replies; 46+ messages in thread
From: Peter Korsgaard @ 2011-01-14 11:43 UTC (permalink / raw)
To: linux-arm-kernel
>>>>> "Hong" == Hong Xu <hong.xu@atmel.com> writes:
Hong> Some SAM9 chips have the ability to perform DMA between CPU and
Hong> SMC controller. This patch adds DMA support for SAM9RL, SAM9G45,
Hong> SSAM9G46,AM9M10, SAM9M11.
Hong> Signed-off-by: Hong Xu <hong.xu@atmel.com>
But mtd buffers are not guaranteed to be DMA safe (E.G. might come from
vmalloc), so you'll need to walk the page tables.
--
Bye, Peter Korsgaard
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
2011-01-14 9:34 ` Hong Xu
(?)
@ 2011-01-14 10:00 ` Jamie Iles
-1 siblings, 0 replies; 46+ messages in thread
From: Jamie Iles @ 2011-01-14 10:00 UTC (permalink / raw)
To: Hong Xu; +Cc: linux-mtd, linux-arm-kernel, linux-kernel, nicolas.ferre
Hi Hong,
A couple of minor comments inline but otherwise looks good to me.
Jamie
On Fri, Jan 14, 2011 at 05:34:47PM +0800, Hong Xu wrote:
> diff --git a/drivers/mtd/nand/atmel_nand.c
> b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..c2fbffe 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,12 @@
> #define no_ecc 0
> #endif
>
> +/* DMA for large buffers only */
> +#define DMA_MIN_BYTES 512
> +
Is it worth making this a module parameter and defaulting to 512?
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +95,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + void __iomem *io_phys;
This should be of type dma_addr_t and this will eliminate some casting
later.
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +165,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +179,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +193,102 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static void atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + if (is_read)
> + phys_addr = dma_map_single(dma_dev->dev, buf, len,
> + DMA_FROM_DEVICE);
> + else
> + phys_addr = dma_map_single(dma_dev->dev, buf, len,
> + DMA_TO_DEVICE);
> + if (!phys_addr) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_dma_map;
> + }
I don't think this is right - you should check phys_addr() with
dma_mapping_error() rather than comparing to 0.
> +
> + if (is_read) {
> + dma_src_addr = (dma_addr_t)host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = (dma_addr_t)host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
> +
> +err:
> + if (is_read)
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
> + else
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
> +err_dma_map:
> + return;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= DMA_MIN_BYTES)
> + atmel_nand_dma_op(mtd, buf, len, 1);
> + else if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= DMA_MIN_BYTES)
> + atmel_nand_dma_op(mtd, (u8 *)buf, len, 0);
> + else if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +509,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (void __iomem *)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -516,6 +629,23 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> }
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* second phase scan */
> if (nand_scan_tail(mtd)) {
> res = -ENXIO;
> @@ -555,6 +685,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +710,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
> --
> 1.7.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 46+ messages in thread
* Re: [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-14 10:00 ` Jamie Iles
0 siblings, 0 replies; 46+ messages in thread
From: Jamie Iles @ 2011-01-14 10:00 UTC (permalink / raw)
To: Hong Xu; +Cc: nicolas.ferre, linux-mtd, linux-kernel, linux-arm-kernel
Hi Hong,
A couple of minor comments inline but otherwise looks good to me.
Jamie
On Fri, Jan 14, 2011 at 05:34:47PM +0800, Hong Xu wrote:
> diff --git a/drivers/mtd/nand/atmel_nand.c
> b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..c2fbffe 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,12 @@
> #define no_ecc 0
> #endif
>
> +/* DMA for large buffers only */
> +#define DMA_MIN_BYTES 512
> +
Is it worth making this a module parameter and defaulting to 512?
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +95,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + void __iomem *io_phys;
This should be of type dma_addr_t and this will eliminate some casting
later.
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +165,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +179,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +193,102 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static void atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + if (is_read)
> + phys_addr = dma_map_single(dma_dev->dev, buf, len,
> + DMA_FROM_DEVICE);
> + else
> + phys_addr = dma_map_single(dma_dev->dev, buf, len,
> + DMA_TO_DEVICE);
> + if (!phys_addr) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_dma_map;
> + }
I don't think this is right - you should check phys_addr() with
dma_mapping_error() rather than comparing to 0.
> +
> + if (is_read) {
> + dma_src_addr = (dma_addr_t)host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = (dma_addr_t)host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
> +
> +err:
> + if (is_read)
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
> + else
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
> +err_dma_map:
> + return;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= DMA_MIN_BYTES)
> + atmel_nand_dma_op(mtd, buf, len, 1);
> + else if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= DMA_MIN_BYTES)
> + atmel_nand_dma_op(mtd, (u8 *)buf, len, 0);
> + else if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +509,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (void __iomem *)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -516,6 +629,23 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> }
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* second phase scan */
> if (nand_scan_tail(mtd)) {
> res = -ENXIO;
> @@ -555,6 +685,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +710,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
> --
> 1.7.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-14 10:00 ` Jamie Iles
0 siblings, 0 replies; 46+ messages in thread
From: Jamie Iles @ 2011-01-14 10:00 UTC (permalink / raw)
To: linux-arm-kernel
Hi Hong,
A couple of minor comments inline but otherwise looks good to me.
Jamie
On Fri, Jan 14, 2011 at 05:34:47PM +0800, Hong Xu wrote:
> diff --git a/drivers/mtd/nand/atmel_nand.c
> b/drivers/mtd/nand/atmel_nand.c
> index ccce0f0..c2fbffe 100644
> --- a/drivers/mtd/nand/atmel_nand.c
> +++ b/drivers/mtd/nand/atmel_nand.c
> @@ -48,6 +48,12 @@
> #define no_ecc 0
> #endif
>
> +/* DMA for large buffers only */
> +#define DMA_MIN_BYTES 512
> +
Is it worth making this a module parameter and defaulting to 512?
> +static int use_dma = 1;
> +module_param(use_dma, int, 0);
> +
> static int on_flash_bbt = 0;
> module_param(on_flash_bbt, int, 0);
>
> @@ -89,11 +95,20 @@ struct atmel_nand_host {
> struct nand_chip nand_chip;
> struct mtd_info mtd;
> void __iomem *io_base;
> + void __iomem *io_phys;
This should be of type dma_addr_t and this will eliminate some casting
later.
> struct atmel_nand_data *board;
> struct device *dev;
> void __iomem *ecc;
> +
> + struct completion comp;
> + struct dma_chan *dma_chan;
> };
>
> +static int cpu_has_dma(void)
> +{
> + return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
> +}
> +
> /*
> * Enable NAND.
> */
> @@ -150,7 +165,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
> /*
> * Minimal-overhead PIO for data access.
> */
> -static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -164,7 +179,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
> __raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
> }
>
> -static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
> {
> struct nand_chip *nand_chip = mtd->priv;
>
> @@ -178,6 +193,102 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
> __raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
> }
>
> +static void dma_complete_func(void *completion)
> +{
> + complete(completion);
> +}
> +
> +static void atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
> + int is_read)
> +{
> + struct dma_device *dma_dev;
> + enum dma_ctrl_flags flags;
> + dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
> + struct dma_async_tx_descriptor *tx = NULL;
> + dma_cookie_t cookie;
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + dma_dev = host->dma_chan->device;
> +
> + flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
> + DMA_COMPL_SKIP_SRC_UNMAP;
> +
> + if (is_read)
> + phys_addr = dma_map_single(dma_dev->dev, buf, len,
> + DMA_FROM_DEVICE);
> + else
> + phys_addr = dma_map_single(dma_dev->dev, buf, len,
> + DMA_TO_DEVICE);
> + if (!phys_addr) {
> + dev_err(host->dev, "Failed to dma_map_single\n");
> + goto err_dma_map;
> + }
I don't think this is right - you should check phys_addr() with
dma_mapping_error() rather than comparing to 0.
> +
> + if (is_read) {
> + dma_src_addr = (dma_addr_t)host->io_phys;
> + dma_dst_addr = phys_addr;
> + } else {
> + dma_src_addr = phys_addr;
> + dma_dst_addr = (dma_addr_t)host->io_phys;
> + }
> +
> + tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
> + dma_src_addr, len, flags);
> + if (!tx) {
> + dev_err(host->dev, "Failed to prepare DMA memcpy\n");
> + goto err;
> + }
> +
> + init_completion(&host->comp);
> + tx->callback = dma_complete_func;
> + tx->callback_param = &host->comp;
> +
> + cookie = tx->tx_submit(tx);
> + if (dma_submit_error(cookie)) {
> + dev_err(host->dev, "Failed to do DMA tx_submit\n");
> + goto err;
> + }
> +
> + dma_async_issue_pending(host->dma_chan);
> +
> + wait_for_completion(&host->comp);
> +
> +err:
> + if (is_read)
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
> + else
> + dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
> +err_dma_map:
> + return;
> +}
> +
> +static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= DMA_MIN_BYTES)
> + atmel_nand_dma_op(mtd, buf, len, 1);
> + else if (host->board->bus_width_16)
> + atmel_read_buf16(mtd, buf, len);
> + else
> + atmel_read_buf8(mtd, buf, len);
> +}
> +
> +static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
> +{
> + struct nand_chip *chip = mtd->priv;
> + struct atmel_nand_host *host = chip->priv;
> +
> + if (use_dma && len >= DMA_MIN_BYTES)
> + atmel_nand_dma_op(mtd, (u8 *)buf, len, 0);
> + else if (host->board->bus_width_16)
> + atmel_write_buf16(mtd, buf, len);
> + else
> + atmel_write_buf8(mtd, buf, len);
> +}
> +
> /*
> * Calculate HW ECC
> *
> @@ -398,6 +509,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> return -ENOMEM;
> }
>
> + host->io_phys = (void __iomem *)mem->start;
> +
> host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
> if (host->io_base == NULL) {
> printk(KERN_ERR "atmel_nand: ioremap failed\n");
> @@ -516,6 +629,23 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
> }
> }
>
> + if (cpu_has_dma() && use_dma) {
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_MEMCPY, mask);
> + host->dma_chan = dma_request_channel(mask, 0, NULL);
> +
> + if (!host->dma_chan) {
> + dev_err(host->dev, "Failed to request DMA channel\n");
> + use_dma = 0;
> + }
> + }
> + if (use_dma)
> + dev_info(host->dev, "Using DMA for NAND access.\n");
> + else
> + dev_info(host->dev, "No DMA support for NAND access.\n");
> +
> /* second phase scan */
> if (nand_scan_tail(mtd)) {
> res = -ENXIO;
> @@ -555,6 +685,8 @@ err_scan_ident:
> err_no_card:
> atmel_nand_disable(host);
> platform_set_drvdata(pdev, NULL);
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> if (host->ecc)
> iounmap(host->ecc);
> err_ecc_ioremap:
> @@ -578,6 +710,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
>
> if (host->ecc)
> iounmap(host->ecc);
> +
> + if (host->dma_chan)
> + dma_release_channel(host->dma_chan);
> +
> iounmap(host->io_base);
> kfree(host);
>
> --
> 1.7.3.3
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-14 9:34 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-14 9:34 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel; +Cc: nicolas.ferre, hong.xu
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 140 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 138 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..c2fbffe 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,12 @@
#define no_ecc 0
#endif
+/* DMA for large buffers only */
+#define DMA_MIN_BYTES 512
+
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +95,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ void __iomem *io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +165,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +179,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +193,102 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static void atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ if (is_read)
+ phys_addr = dma_map_single(dma_dev->dev, buf, len,
+ DMA_FROM_DEVICE);
+ else
+ phys_addr = dma_map_single(dma_dev->dev, buf, len,
+ DMA_TO_DEVICE);
+ if (!phys_addr) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_dma_map;
+ }
+
+ if (is_read) {
+ dma_src_addr = (dma_addr_t)host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = (dma_addr_t)host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+err:
+ if (is_read)
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
+err_dma_map:
+ return;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= DMA_MIN_BYTES)
+ atmel_nand_dma_op(mtd, buf, len, 1);
+ else if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= DMA_MIN_BYTES)
+ atmel_nand_dma_op(mtd, (u8 *)buf, len, 0);
+ else if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +509,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (void __iomem *)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -516,6 +629,23 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
@@ -555,6 +685,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +710,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-14 9:34 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-14 9:34 UTC (permalink / raw)
To: linux-mtd, linux-arm-kernel, linux-kernel; +Cc: hong.xu, nicolas.ferre
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 140 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 138 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..c2fbffe 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,12 @@
#define no_ecc 0
#endif
+/* DMA for large buffers only */
+#define DMA_MIN_BYTES 512
+
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +95,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ void __iomem *io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +165,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +179,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +193,102 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static void atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ if (is_read)
+ phys_addr = dma_map_single(dma_dev->dev, buf, len,
+ DMA_FROM_DEVICE);
+ else
+ phys_addr = dma_map_single(dma_dev->dev, buf, len,
+ DMA_TO_DEVICE);
+ if (!phys_addr) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_dma_map;
+ }
+
+ if (is_read) {
+ dma_src_addr = (dma_addr_t)host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = (dma_addr_t)host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+err:
+ if (is_read)
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
+err_dma_map:
+ return;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= DMA_MIN_BYTES)
+ atmel_nand_dma_op(mtd, buf, len, 1);
+ else if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= DMA_MIN_BYTES)
+ atmel_nand_dma_op(mtd, (u8 *)buf, len, 0);
+ else if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +509,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (void __iomem *)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -516,6 +629,23 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
@@ -555,6 +685,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +710,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
* [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash
@ 2011-01-14 9:34 ` Hong Xu
0 siblings, 0 replies; 46+ messages in thread
From: Hong Xu @ 2011-01-14 9:34 UTC (permalink / raw)
To: linux-arm-kernel
Some SAM9 chips have the ability to perform DMA between CPU and SMC controller.
This patch adds DMA support for SAM9RL, SAM9G45, SSAM9G46,AM9M10, SAM9M11.
Signed-off-by: Hong Xu <hong.xu@atmel.com>
---
drivers/mtd/nand/atmel_nand.c | 140 ++++++++++++++++++++++++++++++++++++++++-
1 files changed, 138 insertions(+), 2 deletions(-)
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c
index ccce0f0..c2fbffe 100644
--- a/drivers/mtd/nand/atmel_nand.c
+++ b/drivers/mtd/nand/atmel_nand.c
@@ -48,6 +48,12 @@
#define no_ecc 0
#endif
+/* DMA for large buffers only */
+#define DMA_MIN_BYTES 512
+
+static int use_dma = 1;
+module_param(use_dma, int, 0);
+
static int on_flash_bbt = 0;
module_param(on_flash_bbt, int, 0);
@@ -89,11 +95,20 @@ struct atmel_nand_host {
struct nand_chip nand_chip;
struct mtd_info mtd;
void __iomem *io_base;
+ void __iomem *io_phys;
struct atmel_nand_data *board;
struct device *dev;
void __iomem *ecc;
+
+ struct completion comp;
+ struct dma_chan *dma_chan;
};
+static int cpu_has_dma(void)
+{
+ return cpu_is_at91sam9rl() || cpu_is_at91sam9g45();
+}
+
/*
* Enable NAND.
*/
@@ -150,7 +165,7 @@ static int atmel_nand_device_ready(struct mtd_info *mtd)
/*
* Minimal-overhead PIO for data access.
*/
-static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+static void atmel_read_buf8(struct mtd_info *mtd, u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -164,7 +179,7 @@ static void atmel_read_buf16(struct mtd_info *mtd, u8 *buf, int len)
__raw_readsw(nand_chip->IO_ADDR_R, buf, len / 2);
}
-static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+static void atmel_write_buf8(struct mtd_info *mtd, const u8 *buf, int len)
{
struct nand_chip *nand_chip = mtd->priv;
@@ -178,6 +193,102 @@ static void atmel_write_buf16(struct mtd_info *mtd, const u8 *buf, int len)
__raw_writesw(nand_chip->IO_ADDR_W, buf, len / 2);
}
+static void dma_complete_func(void *completion)
+{
+ complete(completion);
+}
+
+static void atmel_nand_dma_op(struct mtd_info *mtd, void *buf, int len,
+ int is_read)
+{
+ struct dma_device *dma_dev;
+ enum dma_ctrl_flags flags;
+ dma_addr_t dma_src_addr, dma_dst_addr, phys_addr;
+ struct dma_async_tx_descriptor *tx = NULL;
+ dma_cookie_t cookie;
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ dma_dev = host->dma_chan->device;
+
+ flags = DMA_CTRL_ACK | DMA_COMPL_SKIP_DEST_UNMAP | DMA_PREP_INTERRUPT |
+ DMA_COMPL_SKIP_SRC_UNMAP;
+
+ if (is_read)
+ phys_addr = dma_map_single(dma_dev->dev, buf, len,
+ DMA_FROM_DEVICE);
+ else
+ phys_addr = dma_map_single(dma_dev->dev, buf, len,
+ DMA_TO_DEVICE);
+ if (!phys_addr) {
+ dev_err(host->dev, "Failed to dma_map_single\n");
+ goto err_dma_map;
+ }
+
+ if (is_read) {
+ dma_src_addr = (dma_addr_t)host->io_phys;
+ dma_dst_addr = phys_addr;
+ } else {
+ dma_src_addr = phys_addr;
+ dma_dst_addr = (dma_addr_t)host->io_phys;
+ }
+
+ tx = dma_dev->device_prep_dma_memcpy(host->dma_chan, dma_dst_addr,
+ dma_src_addr, len, flags);
+ if (!tx) {
+ dev_err(host->dev, "Failed to prepare DMA memcpy\n");
+ goto err;
+ }
+
+ init_completion(&host->comp);
+ tx->callback = dma_complete_func;
+ tx->callback_param = &host->comp;
+
+ cookie = tx->tx_submit(tx);
+ if (dma_submit_error(cookie)) {
+ dev_err(host->dev, "Failed to do DMA tx_submit\n");
+ goto err;
+ }
+
+ dma_async_issue_pending(host->dma_chan);
+
+ wait_for_completion(&host->comp);
+
+err:
+ if (is_read)
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(dma_dev->dev, phys_addr, len, DMA_TO_DEVICE);
+err_dma_map:
+ return;
+}
+
+static void atmel_read_buf(struct mtd_info *mtd, u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= DMA_MIN_BYTES)
+ atmel_nand_dma_op(mtd, buf, len, 1);
+ else if (host->board->bus_width_16)
+ atmel_read_buf16(mtd, buf, len);
+ else
+ atmel_read_buf8(mtd, buf, len);
+}
+
+static void atmel_write_buf(struct mtd_info *mtd, const u8 *buf, int len)
+{
+ struct nand_chip *chip = mtd->priv;
+ struct atmel_nand_host *host = chip->priv;
+
+ if (use_dma && len >= DMA_MIN_BYTES)
+ atmel_nand_dma_op(mtd, (u8 *)buf, len, 0);
+ else if (host->board->bus_width_16)
+ atmel_write_buf16(mtd, buf, len);
+ else
+ atmel_write_buf8(mtd, buf, len);
+}
+
/*
* Calculate HW ECC
*
@@ -398,6 +509,8 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ host->io_phys = (void __iomem *)mem->start;
+
host->io_base = ioremap(mem->start, mem->end - mem->start + 1);
if (host->io_base == NULL) {
printk(KERN_ERR "atmel_nand: ioremap failed\n");
@@ -516,6 +629,23 @@ static int __init atmel_nand_probe(struct platform_device *pdev)
}
}
+ if (cpu_has_dma() && use_dma) {
+ dma_cap_mask_t mask;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_MEMCPY, mask);
+ host->dma_chan = dma_request_channel(mask, 0, NULL);
+
+ if (!host->dma_chan) {
+ dev_err(host->dev, "Failed to request DMA channel\n");
+ use_dma = 0;
+ }
+ }
+ if (use_dma)
+ dev_info(host->dev, "Using DMA for NAND access.\n");
+ else
+ dev_info(host->dev, "No DMA support for NAND access.\n");
+
/* second phase scan */
if (nand_scan_tail(mtd)) {
res = -ENXIO;
@@ -555,6 +685,8 @@ err_scan_ident:
err_no_card:
atmel_nand_disable(host);
platform_set_drvdata(pdev, NULL);
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
if (host->ecc)
iounmap(host->ecc);
err_ecc_ioremap:
@@ -578,6 +710,10 @@ static int __exit atmel_nand_remove(struct platform_device *pdev)
if (host->ecc)
iounmap(host->ecc);
+
+ if (host->dma_chan)
+ dma_release_channel(host->dma_chan);
+
iounmap(host->io_base);
kfree(host);
--
1.7.3.3
^ permalink raw reply related [flat|nested] 46+ messages in thread
end of thread, other threads:[~2011-07-08 17:32 UTC | newest]
Thread overview: 46+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
[not found] <hong.xu@atmel.com>
2011-01-17 7:20 ` [PATCH] MTD: atmel_nand: Add DMA support to access Nandflash Hong Xu
2011-07-08 16:43 ` Hong Xu
2011-01-17 7:20 ` Hong Xu
2011-01-17 20:15 ` Ryan Mallon
2011-01-17 20:15 ` Ryan Mallon
2011-01-17 20:15 ` Ryan Mallon
2011-01-17 22:42 ` Ryan Mallon
2011-01-17 22:42 ` Ryan Mallon
2011-01-17 22:42 ` Ryan Mallon
2011-01-18 1:43 ` Xu, Hong
2011-01-18 1:43 ` Xu, Hong
2011-01-18 1:43 ` Xu, Hong
2011-01-18 2:44 ` Ryan Mallon
2011-01-18 2:44 ` Ryan Mallon
2011-01-18 2:44 ` Ryan Mallon
2011-01-17 21:35 ` Ryan Mallon
2011-01-17 21:35 ` Ryan Mallon
2011-01-17 21:35 ` Ryan Mallon
2011-01-18 2:56 ` Hong Xu
2011-07-08 17:32 ` Hong Xu
2011-01-18 2:56 ` Hong Xu
2011-01-18 3:08 ` Ryan Mallon
2011-01-18 3:08 ` Ryan Mallon
2011-01-18 3:08 ` Ryan Mallon
2011-01-18 6:17 ` 答复: " Xu, Hong
2011-01-18 6:36 Hong Xu
2011-01-18 6:36 ` Hong Xu
2011-01-18 6:36 ` Hong Xu
2011-01-18 9:06 ` Ryan Mallon
2011-07-08 15:36 ` Ryan Mallon
2011-01-18 9:06 ` Ryan Mallon
2011-01-21 11:23 ` Artem Bityutskiy
2011-01-21 11:23 ` Artem Bityutskiy
2011-01-21 11:23 ` Artem Bityutskiy
-- strict thread matches above, loose matches on Subject: below --
2011-01-18 3:02 Hong Xu
2011-01-18 3:02 ` Hong Xu
2011-01-18 3:02 ` Hong Xu
2011-01-14 9:34 Hong Xu
2011-01-14 9:34 ` Hong Xu
2011-01-14 9:34 ` Hong Xu
2011-01-14 10:00 ` Jamie Iles
2011-01-14 10:00 ` Jamie Iles
2011-01-14 10:00 ` Jamie Iles
2011-01-14 11:43 ` Peter Korsgaard
2011-01-14 11:43 ` Peter Korsgaard
2011-01-14 11:43 ` Peter Korsgaard
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.