diff options
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 63 | ||||
-rw-r--r-- | arch/arm/plat-omap/include/mach/gpmc.h | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/Kconfig | 8 | ||||
-rw-r--r-- | drivers/mtd/nand/omap2.c | 161 |
4 files changed, 226 insertions, 10 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index f91934b2b092..15876828db23 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
@@ -57,6 +57,11 @@ | |||
57 | #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ | 57 | #define GPMC_CHUNK_SHIFT 24 /* 16 MB */ |
58 | #define GPMC_SECTION_SHIFT 28 /* 128 MB */ | 58 | #define GPMC_SECTION_SHIFT 28 /* 128 MB */ |
59 | 59 | ||
60 | #define PREFETCH_FIFOTHRESHOLD (0x40 << 8) | ||
61 | #define CS_NUM_SHIFT 24 | ||
62 | #define ENABLE_PREFETCH (0x1 << 7) | ||
63 | #define DMA_MPU_MODE 2 | ||
64 | |||
60 | static struct resource gpmc_mem_root; | 65 | static struct resource gpmc_mem_root; |
61 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; | 66 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; |
62 | static DEFINE_SPINLOCK(gpmc_mem_lock); | 67 | static DEFINE_SPINLOCK(gpmc_mem_lock); |
@@ -386,6 +391,63 @@ void gpmc_cs_free(int cs) | |||
386 | } | 391 | } |
387 | EXPORT_SYMBOL(gpmc_cs_free); | 392 | EXPORT_SYMBOL(gpmc_cs_free); |
388 | 393 | ||
394 | /** | ||
395 | * gpmc_prefetch_enable - configures and starts prefetch transfer | ||
396 | * @cs: nand cs (chip select) number | ||
397 | * @dma_mode: dma mode enable (1) or disable (0) | ||
398 | * @u32_count: number of bytes to be transferred | ||
399 | * @is_write: prefetch read(0) or write post(1) mode | ||
400 | */ | ||
401 | int gpmc_prefetch_enable(int cs, int dma_mode, | ||
402 | unsigned int u32_count, int is_write) | ||
403 | { | ||
404 | uint32_t prefetch_config1; | ||
405 | |||
406 | if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { | ||
407 | /* Set the amount of bytes to be prefetched */ | ||
408 | gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count); | ||
409 | |||
410 | /* Set dma/mpu mode, the prefetch read / post write and | ||
411 | * enable the engine. Set which cs is has requested for. | ||
412 | */ | ||
413 | prefetch_config1 = ((cs << CS_NUM_SHIFT) | | ||
414 | PREFETCH_FIFOTHRESHOLD | | ||
415 | ENABLE_PREFETCH | | ||
416 | (dma_mode << DMA_MPU_MODE) | | ||
417 | (0x1 & is_write)); | ||
418 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1); | ||
419 | } else { | ||
420 | return -EBUSY; | ||
421 | } | ||
422 | /* Start the prefetch engine */ | ||
423 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1); | ||
424 | |||
425 | return 0; | ||
426 | } | ||
427 | EXPORT_SYMBOL(gpmc_prefetch_enable); | ||
428 | |||
429 | /** | ||
430 | * gpmc_prefetch_reset - disables and stops the prefetch engine | ||
431 | */ | ||
432 | void gpmc_prefetch_reset(void) | ||
433 | { | ||
434 | /* Stop the PFPW engine */ | ||
435 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0); | ||
436 | |||
437 | /* Reset/disable the PFPW engine */ | ||
438 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0); | ||
439 | } | ||
440 | EXPORT_SYMBOL(gpmc_prefetch_reset); | ||
441 | |||
442 | /** | ||
443 | * gpmc_prefetch_status - reads prefetch status of engine | ||
444 | */ | ||
445 | int gpmc_prefetch_status(void) | ||
446 | { | ||
447 | return gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
448 | } | ||
449 | EXPORT_SYMBOL(gpmc_prefetch_status); | ||
450 | |||
389 | static void __init gpmc_mem_init(void) | 451 | static void __init gpmc_mem_init(void) |
390 | { | 452 | { |
391 | int cs; | 453 | int cs; |
@@ -452,6 +514,5 @@ void __init gpmc_init(void) | |||
452 | l &= 0x03 << 3; | 514 | l &= 0x03 << 3; |
453 | l |= (0x02 << 3) | (1 << 0); | 515 | l |= (0x02 << 3) | (1 << 0); |
454 | gpmc_write_reg(GPMC_SYSCONFIG, l); | 516 | gpmc_write_reg(GPMC_SYSCONFIG, l); |
455 | |||
456 | gpmc_mem_init(); | 517 | gpmc_mem_init(); |
457 | } | 518 | } |
diff --git a/arch/arm/plat-omap/include/mach/gpmc.h b/arch/arm/plat-omap/include/mach/gpmc.h index 921b16532ff5..9c99cda77ba6 100644 --- a/arch/arm/plat-omap/include/mach/gpmc.h +++ b/arch/arm/plat-omap/include/mach/gpmc.h | |||
@@ -103,6 +103,10 @@ extern int gpmc_cs_request(int cs, unsigned long size, unsigned long *base); | |||
103 | extern void gpmc_cs_free(int cs); | 103 | extern void gpmc_cs_free(int cs); |
104 | extern int gpmc_cs_set_reserved(int cs, int reserved); | 104 | extern int gpmc_cs_set_reserved(int cs, int reserved); |
105 | extern int gpmc_cs_reserved(int cs); | 105 | extern int gpmc_cs_reserved(int cs); |
106 | extern int gpmc_prefetch_enable(int cs, int dma_mode, | ||
107 | unsigned int u32_count, int is_write); | ||
108 | extern void gpmc_prefetch_reset(void); | ||
109 | extern int gpmc_prefetch_status(void); | ||
106 | extern void __init gpmc_init(void); | 110 | extern void __init gpmc_init(void); |
107 | 111 | ||
108 | #endif | 112 | #endif |
diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 707d7ee495df..7dab79caed44 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig | |||
@@ -80,6 +80,14 @@ config MTD_NAND_OMAP2 | |||
80 | help | 80 | help |
81 | Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. | 81 | Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. |
82 | 82 | ||
83 | config MTD_NAND_OMAP_PREFETCH | ||
84 | bool "GPMC prefetch support for NAND Flash device" | ||
85 | depends on MTD_NAND && MTD_NAND_OMAP2 | ||
86 | default y | ||
87 | help | ||
88 | The NAND device can be accessed for Read/Write using GPMC PREFETCH engine | ||
89 | to improve the performance. | ||
90 | |||
83 | config MTD_NAND_TS7250 | 91 | config MTD_NAND_TS7250 |
84 | tristate "NAND Flash device on TS-7250 board" | 92 | tristate "NAND Flash device on TS-7250 board" |
85 | depends on MACH_TS72XX | 93 | depends on MACH_TS72XX |
diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index ebd07e95b814..6736822c4751 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c | |||
@@ -112,6 +112,16 @@ | |||
112 | static const char *part_probes[] = { "cmdlinepart", NULL }; | 112 | static const char *part_probes[] = { "cmdlinepart", NULL }; |
113 | #endif | 113 | #endif |
114 | 114 | ||
115 | #ifdef CONFIG_MTD_NAND_OMAP_PREFETCH | ||
116 | static int use_prefetch = 1; | ||
117 | |||
118 | /* "modprobe ... use_prefetch=0" etc */ | ||
119 | module_param(use_prefetch, bool, 0); | ||
120 | MODULE_PARM_DESC(use_prefetch, "enable/disable use of PREFETCH"); | ||
121 | #else | ||
122 | const int use_prefetch; | ||
123 | #endif | ||
124 | |||
115 | struct omap_nand_info { | 125 | struct omap_nand_info { |
116 | struct nand_hw_control controller; | 126 | struct nand_hw_control controller; |
117 | struct omap_nand_platform_data *pdata; | 127 | struct omap_nand_platform_data *pdata; |
@@ -124,6 +134,7 @@ struct omap_nand_info { | |||
124 | unsigned long phys_base; | 134 | unsigned long phys_base; |
125 | void __iomem *gpmc_cs_baseaddr; | 135 | void __iomem *gpmc_cs_baseaddr; |
126 | void __iomem *gpmc_baseaddr; | 136 | void __iomem *gpmc_baseaddr; |
137 | void __iomem *nand_pref_fifo_add; | ||
127 | }; | 138 | }; |
128 | 139 | ||
129 | /** | 140 | /** |
@@ -189,6 +200,38 @@ static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) | |||
189 | } | 200 | } |
190 | 201 | ||
191 | /** | 202 | /** |
203 | * omap_read_buf8 - read data from NAND controller into buffer | ||
204 | * @mtd: MTD device structure | ||
205 | * @buf: buffer to store date | ||
206 | * @len: number of bytes to read | ||
207 | */ | ||
208 | static void omap_read_buf8(struct mtd_info *mtd, u_char *buf, int len) | ||
209 | { | ||
210 | struct nand_chip *nand = mtd->priv; | ||
211 | |||
212 | ioread8_rep(nand->IO_ADDR_R, buf, len); | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * omap_write_buf8 - write buffer to NAND controller | ||
217 | * @mtd: MTD device structure | ||
218 | * @buf: data buffer | ||
219 | * @len: number of bytes to write | ||
220 | */ | ||
221 | static void omap_write_buf8(struct mtd_info *mtd, const u_char *buf, int len) | ||
222 | { | ||
223 | struct omap_nand_info *info = container_of(mtd, | ||
224 | struct omap_nand_info, mtd); | ||
225 | u_char *p = (u_char *)buf; | ||
226 | |||
227 | while (len--) { | ||
228 | iowrite8(*p++, info->nand.IO_ADDR_W); | ||
229 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | ||
230 | GPMC_STATUS) & GPMC_BUF_FULL)); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | /** | ||
192 | * omap_read_buf16 - read data from NAND controller into buffer | 235 | * omap_read_buf16 - read data from NAND controller into buffer |
193 | * @mtd: MTD device structure | 236 | * @mtd: MTD device structure |
194 | * @buf: buffer to store date | 237 | * @buf: buffer to store date |
@@ -198,7 +241,7 @@ static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) | |||
198 | { | 241 | { |
199 | struct nand_chip *nand = mtd->priv; | 242 | struct nand_chip *nand = mtd->priv; |
200 | 243 | ||
201 | __raw_readsw(nand->IO_ADDR_R, buf, len / 2); | 244 | ioread16_rep(nand->IO_ADDR_R, buf, len / 2); |
202 | } | 245 | } |
203 | 246 | ||
204 | /** | 247 | /** |
@@ -217,13 +260,101 @@ static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) | |||
217 | len >>= 1; | 260 | len >>= 1; |
218 | 261 | ||
219 | while (len--) { | 262 | while (len--) { |
220 | writew(*p++, info->nand.IO_ADDR_W); | 263 | iowrite16(*p++, info->nand.IO_ADDR_W); |
221 | 264 | ||
222 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + | 265 | while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + |
223 | GPMC_STATUS) & GPMC_BUF_FULL)) | 266 | GPMC_STATUS) & GPMC_BUF_FULL)) |
224 | ; | 267 | ; |
225 | } | 268 | } |
226 | } | 269 | } |
270 | |||
271 | /** | ||
272 | * omap_read_buf_pref - read data from NAND controller into buffer | ||
273 | * @mtd: MTD device structure | ||
274 | * @buf: buffer to store date | ||
275 | * @len: number of bytes to read | ||
276 | */ | ||
277 | static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) | ||
278 | { | ||
279 | struct omap_nand_info *info = container_of(mtd, | ||
280 | struct omap_nand_info, mtd); | ||
281 | uint32_t pfpw_status = 0, r_count = 0; | ||
282 | int ret = 0; | ||
283 | u32 *p = (u32 *)buf; | ||
284 | |||
285 | /* take care of subpage reads */ | ||
286 | for (; len % 4 != 0; ) { | ||
287 | *buf++ = __raw_readb(info->nand.IO_ADDR_R); | ||
288 | len--; | ||
289 | } | ||
290 | p = (u32 *) buf; | ||
291 | |||
292 | /* configure and start prefetch transfer */ | ||
293 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); | ||
294 | if (ret) { | ||
295 | /* PFPW engine is busy, use cpu copy method */ | ||
296 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
297 | omap_read_buf16(mtd, buf, len); | ||
298 | else | ||
299 | omap_read_buf8(mtd, buf, len); | ||
300 | } else { | ||
301 | do { | ||
302 | pfpw_status = gpmc_prefetch_status(); | ||
303 | r_count = ((pfpw_status >> 24) & 0x7F) >> 2; | ||
304 | ioread32_rep(info->nand_pref_fifo_add, p, r_count); | ||
305 | p += r_count; | ||
306 | len -= r_count << 2; | ||
307 | } while (len); | ||
308 | |||
309 | /* disable and stop the PFPW engine */ | ||
310 | gpmc_prefetch_reset(); | ||
311 | } | ||
312 | } | ||
313 | |||
314 | /** | ||
315 | * omap_write_buf_pref - write buffer to NAND controller | ||
316 | * @mtd: MTD device structure | ||
317 | * @buf: data buffer | ||
318 | * @len: number of bytes to write | ||
319 | */ | ||
320 | static void omap_write_buf_pref(struct mtd_info *mtd, | ||
321 | const u_char *buf, int len) | ||
322 | { | ||
323 | struct omap_nand_info *info = container_of(mtd, | ||
324 | struct omap_nand_info, mtd); | ||
325 | uint32_t pfpw_status = 0, w_count = 0; | ||
326 | int i = 0, ret = 0; | ||
327 | u16 *p = (u16 *) buf; | ||
328 | |||
329 | /* take care of subpage writes */ | ||
330 | if (len % 2 != 0) { | ||
331 | writeb(*buf, info->nand.IO_ADDR_R); | ||
332 | p = (u16 *)(buf + 1); | ||
333 | len--; | ||
334 | } | ||
335 | |||
336 | /* configure and start prefetch transfer */ | ||
337 | ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x1); | ||
338 | if (ret) { | ||
339 | /* PFPW engine is busy, use cpu copy method */ | ||
340 | if (info->nand.options & NAND_BUSWIDTH_16) | ||
341 | omap_write_buf16(mtd, buf, len); | ||
342 | else | ||
343 | omap_write_buf8(mtd, buf, len); | ||
344 | } else { | ||
345 | pfpw_status = gpmc_prefetch_status(); | ||
346 | while (pfpw_status & 0x3FFF) { | ||
347 | w_count = ((pfpw_status >> 24) & 0x7F) >> 1; | ||
348 | for (i = 0; (i < w_count) && len; i++, len -= 2) | ||
349 | iowrite16(*p++, info->nand_pref_fifo_add); | ||
350 | pfpw_status = gpmc_prefetch_status(); | ||
351 | } | ||
352 | |||
353 | /* disable and stop the PFPW engine */ | ||
354 | gpmc_prefetch_reset(); | ||
355 | } | ||
356 | } | ||
357 | |||
227 | /** | 358 | /** |
228 | * omap_verify_buf - Verify chip data against buffer | 359 | * omap_verify_buf - Verify chip data against buffer |
229 | * @mtd: MTD device structure | 360 | * @mtd: MTD device structure |
@@ -658,17 +789,12 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
658 | err = -ENOMEM; | 789 | err = -ENOMEM; |
659 | goto out_release_mem_region; | 790 | goto out_release_mem_region; |
660 | } | 791 | } |
792 | |||
661 | info->nand.controller = &info->controller; | 793 | info->nand.controller = &info->controller; |
662 | 794 | ||
663 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; | 795 | info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; |
664 | info->nand.cmd_ctrl = omap_hwcontrol; | 796 | info->nand.cmd_ctrl = omap_hwcontrol; |
665 | 797 | ||
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 | /* | 798 | /* |
673 | * If RDY/BSY line is connected to OMAP then use the omap ready | 799 | * 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 | 800 | * funcrtion and the generic nand_wait function which reads the status |
@@ -689,6 +815,23 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) | |||
689 | == 0x1000) | 815 | == 0x1000) |
690 | info->nand.options |= NAND_BUSWIDTH_16; | 816 | info->nand.options |= NAND_BUSWIDTH_16; |
691 | 817 | ||
818 | if (use_prefetch) { | ||
819 | /* copy the virtual address of nand base for fifo access */ | ||
820 | info->nand_pref_fifo_add = info->nand.IO_ADDR_R; | ||
821 | |||
822 | info->nand.read_buf = omap_read_buf_pref; | ||
823 | info->nand.write_buf = omap_write_buf_pref; | ||
824 | } else { | ||
825 | if (info->nand.options & NAND_BUSWIDTH_16) { | ||
826 | info->nand.read_buf = omap_read_buf16; | ||
827 | info->nand.write_buf = omap_write_buf16; | ||
828 | } else { | ||
829 | info->nand.read_buf = omap_read_buf8; | ||
830 | info->nand.write_buf = omap_write_buf8; | ||
831 | } | ||
832 | } | ||
833 | info->nand.verify_buf = omap_verify_buf; | ||
834 | |||
692 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC | 835 | #ifdef CONFIG_MTD_NAND_OMAP_HWECC |
693 | info->nand.ecc.bytes = 3; | 836 | info->nand.ecc.bytes = 3; |
694 | info->nand.ecc.size = 512; | 837 | info->nand.ecc.size = 512; |
@@ -746,7 +889,7 @@ static int omap_nand_remove(struct platform_device *pdev) | |||
746 | platform_set_drvdata(pdev, NULL); | 889 | platform_set_drvdata(pdev, NULL); |
747 | /* Release NAND device, its internal structures and partitions */ | 890 | /* Release NAND device, its internal structures and partitions */ |
748 | nand_release(&info->mtd); | 891 | nand_release(&info->mtd); |
749 | iounmap(info->nand.IO_ADDR_R); | 892 | iounmap(info->nand_pref_fifo_add); |
750 | kfree(&info->mtd); | 893 | kfree(&info->mtd); |
751 | return 0; | 894 | return 0; |
752 | } | 895 | } |