diff options
Diffstat (limited to 'drivers/mtd/nand/omap2.c')
-rw-r--r-- | drivers/mtd/nand/omap2.c | 347 |
1 files changed, 336 insertions, 11 deletions
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ebd07e95b814..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 | ||
@@ -112,6 +111,27 @@ | |||
112 | static const char *part_probes[] = { "cmdlinepart", NULL }; | 111 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
113 | #endif | 112 | #endif |
114 | 113 | ||
114 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH | ||
115 | static int use_prefetch = 1; | ||
116 | |||
117 | /* "modprobe ... use_prefetch=0" etc */ | ||
118 | module_param(use_prefetch, bool, 0); | ||
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 | ||
130 | #else | ||
131 | const int use_prefetch; | ||
132 | const int use_dma; | ||
133 | #endif | ||
134 | |||
115 | struct omap_nand_info { | 135 | struct omap_nand_info { |
116 | struct nand_hw_control controller; | 136 | struct nand_hw_control controller; |
117 | struct omap_nand_platform_data *pdata; | 137 | struct omap_nand_platform_data *pdata; |
@@ -124,6 +144,9 @@ struct omap_nand_info { | |||
124 | unsigned long phys_base; | 144 | unsigned long phys_base; |
125 | void __iomem *gpmc_cs_baseaddr; | 145 | void __iomem *gpmc_cs_baseaddr; |
126 | void __iomem *gpmc_baseaddr; | 146 | void __iomem *gpmc_baseaddr; |
147 | void __iomem *nand_pref_fifo_add; | ||
148 | struct completion comp; | ||
149 | int dma_ch; | ||
127 | }; | 150 | }; |
128 | 151 | ||
129 | /** | 152 | /** |
@@ -189,6 +212,38 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | |||
189 | } | 212 | } |
190 | 213 | ||
191 | /** | 214 | /** |
215 | * omap_read_buf8 - read data from NAND controller into buffer | ||
216 | * @mtd: MTD device structure | ||
217 | * @buf: buffer to store date | ||
218 | * @len: number of bytes to read | ||
219 | */ | ||
220 | static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) | ||
221 | { | ||
222 | struct nand_chip *nand = mtd->priv; | ||
223 | |||
224 | ioread8_rep(nand->IO_ADDR_R, buf, len); | ||
225 | } | ||
226 | |||
227 | /** | ||
228 | * omap_write_buf8 - write buffer to NAND controller | ||
229 | * @mtd: MTD device structure | ||
230 | * @buf: data buffer | ||
231 | * @len: number of bytes to write | ||
232 | */ | ||
233 | static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) | ||
234 | { | ||
235 | struct omap_nand_info *info = container_of(mtd, | ||
236 | struct omap_nand_info, mtd); | ||
237 | u_char *p = (u_char *)buf; | ||
238 | |||
239 | while (len--) { | ||
240 | iowrite8(*p++, info->nand.IO_ADDR_W); | ||
241 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | ||
242 | GPMC_STATUS) & GPMC_BUF_FULL)); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | /** | ||
192 | * omap_read_buf16 - read data from NAND controller into buffer | 247 | * omap_read_buf16 - read data from NAND controller into buffer |
193 | * @mtd: MTD device structure | 248 | * @mtd: MTD device structure |
194 | * @buf: buffer to store date | 249 | * @buf: buffer to store date |
@@ -198,7 +253,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) | |||
198 | { | 253 | { |
199 | struct nand_chip *nand = mtd->priv; | 254 | struct nand_chip *nand = mtd->priv; |
200 | 255 | ||
201 | __raw_readsw(nand->IO_ADDR_R, buf, len / 2); | 256 | ioread16_rep(nand->IO_ADDR_R, buf, len / 2); |
202 | } | 257 | } |
203 | 258 | ||
204 | /** | 259 | /** |
@@ -217,13 +272,242 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) | |||
217 | len >>= 1; | 272 | len >>= 1; |
218 | 273 | ||
219 | while (len--) { | 274 | while (len--) { |
220 | writew(*p++, info->nand.IO_ADDR_W); | 275 | iowrite16(*p++, info->nand.IO_ADDR_W); |
221 | 276 | ||
222 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | 277 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + |
223 | GPMC_STATUS) & GPMC_BUF_FULL)) | 278 | GPMC_STATUS) & GPMC_BUF_FULL)) |
224 | ; | 279 | ; |
225 | } | 280 | } |
226 | } | 281 | } |
282 | |||
283 | /** | ||
284 | * omap_read_buf_pref - read data from NAND controller into buffer | ||
285 | * @mtd: MTD device structure | ||
286 | * @buf: buffer to store date | ||
287 | * @len: number of bytes to read | ||
288 | */ | ||
289 | static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) | ||
290 | { | ||
291 | struct omap_nand_info *info = container_of(mtd, | ||
292 | struct omap_nand_info, mtd); | ||
293 | uint32_t pfpw_status = 0, r_count = 0; | ||
294 | int ret = 0; | ||
295 | u32 *p = (u32 *)buf; | ||
296 | |||
297 | /* take care of subpage reads */ | ||
298 | for (; len % 4 != 0; ) { | ||
299 | *buf++ = __raw_readb(info->nand.IO_ADDR_R); | ||
300 | len--; | ||
301 | } | ||
302 | p = (u32 *) buf; | ||
303 | |||
304 | /* configure and start prefetch transfer */ | ||
305 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); | ||
306 | if (ret) { | ||
307 | /* PFPW engine is busy, use cpu copy method */ | ||
308 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
309 | omap_read_buf16(mtd, buf, len); | ||
310 | else | ||
311 | omap_read_buf8(mtd, buf, len); | ||
312 | } else { | ||
313 | do { | ||
314 | pfpw_status = gpmc_prefetch_status(); | ||
315 | r_count = ((pfpw_status >> 24) & 0x7F) >> 2; | ||
316 | ioread32_rep(info->nand_pref_fifo_add, p, r_count); | ||
317 | p += r_count; | ||
318 | len -= r_count << 2; | ||
319 | } while (len); | ||
320 | |||
321 | /* disable and stop the PFPW engine */ | ||
322 | gpmc_prefetch_reset(); | ||
323 | } | ||
324 | } | ||
325 | |||
326 | /** | ||
327 | * omap_write_buf_pref - write buffer to NAND controller | ||
328 | * @mtd: MTD device structure | ||
329 | * @buf: data buffer | ||
330 | * @len: number of bytes to write | ||
331 | */ | ||
332 | static void omap_write_buf_pref(struct mtd_info *mtd, | ||
333 | const u_char *buf, int len) | ||
334 | { | ||
335 | struct omap_nand_info *info = container_of(mtd, | ||
336 | struct omap_nand_info, mtd); | ||
337 | uint32_t pfpw_status = 0, w_count = 0; | ||
338 | int i = 0, ret = 0; | ||
339 | u16 *p = (u16 *) buf; | ||
340 | |||
341 | /* take care of subpage writes */ | ||
342 | if (len % 2 != 0) { | ||
343 | writeb(*buf, info->nand.IO_ADDR_R); | ||
344 | p = (u16 *)(buf + 1); | ||
345 | len--; | ||
346 | } | ||
347 | |||
348 | /* configure and start prefetch transfer */ | ||
349 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); | ||
350 | if (ret) { | ||
351 | /* PFPW engine is busy, use cpu copy method */ | ||
352 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
353 | omap_write_buf16(mtd, buf, len); | ||
354 | else | ||
355 | omap_write_buf8(mtd, buf, len); | ||
356 | } else { | ||
357 | pfpw_status = gpmc_prefetch_status(); | ||
358 | while (pfpw_status & 0x3FFF) { | ||
359 | w_count = ((pfpw_status >> 24) & 0x7F) >> 1; | ||
360 | for (i = 0; (i < w_count) && len; i++, len -= 2) | ||
361 | iowrite16(*p++, info->nand_pref_fifo_add); | ||
362 | pfpw_status = gpmc_prefetch_status(); | ||
363 | } | ||
364 | |||
365 | /* disable and stop the PFPW engine */ | ||
366 | gpmc_prefetch_reset(); | ||
367 | } | ||
368 | } | ||
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 | |||
227 | /** | 511 | /** |
228 | * omap_verify_buf - Verify chip data against buffer | 512 | * omap_verify_buf - Verify chip data against buffer |
229 | * @mtd: MTD device structure | 513 | * @mtd: MTD device structure |
@@ -658,17 +942,12 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
658 | err = -ENOMEM; | 942 | err = -ENOMEM; |
659 | goto out_release_mem_region; | 943 | goto out_release_mem_region; |
660 | } | 944 | } |
945 | |||
661 | info->nand.controller = &info->controller; | 946 | info->nand.controller = &info->controller; |
662 | 947 | ||
663 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; | 948 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; |
664 | info->nand.cmd_ctrl = omap_hwcontrol; | 949 | info->nand.cmd_ctrl = omap_hwcontrol; |
665 | 950 | ||
666 | /* REVISIT: only supports 16-bit NAND flash */ | ||
667 | |||
668 | info->nand.read_buf = omap_read_buf16; | ||
669 | info->nand.write_buf = omap_write_buf16; | ||
670 | info->nand.verify_buf = omap_verify_buf; | ||
671 | |||
672 | /* | 951 | /* |
673 | * If RDY/BSY line is connected to OMAP then use the omap ready | 952 | * If RDY/BSY line is connected to OMAP then use the omap ready |
674 | * funcrtion and the generic nand_wait function which reads the status | 953 | * funcrtion and the generic nand_wait function which reads the status |
@@ -689,6 +968,40 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
689 | == 0x1000) | 968 | == 0x1000) |
690 | info->nand.options |= NAND_BUSWIDTH_16; | 969 | info->nand.options |= NAND_BUSWIDTH_16; |
691 | 970 | ||
971 | if (use_prefetch) { | ||
972 | /* copy the virtual address of nand base for fifo access */ | ||
973 | info->nand_pref_fifo_add = info->nand.IO_ADDR_R; | ||
974 | |||
975 | info->nand.read_buf = omap_read_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 | } | ||
994 | } else { | ||
995 | if (info->nand.options & NAND_BUSWIDTH_16) { | ||
996 | info->nand.read_buf = omap_read_buf16; | ||
997 | info->nand.write_buf = omap_write_buf16; | ||
998 | } else { | ||
999 | info->nand.read_buf = omap_read_buf8; | ||
1000 | info->nand.write_buf = omap_write_buf8; | ||
1001 | } | ||
1002 | } | ||
1003 | info->nand.verify_buf = omap_verify_buf; | ||
1004 | |||
692 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC | 1005 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC |
693 | info->nand.ecc.bytes = 3; | 1006 | info->nand.ecc.bytes = 3; |
694 | info->nand.ecc.size = 512; | 1007 | info->nand.ecc.size = 512; |
@@ -744,9 +1057,12 @@ static int omap_nand_remove(struct platform_device *pdev) | |||
744 | struct omap_nand_info *info = mtd->priv; | 1057 | struct omap_nand_info *info = mtd->priv; |
745 | 1058 | ||
746 | platform_set_drvdata(pdev, NULL); | 1059 | platform_set_drvdata(pdev, NULL); |
1060 | if (use_dma) | ||
1061 | omap_free_dma(info->dma_ch); | ||
1062 | |||
747 | /* Release NAND device, its internal structures and partitions */ | 1063 | /* Release NAND device, its internal structures and partitions */ |
748 | nand_release(&info->mtd); | 1064 | nand_release(&info->mtd); |
749 | iounmap(info->nand.IO_ADDR_R); | 1065 | iounmap(info->nand_pref_fifo_add); |
750 | kfree(&info->mtd); | 1066 | kfree(&info->mtd); |
751 | return 0; | 1067 | return 0; |
752 | } | 1068 | } |
@@ -763,6 +1079,15 @@ static struct platform_driver omap_nand_driver = { | |||
763 | static int __init omap_nand_init(void) | 1079 | static int __init omap_nand_init(void) |
764 | { | 1080 | { |
765 | 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 | } | ||
766 | return platform_driver_register(&omap_nand_driver); | 1091 | return platform_driver_register(&omap_nand_driver); |
767 | } | 1092 | } |
768 | 1093 | ||