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 f91934b2b09..15876828db2 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 921b16532ff..9c99cda77ba 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 707d7ee495d..7dab79caed4 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 ebd07e95b81..6736822c475 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 | } |
