diff options
author | vimal singh <vimalsingh@ti.com> | 2009-07-13 06:59:16 -0400 |
---|---|---|
committer | David Woodhouse <David.Woodhouse@intel.com> | 2009-09-19 16:21:05 -0400 |
commit | dfe32893cbe3e599a39770199b9982a6ad5daa7b (patch) | |
tree | 44f019729831fcda3bcec71dcf31827861f77219 /drivers/mtd | |
parent | 59e9c5ae17179fe561103fbe0808fac5976ca1bd (diff) |
mtd: omap: adding DMA mode support in nand prefetch/post-write
This patch adds DMA mode support for nand prefetch/post-write engine.
Signed-off-by: Vimal Singh <vimalsingh@ti.com>
Signed-off-by: David Woodhouse <David.Woodhouse@intel.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/nand/Kconfig | 9 | ||||
-rw-r--r-- | drivers/mtd/nand/omap2.c | 186 |
2 files changed, 193 insertions, 2 deletions
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7dab79caed44..2c9a0ed4aed8 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -88,6 +88,15 @@ config MTD_NAND_OMAP_PREFETCH | |||
88 | The NAND device can be accessed for Read/Write using GPMC PREFETCH engine | 88 | The NAND device can be accessed for Read/Write using GPMC PREFETCH engine |
89 | to improve the performance. | 89 | to improve the performance. |
90 | 90 | ||
91 | config MTD_NAND_OMAP_PREFETCH_DMA | ||
92 | depends on MTD_NAND_OMAP_PREFETCH | ||
93 | bool "DMA mode" | ||
94 | default n | ||
95 | help | ||
96 | The GPMC PREFETCH engine can be configured eigther in MPU interrupt mode | ||
97 | or in DMA interrupt mode. | ||
98 | Say y for DMA mode or MPU mode will be used | ||
99 | |||
91 | config MTD_NAND_TS7250 | 100 | config MTD_NAND_TS7250 |
92 | tristate "NAND Flash device on TS-7250 board" | 101 | tristate "NAND Flash device on TS-7250 board" |
93 | depends on MACH_TS72XX | 102 | depends on MACH_TS72XX |
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 6736822c4751..090ab87086b5 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
@@ -18,8 +18,7 @@ | |||
18 | #include <linux/mtd/partitions.h> | 18 | #include <linux/mtd/partitions.h> |
19 | #include <linux/io.h> | 19 | #include <linux/io.h> |
20 | 20 | ||
21 | #include <asm/dma.h> | 21 | #include <mach/dma.h> |
22 | |||
23 | #include <mach/gpmc.h> | 22 | #include <mach/gpmc.h> |
24 | #include <mach/nand.h> | 23 | #include <mach/nand.h> |
25 | 24 | ||
@@ -118,8 +117,19 @@ static int use_prefetch = 1; | |||
118 | /* "modprobe ... use_prefetch=0" etc */ | 117 | /* "modprobe ... use_prefetch=0" etc */ |
119 | module_param(use_prefetch, bool, 0); | 118 | module_param(use_prefetch, bool, 0); |
120 | MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); | 119 | MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); |
120 | |||
121 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA | ||
122 | static int use_dma = 1; | ||
123 | |||
124 | /* "modprobe ... use_dma=0" etc */ | ||
125 | module_param(use_dma, bool, 0); | ||
126 | MODULE_PARM_DESC(use_dma, "enable/disable use of DMA"); | ||
127 | #else | ||
128 | const int use_dma; | ||
129 | #endif | ||
121 | #else | 130 | #else |
122 | const int use_prefetch; | 131 | const int use_prefetch; |
132 | const int use_dma; | ||
123 | #endif | 133 | #endif |
124 | 134 | ||
125 | struct omap_nand_info { | 135 | struct omap_nand_info { |
@@ -135,6 +145,8 @@ struct omap_nand_info { | |||
135 | void __iomem *gpmc_cs_baseaddr; | 145 | void __iomem *gpmc_cs_baseaddr; |
136 | void __iomem *gpmc_baseaddr; | 146 | void __iomem *gpmc_baseaddr; |
137 | void __iomem *nand_pref_fifo_add; | 147 | void __iomem *nand_pref_fifo_add; |
148 | struct completion comp; | ||
149 | int dma_ch; | ||
138 | }; | 150 | }; |
139 | 151 | ||
140 | /** | 152 | /** |
@@ -355,6 +367,147 @@ static void omap_write_buf_pref(struct mtd_info *mtd, | |||
355 | } | 367 | } |
356 | } | 368 | } |
357 | 369 | ||
370 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH_DMA | ||
371 | /* | ||
372 | * omap_nand_dma_cb: callback on the completion of dma transfer | ||
373 | * @lch: logical channel | ||
374 | * @ch_satuts: channel status | ||
375 | * @data: pointer to completion data structure | ||
376 | */ | ||
377 | static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) | ||
378 | { | ||
379 | complete((struct completion *) data); | ||
380 | } | ||
381 | |||
382 | /* | ||
383 | * omap_nand_dma_transfer: configer and start dma transfer | ||
384 | * @mtd: MTD device structure | ||
385 | * @addr: virtual address in RAM of source/destination | ||
386 | * @len: number of data bytes to be transferred | ||
387 | * @is_write: flag for read/write operation | ||
388 | */ | ||
389 | static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, | ||
390 | unsigned int len, int is_write) | ||
391 | { | ||
392 | struct omap_nand_info *info = container_of(mtd, | ||
393 | struct omap_nand_info, mtd); | ||
394 | uint32_t prefetch_status = 0; | ||
395 | enum dma_data_direction dir = is_write ? DMA_TO_DEVICE : | ||
396 | DMA_FROM_DEVICE; | ||
397 | dma_addr_t dma_addr; | ||
398 | int ret; | ||
399 | |||
400 | /* The fifo depth is 64 bytes. We have a sync at each frame and frame | ||
401 | * length is 64 bytes. | ||
402 | */ | ||
403 | int buf_len = len >> 6; | ||
404 | |||
405 | if (addr >= high_memory) { | ||
406 | struct page *p1; | ||
407 | |||
408 | if (((size_t)addr & PAGE_MASK) != | ||
409 | ((size_t)(addr + len - 1) & PAGE_MASK)) | ||
410 | goto out_copy; | ||
411 | p1 = vmalloc_to_page(addr); | ||
412 | if (!p1) | ||
413 | goto out_copy; | ||
414 | addr = page_address(p1) + ((size_t)addr & ~PAGE_MASK); | ||
415 | } | ||
416 | |||
417 | dma_addr = dma_map_single(&info->pdev->dev, addr, len, dir); | ||
418 | if (dma_mapping_error(&info->pdev->dev, dma_addr)) { | ||
419 | dev_err(&info->pdev->dev, | ||
420 | "Couldn't DMA map a %d byte buffer\n", len); | ||
421 | goto out_copy; | ||
422 | } | ||
423 | |||
424 | if (is_write) { | ||
425 | omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, | ||
426 | info->phys_base, 0, 0); | ||
427 | omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, | ||
428 | dma_addr, 0, 0); | ||
429 | omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, | ||
430 | 0x10, buf_len, OMAP_DMA_SYNC_FRAME, | ||
431 | OMAP24XX_DMA_GPMC, OMAP_DMA_DST_SYNC); | ||
432 | } else { | ||
433 | omap_set_dma_src_params(info->dma_ch, 0, OMAP_DMA_AMODE_CONSTANT, | ||
434 | info->phys_base, 0, 0); | ||
435 | omap_set_dma_dest_params(info->dma_ch, 0, OMAP_DMA_AMODE_POST_INC, | ||
436 | dma_addr, 0, 0); | ||
437 | omap_set_dma_transfer_params(info->dma_ch, OMAP_DMA_DATA_TYPE_S32, | ||
438 | 0x10, buf_len, OMAP_DMA_SYNC_FRAME, | ||
439 | OMAP24XX_DMA_GPMC, OMAP_DMA_SRC_SYNC); | ||
440 | } | ||
441 | /* configure and start prefetch transfer */ | ||
442 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x1, len, is_write); | ||
443 | if (ret) | ||
444 | /* PFPW engine is busy, use cpu copy methode */ | ||
445 | goto out_copy; | ||
446 | |||
447 | init_completion(&info->comp); | ||
448 | |||
449 | omap_start_dma(info->dma_ch); | ||
450 | |||
451 | /* setup and start DMA using dma_addr */ | ||
452 | wait_for_completion(&info->comp); | ||
453 | |||
454 | while (0x3fff & (prefetch_status = gpmc_prefetch_status())) | ||
455 | ; | ||
456 | /* disable and stop the PFPW engine */ | ||
457 | gpmc_prefetch_reset(); | ||
458 | |||
459 | dma_unmap_single(&info->pdev->dev, dma_addr, len, dir); | ||
460 | return 0; | ||
461 | |||
462 | out_copy: | ||
463 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
464 | is_write == 0 ? omap_read_buf16(mtd, (u_char *) addr, len) | ||
465 | : omap_write_buf16(mtd, (u_char *) addr, len); | ||
466 | else | ||
467 | is_write == 0 ? omap_read_buf8(mtd, (u_char *) addr, len) | ||
468 | : omap_write_buf8(mtd, (u_char *) addr, len); | ||
469 | return 0; | ||
470 | } | ||
471 | #else | ||
472 | static void omap_nand_dma_cb(int lch, u16 ch_status, void *data) {} | ||
473 | static inline int omap_nand_dma_transfer(struct mtd_info *mtd, void *addr, | ||
474 | unsigned int len, int is_write) | ||
475 | { | ||
476 | return 0; | ||
477 | } | ||
478 | #endif | ||
479 | |||
480 | /** | ||
481 | * omap_read_buf_dma_pref - read data from NAND controller into buffer | ||
482 | * @mtd: MTD device structure | ||
483 | * @buf: buffer to store date | ||
484 | * @len: number of bytes to read | ||
485 | */ | ||
486 | static void omap_read_buf_dma_pref(struct mtd_info *mtd, u_char *buf, int len) | ||
487 | { | ||
488 | if (len <= mtd->oobsize) | ||
489 | omap_read_buf_pref(mtd, buf, len); | ||
490 | else | ||
491 | /* start transfer in DMA mode */ | ||
492 | omap_nand_dma_transfer(mtd, buf, len, 0x0); | ||
493 | } | ||
494 | |||
495 | /** | ||
496 | * omap_write_buf_dma_pref - write buffer to NAND controller | ||
497 | * @mtd: MTD device structure | ||
498 | * @buf: data buffer | ||
499 | * @len: number of bytes to write | ||
500 | */ | ||
501 | static void omap_write_buf_dma_pref(struct mtd_info *mtd, | ||
502 | const u_char *buf, int len) | ||
503 | { | ||
504 | if (len <= mtd->oobsize) | ||
505 | omap_write_buf_pref(mtd, buf, len); | ||
506 | else | ||
507 | /* start transfer in DMA mode */ | ||
508 | omap_nand_dma_transfer(mtd, buf, len, 0x1); | ||
509 | } | ||
510 | |||
358 | /** | 511 | /** |
359 | * omap_verify_buf - Verify chip data against buffer | 512 | * omap_verify_buf - Verify chip data against buffer |
360 | * @mtd: MTD device structure | 513 | * @mtd: MTD device structure |
@@ -821,6 +974,23 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
821 | 974 | ||
822 | info->nand.read_buf = omap_read_buf_pref; | 975 | info->nand.read_buf = omap_read_buf_pref; |
823 | info->nand.write_buf = omap_write_buf_pref; | 976 | info->nand.write_buf = omap_write_buf_pref; |
977 | if (use_dma) { | ||
978 | err = omap_request_dma(OMAP24XX_DMA_GPMC, "NAND", | ||
979 | omap_nand_dma_cb, &info->comp, &info->dma_ch); | ||
980 | if (err < 0) { | ||
981 | info->dma_ch = -1; | ||
982 | printk(KERN_WARNING "DMA request failed." | ||
983 | " Non-dma data transfer mode\n"); | ||
984 | } else { | ||
985 | omap_set_dma_dest_burst_mode(info->dma_ch, | ||
986 | OMAP_DMA_DATA_BURST_16); | ||
987 | omap_set_dma_src_burst_mode(info->dma_ch, | ||
988 | OMAP_DMA_DATA_BURST_16); | ||
989 | |||
990 | info->nand.read_buf = omap_read_buf_dma_pref; | ||
991 | info->nand.write_buf = omap_write_buf_dma_pref; | ||
992 | } | ||
993 | } | ||
824 | } else { | 994 | } else { |
825 | if (info->nand.options & NAND_BUSWIDTH_16) { | 995 | if (info->nand.options & NAND_BUSWIDTH_16) { |
826 | info->nand.read_buf = omap_read_buf16; | 996 | info->nand.read_buf = omap_read_buf16; |
@@ -887,6 +1057,9 @@ static int omap_nand_remove(struct platform_device *pdev) | |||
887 | struct omap_nand_info *info = mtd->priv; | 1057 | struct omap_nand_info *info = mtd->priv; |
888 | 1058 | ||
889 | platform_set_drvdata(pdev, NULL); | 1059 | platform_set_drvdata(pdev, NULL); |
1060 | if (use_dma) | ||
1061 | omap_free_dma(info->dma_ch); | ||
1062 | |||
890 | /* Release NAND device, its internal structures and partitions */ | 1063 | /* Release NAND device, its internal structures and partitions */ |
891 | nand_release(&info->mtd); | 1064 | nand_release(&info->mtd); |
892 | iounmap(info->nand_pref_fifo_add); | 1065 | iounmap(info->nand_pref_fifo_add); |
@@ -906,6 +1079,15 @@ static struct platform_driver omap_nand_driver = { | |||
906 | static int __init omap_nand_init(void) | 1079 | static int __init omap_nand_init(void) |
907 | { | 1080 | { |
908 | printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); | 1081 | printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); |
1082 | |||
1083 | /* This check is required if driver is being | ||
1084 | * loaded run time as a module | ||
1085 | */ | ||
1086 | if ((1 == use_dma) && (0 == use_prefetch)) { | ||
1087 | printk(KERN_INFO"Wrong parameters: 'use_dma' can not be 1 " | ||
1088 | "without use_prefetch'. Prefetch will not be" | ||
1089 | " used in either mode (mpu or dma)\n"); | ||
1090 | } | ||
909 | return platform_driver_register(&omap_nand_driver); | 1091 | return platform_driver_register(&omap_nand_driver); |
910 | } | 1092 | } |
911 | 1093 | ||