diff options
Diffstat (limited to 'arch/arm/mach-omap2/gpmc.c')
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 284 |
1 files changed, 263 insertions, 21 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 5bc3ca03551c..f46933bc9373 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
@@ -46,8 +46,9 @@ | |||
46 | #define GPMC_ECC_CONFIG 0x1f4 | 46 | #define GPMC_ECC_CONFIG 0x1f4 |
47 | #define GPMC_ECC_CONTROL 0x1f8 | 47 | #define GPMC_ECC_CONTROL 0x1f8 |
48 | #define GPMC_ECC_SIZE_CONFIG 0x1fc | 48 | #define GPMC_ECC_SIZE_CONFIG 0x1fc |
49 | #define GPMC_ECC1_RESULT 0x200 | ||
49 | 50 | ||
50 | #define GPMC_CS0 0x60 | 51 | #define GPMC_CS0_OFFSET 0x60 |
51 | #define GPMC_CS_SIZE 0x30 | 52 | #define GPMC_CS_SIZE 0x30 |
52 | 53 | ||
53 | #define GPMC_MEM_START 0x00000000 | 54 | #define GPMC_MEM_START 0x00000000 |
@@ -92,7 +93,8 @@ struct omap3_gpmc_regs { | |||
92 | static struct resource gpmc_mem_root; | 93 | static struct resource gpmc_mem_root; |
93 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; | 94 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; |
94 | static DEFINE_SPINLOCK(gpmc_mem_lock); | 95 | static DEFINE_SPINLOCK(gpmc_mem_lock); |
95 | static unsigned gpmc_cs_map; | 96 | static unsigned int gpmc_cs_map; /* flag for cs which are initialized */ |
97 | static int gpmc_ecc_used = -EINVAL; /* cs using ecc engine */ | ||
96 | 98 | ||
97 | static void __iomem *gpmc_base; | 99 | static void __iomem *gpmc_base; |
98 | 100 | ||
@@ -108,11 +110,27 @@ static u32 gpmc_read_reg(int idx) | |||
108 | return __raw_readl(gpmc_base + idx); | 110 | return __raw_readl(gpmc_base + idx); |
109 | } | 111 | } |
110 | 112 | ||
113 | static void gpmc_cs_write_byte(int cs, int idx, u8 val) | ||
114 | { | ||
115 | void __iomem *reg_addr; | ||
116 | |||
117 | reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; | ||
118 | __raw_writeb(val, reg_addr); | ||
119 | } | ||
120 | |||
121 | static u8 gpmc_cs_read_byte(int cs, int idx) | ||
122 | { | ||
123 | void __iomem *reg_addr; | ||
124 | |||
125 | reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; | ||
126 | return __raw_readb(reg_addr); | ||
127 | } | ||
128 | |||
111 | void gpmc_cs_write_reg(int cs, int idx, u32 val) | 129 | void gpmc_cs_write_reg(int cs, int idx, u32 val) |
112 | { | 130 | { |
113 | void __iomem *reg_addr; | 131 | void __iomem *reg_addr; |
114 | 132 | ||
115 | reg_addr = gpmc_base + GPMC_CS0 + (cs * GPMC_CS_SIZE) + idx; | 133 | reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; |
116 | __raw_writel(val, reg_addr); | 134 | __raw_writel(val, reg_addr); |
117 | } | 135 | } |
118 | 136 | ||
@@ -120,7 +138,7 @@ u32 gpmc_cs_read_reg(int cs, int idx) | |||
120 | { | 138 | { |
121 | void __iomem *reg_addr; | 139 | void __iomem *reg_addr; |
122 | 140 | ||
123 | reg_addr = gpmc_base + GPMC_CS0 + (cs * GPMC_CS_SIZE) + idx; | 141 | reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; |
124 | return __raw_readl(reg_addr); | 142 | return __raw_readl(reg_addr); |
125 | } | 143 | } |
126 | 144 | ||
@@ -419,8 +437,157 @@ void gpmc_cs_free(int cs) | |||
419 | EXPORT_SYMBOL(gpmc_cs_free); | 437 | EXPORT_SYMBOL(gpmc_cs_free); |
420 | 438 | ||
421 | /** | 439 | /** |
440 | * gpmc_read_status - read access request to get the different gpmc status | ||
441 | * @cmd: command type | ||
442 | * @return status | ||
443 | */ | ||
444 | int gpmc_read_status(int cmd) | ||
445 | { | ||
446 | int status = -EINVAL; | ||
447 | u32 regval = 0; | ||
448 | |||
449 | switch (cmd) { | ||
450 | case GPMC_GET_IRQ_STATUS: | ||
451 | status = gpmc_read_reg(GPMC_IRQSTATUS); | ||
452 | break; | ||
453 | |||
454 | case GPMC_PREFETCH_FIFO_CNT: | ||
455 | regval = gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
456 | status = GPMC_PREFETCH_STATUS_FIFO_CNT(regval); | ||
457 | break; | ||
458 | |||
459 | case GPMC_PREFETCH_COUNT: | ||
460 | regval = gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
461 | status = GPMC_PREFETCH_STATUS_COUNT(regval); | ||
462 | break; | ||
463 | |||
464 | case GPMC_STATUS_BUFFER: | ||
465 | regval = gpmc_read_reg(GPMC_STATUS); | ||
466 | /* 1 : buffer is available to write */ | ||
467 | status = regval & GPMC_STATUS_BUFF_EMPTY; | ||
468 | break; | ||
469 | |||
470 | default: | ||
471 | printk(KERN_ERR "gpmc_read_status: Not supported\n"); | ||
472 | } | ||
473 | return status; | ||
474 | } | ||
475 | EXPORT_SYMBOL(gpmc_read_status); | ||
476 | |||
477 | /** | ||
478 | * gpmc_cs_configure - write request to configure gpmc | ||
479 | * @cs: chip select number | ||
480 | * @cmd: command type | ||
481 | * @wval: value to write | ||
482 | * @return status of the operation | ||
483 | */ | ||
484 | int gpmc_cs_configure(int cs, int cmd, int wval) | ||
485 | { | ||
486 | int err = 0; | ||
487 | u32 regval = 0; | ||
488 | |||
489 | switch (cmd) { | ||
490 | case GPMC_SET_IRQ_STATUS: | ||
491 | gpmc_write_reg(GPMC_IRQSTATUS, wval); | ||
492 | break; | ||
493 | |||
494 | case GPMC_CONFIG_WP: | ||
495 | regval = gpmc_read_reg(GPMC_CONFIG); | ||
496 | if (wval) | ||
497 | regval &= ~GPMC_CONFIG_WRITEPROTECT; /* WP is ON */ | ||
498 | else | ||
499 | regval |= GPMC_CONFIG_WRITEPROTECT; /* WP is OFF */ | ||
500 | gpmc_write_reg(GPMC_CONFIG, regval); | ||
501 | break; | ||
502 | |||
503 | case GPMC_CONFIG_RDY_BSY: | ||
504 | regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); | ||
505 | if (wval) | ||
506 | regval |= WR_RD_PIN_MONITORING; | ||
507 | else | ||
508 | regval &= ~WR_RD_PIN_MONITORING; | ||
509 | gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); | ||
510 | break; | ||
511 | |||
512 | case GPMC_CONFIG_DEV_SIZE: | ||
513 | regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); | ||
514 | regval |= GPMC_CONFIG1_DEVICESIZE(wval); | ||
515 | gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); | ||
516 | break; | ||
517 | |||
518 | case GPMC_CONFIG_DEV_TYPE: | ||
519 | regval = gpmc_cs_read_reg(cs, GPMC_CS_CONFIG1); | ||
520 | regval |= GPMC_CONFIG1_DEVICETYPE(wval); | ||
521 | if (wval == GPMC_DEVICETYPE_NOR) | ||
522 | regval |= GPMC_CONFIG1_MUXADDDATA; | ||
523 | gpmc_cs_write_reg(cs, GPMC_CS_CONFIG1, regval); | ||
524 | break; | ||
525 | |||
526 | default: | ||
527 | printk(KERN_ERR "gpmc_configure_cs: Not supported\n"); | ||
528 | err = -EINVAL; | ||
529 | } | ||
530 | |||
531 | return err; | ||
532 | } | ||
533 | EXPORT_SYMBOL(gpmc_cs_configure); | ||
534 | |||
535 | /** | ||
536 | * gpmc_nand_read - nand specific read access request | ||
537 | * @cs: chip select number | ||
538 | * @cmd: command type | ||
539 | */ | ||
540 | int gpmc_nand_read(int cs, int cmd) | ||
541 | { | ||
542 | int rval = -EINVAL; | ||
543 | |||
544 | switch (cmd) { | ||
545 | case GPMC_NAND_DATA: | ||
546 | rval = gpmc_cs_read_byte(cs, GPMC_CS_NAND_DATA); | ||
547 | break; | ||
548 | |||
549 | default: | ||
550 | printk(KERN_ERR "gpmc_read_nand_ctrl: Not supported\n"); | ||
551 | } | ||
552 | return rval; | ||
553 | } | ||
554 | EXPORT_SYMBOL(gpmc_nand_read); | ||
555 | |||
556 | /** | ||
557 | * gpmc_nand_write - nand specific write request | ||
558 | * @cs: chip select number | ||
559 | * @cmd: command type | ||
560 | * @wval: value to write | ||
561 | */ | ||
562 | int gpmc_nand_write(int cs, int cmd, int wval) | ||
563 | { | ||
564 | int err = 0; | ||
565 | |||
566 | switch (cmd) { | ||
567 | case GPMC_NAND_COMMAND: | ||
568 | gpmc_cs_write_byte(cs, GPMC_CS_NAND_COMMAND, wval); | ||
569 | break; | ||
570 | |||
571 | case GPMC_NAND_ADDRESS: | ||
572 | gpmc_cs_write_byte(cs, GPMC_CS_NAND_ADDRESS, wval); | ||
573 | break; | ||
574 | |||
575 | case GPMC_NAND_DATA: | ||
576 | gpmc_cs_write_byte(cs, GPMC_CS_NAND_DATA, wval); | ||
577 | |||
578 | default: | ||
579 | printk(KERN_ERR "gpmc_write_nand_ctrl: Not supported\n"); | ||
580 | err = -EINVAL; | ||
581 | } | ||
582 | return err; | ||
583 | } | ||
584 | EXPORT_SYMBOL(gpmc_nand_write); | ||
585 | |||
586 | |||
587 | |||
588 | /** | ||
422 | * gpmc_prefetch_enable - configures and starts prefetch transfer | 589 | * gpmc_prefetch_enable - configures and starts prefetch transfer |
423 | * @cs: nand cs (chip select) number | 590 | * @cs: cs (chip select) number |
424 | * @dma_mode: dma mode enable (1) or disable (0) | 591 | * @dma_mode: dma mode enable (1) or disable (0) |
425 | * @u32_count: number of bytes to be transferred | 592 | * @u32_count: number of bytes to be transferred |
426 | * @is_write: prefetch read(0) or write post(1) mode | 593 | * @is_write: prefetch read(0) or write post(1) mode |
@@ -428,7 +595,6 @@ EXPORT_SYMBOL(gpmc_cs_free); | |||
428 | int gpmc_prefetch_enable(int cs, int dma_mode, | 595 | int gpmc_prefetch_enable(int cs, int dma_mode, |
429 | unsigned int u32_count, int is_write) | 596 | unsigned int u32_count, int is_write) |
430 | { | 597 | { |
431 | uint32_t prefetch_config1; | ||
432 | 598 | ||
433 | if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { | 599 | if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { |
434 | /* Set the amount of bytes to be prefetched */ | 600 | /* Set the amount of bytes to be prefetched */ |
@@ -437,17 +603,17 @@ int gpmc_prefetch_enable(int cs, int dma_mode, | |||
437 | /* Set dma/mpu mode, the prefetch read / post write and | 603 | /* Set dma/mpu mode, the prefetch read / post write and |
438 | * enable the engine. Set which cs is has requested for. | 604 | * enable the engine. Set which cs is has requested for. |
439 | */ | 605 | */ |
440 | prefetch_config1 = ((cs << CS_NUM_SHIFT) | | 606 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, ((cs << CS_NUM_SHIFT) | |
441 | PREFETCH_FIFOTHRESHOLD | | 607 | PREFETCH_FIFOTHRESHOLD | |
442 | ENABLE_PREFETCH | | 608 | ENABLE_PREFETCH | |
443 | (dma_mode << DMA_MPU_MODE) | | 609 | (dma_mode << DMA_MPU_MODE) | |
444 | (0x1 & is_write)); | 610 | (0x1 & is_write))); |
445 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, prefetch_config1); | 611 | |
612 | /* Start the prefetch engine */ | ||
613 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1); | ||
446 | } else { | 614 | } else { |
447 | return -EBUSY; | 615 | return -EBUSY; |
448 | } | 616 | } |
449 | /* Start the prefetch engine */ | ||
450 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1); | ||
451 | 617 | ||
452 | return 0; | 618 | return 0; |
453 | } | 619 | } |
@@ -456,24 +622,24 @@ EXPORT_SYMBOL(gpmc_prefetch_enable); | |||
456 | /** | 622 | /** |
457 | * gpmc_prefetch_reset - disables and stops the prefetch engine | 623 | * gpmc_prefetch_reset - disables and stops the prefetch engine |
458 | */ | 624 | */ |
459 | void gpmc_prefetch_reset(void) | 625 | int gpmc_prefetch_reset(int cs) |
460 | { | 626 | { |
627 | u32 config1; | ||
628 | |||
629 | /* check if the same module/cs is trying to reset */ | ||
630 | config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1); | ||
631 | if (((config1 >> CS_NUM_SHIFT) & 0x7) != cs) | ||
632 | return -EINVAL; | ||
633 | |||
461 | /* Stop the PFPW engine */ | 634 | /* Stop the PFPW engine */ |
462 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0); | 635 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0); |
463 | 636 | ||
464 | /* Reset/disable the PFPW engine */ | 637 | /* Reset/disable the PFPW engine */ |
465 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0); | 638 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0); |
466 | } | ||
467 | EXPORT_SYMBOL(gpmc_prefetch_reset); | ||
468 | 639 | ||
469 | /** | 640 | return 0; |
470 | * gpmc_prefetch_status - reads prefetch status of engine | ||
471 | */ | ||
472 | int gpmc_prefetch_status(void) | ||
473 | { | ||
474 | return gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
475 | } | 641 | } |
476 | EXPORT_SYMBOL(gpmc_prefetch_status); | 642 | EXPORT_SYMBOL(gpmc_prefetch_reset); |
477 | 643 | ||
478 | static void __init gpmc_mem_init(void) | 644 | static void __init gpmc_mem_init(void) |
479 | { | 645 | { |
@@ -615,3 +781,79 @@ void omap3_gpmc_restore_context(void) | |||
615 | } | 781 | } |
616 | } | 782 | } |
617 | #endif /* CONFIG_ARCH_OMAP3 */ | 783 | #endif /* CONFIG_ARCH_OMAP3 */ |
784 | |||
785 | /** | ||
786 | * gpmc_enable_hwecc - enable hardware ecc functionality | ||
787 | * @cs: chip select number | ||
788 | * @mode: read/write mode | ||
789 | * @dev_width: device bus width(1 for x16, 0 for x8) | ||
790 | * @ecc_size: bytes for which ECC will be generated | ||
791 | */ | ||
792 | int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size) | ||
793 | { | ||
794 | unsigned int val; | ||
795 | |||
796 | /* check if ecc module is in used */ | ||
797 | if (gpmc_ecc_used != -EINVAL) | ||
798 | return -EINVAL; | ||
799 | |||
800 | gpmc_ecc_used = cs; | ||
801 | |||
802 | /* clear ecc and enable bits */ | ||
803 | val = ((0x00000001<<8) | 0x00000001); | ||
804 | gpmc_write_reg(GPMC_ECC_CONTROL, val); | ||
805 | |||
806 | /* program ecc and result sizes */ | ||
807 | val = ((((ecc_size >> 1) - 1) << 22) | (0x0000000F)); | ||
808 | gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, val); | ||
809 | |||
810 | switch (mode) { | ||
811 | case GPMC_ECC_READ: | ||
812 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | ||
813 | break; | ||
814 | case GPMC_ECC_READSYN: | ||
815 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x100); | ||
816 | break; | ||
817 | case GPMC_ECC_WRITE: | ||
818 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | ||
819 | break; | ||
820 | default: | ||
821 | printk(KERN_INFO "Error: Unrecognized Mode[%d]!\n", mode); | ||
822 | break; | ||
823 | } | ||
824 | |||
825 | /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ | ||
826 | val = (dev_width << 7) | (cs << 1) | (0x1); | ||
827 | gpmc_write_reg(GPMC_ECC_CONFIG, val); | ||
828 | return 0; | ||
829 | } | ||
830 | |||
831 | /** | ||
832 | * gpmc_calculate_ecc - generate non-inverted ecc bytes | ||
833 | * @cs: chip select number | ||
834 | * @dat: data pointer over which ecc is computed | ||
835 | * @ecc_code: ecc code buffer | ||
836 | * | ||
837 | * Using non-inverted ECC is considered ugly since writing a blank | ||
838 | * page (padding) will clear the ECC bytes. This is not a problem as long | ||
839 | * no one is trying to write data on the seemingly unused page. Reading | ||
840 | * an erased page will produce an ECC mismatch between generated and read | ||
841 | * ECC bytes that has to be dealt with separately. | ||
842 | */ | ||
843 | int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code) | ||
844 | { | ||
845 | unsigned int val = 0x0; | ||
846 | |||
847 | if (gpmc_ecc_used != cs) | ||
848 | return -EINVAL; | ||
849 | |||
850 | /* read ecc result */ | ||
851 | val = gpmc_read_reg(GPMC_ECC1_RESULT); | ||
852 | *ecc_code++ = val; /* P128e, ..., P1e */ | ||
853 | *ecc_code++ = val >> 16; /* P128o, ..., P1o */ | ||
854 | /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ | ||
855 | *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); | ||
856 | |||
857 | gpmc_ecc_used = -EINVAL; | ||
858 | return 0; | ||
859 | } | ||