diff options
Diffstat (limited to 'arch/arm/mach-omap2/gpmc.c')
-rw-r--r-- | arch/arm/mach-omap2/gpmc.c | 464 |
1 files changed, 23 insertions, 441 deletions
diff --git a/arch/arm/mach-omap2/gpmc.c b/arch/arm/mach-omap2/gpmc.c index 92b5718fa722..bf6117c32f4b 100644 --- a/arch/arm/mach-omap2/gpmc.c +++ b/arch/arm/mach-omap2/gpmc.c | |||
@@ -26,16 +26,14 @@ | |||
26 | #include <linux/interrupt.h> | 26 | #include <linux/interrupt.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | 28 | ||
29 | #include <asm/mach-types.h> | 29 | #include <linux/platform_data/mtd-nand-omap2.h> |
30 | #include <plat/gpmc.h> | ||
31 | 30 | ||
32 | #include <plat/cpu.h> | 31 | #include <asm/mach-types.h> |
33 | #include <plat/gpmc.h> | ||
34 | #include <plat/sdrc.h> | ||
35 | #include <plat/omap_device.h> | ||
36 | 32 | ||
37 | #include "soc.h" | 33 | #include "soc.h" |
38 | #include "common.h" | 34 | #include "common.h" |
35 | #include "omap_device.h" | ||
36 | #include "gpmc.h" | ||
39 | 37 | ||
40 | #define DEVICE_NAME "omap-gpmc" | 38 | #define DEVICE_NAME "omap-gpmc" |
41 | 39 | ||
@@ -59,6 +57,9 @@ | |||
59 | #define GPMC_ECC_SIZE_CONFIG 0x1fc | 57 | #define GPMC_ECC_SIZE_CONFIG 0x1fc |
60 | #define GPMC_ECC1_RESULT 0x200 | 58 | #define GPMC_ECC1_RESULT 0x200 |
61 | #define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */ | 59 | #define GPMC_ECC_BCH_RESULT_0 0x240 /* not available on OMAP2 */ |
60 | #define GPMC_ECC_BCH_RESULT_1 0x244 /* not available on OMAP2 */ | ||
61 | #define GPMC_ECC_BCH_RESULT_2 0x248 /* not available on OMAP2 */ | ||
62 | #define GPMC_ECC_BCH_RESULT_3 0x24c /* not available on OMAP2 */ | ||
62 | 63 | ||
63 | /* GPMC ECC control settings */ | 64 | /* GPMC ECC control settings */ |
64 | #define GPMC_ECC_CTRL_ECCCLEAR 0x100 | 65 | #define GPMC_ECC_CTRL_ECCCLEAR 0x100 |
@@ -75,6 +76,7 @@ | |||
75 | 76 | ||
76 | #define GPMC_CS0_OFFSET 0x60 | 77 | #define GPMC_CS0_OFFSET 0x60 |
77 | #define GPMC_CS_SIZE 0x30 | 78 | #define GPMC_CS_SIZE 0x30 |
79 | #define GPMC_BCH_SIZE 0x10 | ||
78 | 80 | ||
79 | #define GPMC_MEM_START 0x00000000 | 81 | #define GPMC_MEM_START 0x00000000 |
80 | #define GPMC_MEM_END 0x3FFFFFFF | 82 | #define GPMC_MEM_END 0x3FFFFFFF |
@@ -137,7 +139,6 @@ static struct resource gpmc_mem_root; | |||
137 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; | 139 | static struct resource gpmc_cs_mem[GPMC_CS_NUM]; |
138 | static DEFINE_SPINLOCK(gpmc_mem_lock); | 140 | static DEFINE_SPINLOCK(gpmc_mem_lock); |
139 | static unsigned int gpmc_cs_map; /* flag for cs which are initialized */ | 141 | static unsigned int gpmc_cs_map; /* flag for cs which are initialized */ |
140 | static int gpmc_ecc_used = -EINVAL; /* cs using ecc engine */ | ||
141 | static struct device *gpmc_dev; | 142 | static struct device *gpmc_dev; |
142 | static int gpmc_irq; | 143 | static int gpmc_irq; |
143 | static resource_size_t phys_base, mem_size; | 144 | static resource_size_t phys_base, mem_size; |
@@ -158,22 +159,6 @@ static u32 gpmc_read_reg(int idx) | |||
158 | return __raw_readl(gpmc_base + idx); | 159 | return __raw_readl(gpmc_base + idx); |
159 | } | 160 | } |
160 | 161 | ||
161 | static void gpmc_cs_write_byte(int cs, int idx, u8 val) | ||
162 | { | ||
163 | void __iomem *reg_addr; | ||
164 | |||
165 | reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; | ||
166 | __raw_writeb(val, reg_addr); | ||
167 | } | ||
168 | |||
169 | static u8 gpmc_cs_read_byte(int cs, int idx) | ||
170 | { | ||
171 | void __iomem *reg_addr; | ||
172 | |||
173 | reg_addr = gpmc_base + GPMC_CS0_OFFSET + (cs * GPMC_CS_SIZE) + idx; | ||
174 | return __raw_readb(reg_addr); | ||
175 | } | ||
176 | |||
177 | void gpmc_cs_write_reg(int cs, int idx, u32 val) | 162 | void gpmc_cs_write_reg(int cs, int idx, u32 val) |
178 | { | 163 | { |
179 | void __iomem *reg_addr; | 164 | void __iomem *reg_addr; |
@@ -288,7 +273,7 @@ static int set_gpmc_timing_reg(int cs, int reg, int st_bit, int end_bit, | |||
288 | return -1 | 273 | return -1 |
289 | #endif | 274 | #endif |
290 | 275 | ||
291 | int gpmc_cs_calc_divider(int cs, unsigned int sync_clk) | 276 | int gpmc_calc_divider(unsigned int sync_clk) |
292 | { | 277 | { |
293 | int div; | 278 | int div; |
294 | u32 l; | 279 | u32 l; |
@@ -308,7 +293,7 @@ int gpmc_cs_set_timings(int cs, const struct gpmc_timings *t) | |||
308 | int div; | 293 | int div; |
309 | u32 l; | 294 | u32 l; |
310 | 295 | ||
311 | div = gpmc_cs_calc_divider(cs, t->sync_clk); | 296 | div = gpmc_calc_divider(t->sync_clk); |
312 | if (div < 0) | 297 | if (div < 0) |
313 | return div; | 298 | return div; |
314 | 299 | ||
@@ -509,44 +494,6 @@ void gpmc_cs_free(int cs) | |||
509 | EXPORT_SYMBOL(gpmc_cs_free); | 494 | EXPORT_SYMBOL(gpmc_cs_free); |
510 | 495 | ||
511 | /** | 496 | /** |
512 | * gpmc_read_status - read access request to get the different gpmc status | ||
513 | * @cmd: command type | ||
514 | * @return status | ||
515 | */ | ||
516 | int gpmc_read_status(int cmd) | ||
517 | { | ||
518 | int status = -EINVAL; | ||
519 | u32 regval = 0; | ||
520 | |||
521 | switch (cmd) { | ||
522 | case GPMC_GET_IRQ_STATUS: | ||
523 | status = gpmc_read_reg(GPMC_IRQSTATUS); | ||
524 | break; | ||
525 | |||
526 | case GPMC_PREFETCH_FIFO_CNT: | ||
527 | regval = gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
528 | status = GPMC_PREFETCH_STATUS_FIFO_CNT(regval); | ||
529 | break; | ||
530 | |||
531 | case GPMC_PREFETCH_COUNT: | ||
532 | regval = gpmc_read_reg(GPMC_PREFETCH_STATUS); | ||
533 | status = GPMC_PREFETCH_STATUS_COUNT(regval); | ||
534 | break; | ||
535 | |||
536 | case GPMC_STATUS_BUFFER: | ||
537 | regval = gpmc_read_reg(GPMC_STATUS); | ||
538 | /* 1 : buffer is available to write */ | ||
539 | status = regval & GPMC_STATUS_BUFF_EMPTY; | ||
540 | break; | ||
541 | |||
542 | default: | ||
543 | printk(KERN_ERR "gpmc_read_status: Not supported\n"); | ||
544 | } | ||
545 | return status; | ||
546 | } | ||
547 | EXPORT_SYMBOL(gpmc_read_status); | ||
548 | |||
549 | /** | ||
550 | * gpmc_cs_configure - write request to configure gpmc | 497 | * gpmc_cs_configure - write request to configure gpmc |
551 | * @cs: chip select number | 498 | * @cs: chip select number |
552 | * @cmd: command type | 499 | * @cmd: command type |
@@ -614,121 +561,10 @@ int gpmc_cs_configure(int cs, int cmd, int wval) | |||
614 | } | 561 | } |
615 | EXPORT_SYMBOL(gpmc_cs_configure); | 562 | EXPORT_SYMBOL(gpmc_cs_configure); |
616 | 563 | ||
617 | /** | ||
618 | * gpmc_nand_read - nand specific read access request | ||
619 | * @cs: chip select number | ||
620 | * @cmd: command type | ||
621 | */ | ||
622 | int gpmc_nand_read(int cs, int cmd) | ||
623 | { | ||
624 | int rval = -EINVAL; | ||
625 | |||
626 | switch (cmd) { | ||
627 | case GPMC_NAND_DATA: | ||
628 | rval = gpmc_cs_read_byte(cs, GPMC_CS_NAND_DATA); | ||
629 | break; | ||
630 | |||
631 | default: | ||
632 | printk(KERN_ERR "gpmc_read_nand_ctrl: Not supported\n"); | ||
633 | } | ||
634 | return rval; | ||
635 | } | ||
636 | EXPORT_SYMBOL(gpmc_nand_read); | ||
637 | |||
638 | /** | ||
639 | * gpmc_nand_write - nand specific write request | ||
640 | * @cs: chip select number | ||
641 | * @cmd: command type | ||
642 | * @wval: value to write | ||
643 | */ | ||
644 | int gpmc_nand_write(int cs, int cmd, int wval) | ||
645 | { | ||
646 | int err = 0; | ||
647 | |||
648 | switch (cmd) { | ||
649 | case GPMC_NAND_COMMAND: | ||
650 | gpmc_cs_write_byte(cs, GPMC_CS_NAND_COMMAND, wval); | ||
651 | break; | ||
652 | |||
653 | case GPMC_NAND_ADDRESS: | ||
654 | gpmc_cs_write_byte(cs, GPMC_CS_NAND_ADDRESS, wval); | ||
655 | break; | ||
656 | |||
657 | case GPMC_NAND_DATA: | ||
658 | gpmc_cs_write_byte(cs, GPMC_CS_NAND_DATA, wval); | ||
659 | |||
660 | default: | ||
661 | printk(KERN_ERR "gpmc_write_nand_ctrl: Not supported\n"); | ||
662 | err = -EINVAL; | ||
663 | } | ||
664 | return err; | ||
665 | } | ||
666 | EXPORT_SYMBOL(gpmc_nand_write); | ||
667 | |||
668 | |||
669 | |||
670 | /** | ||
671 | * gpmc_prefetch_enable - configures and starts prefetch transfer | ||
672 | * @cs: cs (chip select) number | ||
673 | * @fifo_th: fifo threshold to be used for read/ write | ||
674 | * @dma_mode: dma mode enable (1) or disable (0) | ||
675 | * @u32_count: number of bytes to be transferred | ||
676 | * @is_write: prefetch read(0) or write post(1) mode | ||
677 | */ | ||
678 | int gpmc_prefetch_enable(int cs, int fifo_th, int dma_mode, | ||
679 | unsigned int u32_count, int is_write) | ||
680 | { | ||
681 | |||
682 | if (fifo_th > PREFETCH_FIFOTHRESHOLD_MAX) { | ||
683 | pr_err("gpmc: fifo threshold is not supported\n"); | ||
684 | return -1; | ||
685 | } else if (!(gpmc_read_reg(GPMC_PREFETCH_CONTROL))) { | ||
686 | /* Set the amount of bytes to be prefetched */ | ||
687 | gpmc_write_reg(GPMC_PREFETCH_CONFIG2, u32_count); | ||
688 | |||
689 | /* Set dma/mpu mode, the prefetch read / post write and | ||
690 | * enable the engine. Set which cs is has requested for. | ||
691 | */ | ||
692 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, ((cs << CS_NUM_SHIFT) | | ||
693 | PREFETCH_FIFOTHRESHOLD(fifo_th) | | ||
694 | ENABLE_PREFETCH | | ||
695 | (dma_mode << DMA_MPU_MODE) | | ||
696 | (0x1 & is_write))); | ||
697 | |||
698 | /* Start the prefetch engine */ | ||
699 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x1); | ||
700 | } else { | ||
701 | return -EBUSY; | ||
702 | } | ||
703 | |||
704 | return 0; | ||
705 | } | ||
706 | EXPORT_SYMBOL(gpmc_prefetch_enable); | ||
707 | |||
708 | /** | ||
709 | * gpmc_prefetch_reset - disables and stops the prefetch engine | ||
710 | */ | ||
711 | int gpmc_prefetch_reset(int cs) | ||
712 | { | ||
713 | u32 config1; | ||
714 | |||
715 | /* check if the same module/cs is trying to reset */ | ||
716 | config1 = gpmc_read_reg(GPMC_PREFETCH_CONFIG1); | ||
717 | if (((config1 >> CS_NUM_SHIFT) & 0x7) != cs) | ||
718 | return -EINVAL; | ||
719 | |||
720 | /* Stop the PFPW engine */ | ||
721 | gpmc_write_reg(GPMC_PREFETCH_CONTROL, 0x0); | ||
722 | |||
723 | /* Reset/disable the PFPW engine */ | ||
724 | gpmc_write_reg(GPMC_PREFETCH_CONFIG1, 0x0); | ||
725 | |||
726 | return 0; | ||
727 | } | ||
728 | EXPORT_SYMBOL(gpmc_prefetch_reset); | ||
729 | |||
730 | void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) | 564 | void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) |
731 | { | 565 | { |
566 | int i; | ||
567 | |||
732 | reg->gpmc_status = gpmc_base + GPMC_STATUS; | 568 | reg->gpmc_status = gpmc_base + GPMC_STATUS; |
733 | reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + | 569 | reg->gpmc_nand_command = gpmc_base + GPMC_CS0_OFFSET + |
734 | GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; | 570 | GPMC_CS_NAND_COMMAND + GPMC_CS_SIZE * cs; |
@@ -744,7 +580,17 @@ void gpmc_update_nand_reg(struct gpmc_nand_regs *reg, int cs) | |||
744 | reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL; | 580 | reg->gpmc_ecc_control = gpmc_base + GPMC_ECC_CONTROL; |
745 | reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG; | 581 | reg->gpmc_ecc_size_config = gpmc_base + GPMC_ECC_SIZE_CONFIG; |
746 | reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT; | 582 | reg->gpmc_ecc1_result = gpmc_base + GPMC_ECC1_RESULT; |
747 | reg->gpmc_bch_result0 = gpmc_base + GPMC_ECC_BCH_RESULT_0; | 583 | |
584 | for (i = 0; i < GPMC_BCH_NUM_REMAINDER; i++) { | ||
585 | reg->gpmc_bch_result0[i] = gpmc_base + GPMC_ECC_BCH_RESULT_0 + | ||
586 | GPMC_BCH_SIZE * i; | ||
587 | reg->gpmc_bch_result1[i] = gpmc_base + GPMC_ECC_BCH_RESULT_1 + | ||
588 | GPMC_BCH_SIZE * i; | ||
589 | reg->gpmc_bch_result2[i] = gpmc_base + GPMC_ECC_BCH_RESULT_2 + | ||
590 | GPMC_BCH_SIZE * i; | ||
591 | reg->gpmc_bch_result3[i] = gpmc_base + GPMC_ECC_BCH_RESULT_3 + | ||
592 | GPMC_BCH_SIZE * i; | ||
593 | } | ||
748 | } | 594 | } |
749 | 595 | ||
750 | int gpmc_get_client_irq(unsigned irq_config) | 596 | int gpmc_get_client_irq(unsigned irq_config) |
@@ -1093,267 +939,3 @@ void omap3_gpmc_restore_context(void) | |||
1093 | } | 939 | } |
1094 | } | 940 | } |
1095 | #endif /* CONFIG_ARCH_OMAP3 */ | 941 | #endif /* CONFIG_ARCH_OMAP3 */ |
1096 | |||
1097 | /** | ||
1098 | * gpmc_enable_hwecc - enable hardware ecc functionality | ||
1099 | * @cs: chip select number | ||
1100 | * @mode: read/write mode | ||
1101 | * @dev_width: device bus width(1 for x16, 0 for x8) | ||
1102 | * @ecc_size: bytes for which ECC will be generated | ||
1103 | */ | ||
1104 | int gpmc_enable_hwecc(int cs, int mode, int dev_width, int ecc_size) | ||
1105 | { | ||
1106 | unsigned int val; | ||
1107 | |||
1108 | /* check if ecc module is in used */ | ||
1109 | if (gpmc_ecc_used != -EINVAL) | ||
1110 | return -EINVAL; | ||
1111 | |||
1112 | gpmc_ecc_used = cs; | ||
1113 | |||
1114 | /* clear ecc and enable bits */ | ||
1115 | gpmc_write_reg(GPMC_ECC_CONTROL, | ||
1116 | GPMC_ECC_CTRL_ECCCLEAR | | ||
1117 | GPMC_ECC_CTRL_ECCREG1); | ||
1118 | |||
1119 | /* program ecc and result sizes */ | ||
1120 | val = ((((ecc_size >> 1) - 1) << 22) | (0x0000000F)); | ||
1121 | gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, val); | ||
1122 | |||
1123 | switch (mode) { | ||
1124 | case GPMC_ECC_READ: | ||
1125 | case GPMC_ECC_WRITE: | ||
1126 | gpmc_write_reg(GPMC_ECC_CONTROL, | ||
1127 | GPMC_ECC_CTRL_ECCCLEAR | | ||
1128 | GPMC_ECC_CTRL_ECCREG1); | ||
1129 | break; | ||
1130 | case GPMC_ECC_READSYN: | ||
1131 | gpmc_write_reg(GPMC_ECC_CONTROL, | ||
1132 | GPMC_ECC_CTRL_ECCCLEAR | | ||
1133 | GPMC_ECC_CTRL_ECCDISABLE); | ||
1134 | break; | ||
1135 | default: | ||
1136 | printk(KERN_INFO "Error: Unrecognized Mode[%d]!\n", mode); | ||
1137 | break; | ||
1138 | } | ||
1139 | |||
1140 | /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ | ||
1141 | val = (dev_width << 7) | (cs << 1) | (0x1); | ||
1142 | gpmc_write_reg(GPMC_ECC_CONFIG, val); | ||
1143 | return 0; | ||
1144 | } | ||
1145 | EXPORT_SYMBOL_GPL(gpmc_enable_hwecc); | ||
1146 | |||
1147 | /** | ||
1148 | * gpmc_calculate_ecc - generate non-inverted ecc bytes | ||
1149 | * @cs: chip select number | ||
1150 | * @dat: data pointer over which ecc is computed | ||
1151 | * @ecc_code: ecc code buffer | ||
1152 | * | ||
1153 | * Using non-inverted ECC is considered ugly since writing a blank | ||
1154 | * page (padding) will clear the ECC bytes. This is not a problem as long | ||
1155 | * no one is trying to write data on the seemingly unused page. Reading | ||
1156 | * an erased page will produce an ECC mismatch between generated and read | ||
1157 | * ECC bytes that has to be dealt with separately. | ||
1158 | */ | ||
1159 | int gpmc_calculate_ecc(int cs, const u_char *dat, u_char *ecc_code) | ||
1160 | { | ||
1161 | unsigned int val = 0x0; | ||
1162 | |||
1163 | if (gpmc_ecc_used != cs) | ||
1164 | return -EINVAL; | ||
1165 | |||
1166 | /* read ecc result */ | ||
1167 | val = gpmc_read_reg(GPMC_ECC1_RESULT); | ||
1168 | *ecc_code++ = val; /* P128e, ..., P1e */ | ||
1169 | *ecc_code++ = val >> 16; /* P128o, ..., P1o */ | ||
1170 | /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ | ||
1171 | *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); | ||
1172 | |||
1173 | gpmc_ecc_used = -EINVAL; | ||
1174 | return 0; | ||
1175 | } | ||
1176 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc); | ||
1177 | |||
1178 | #ifdef CONFIG_ARCH_OMAP3 | ||
1179 | |||
1180 | /** | ||
1181 | * gpmc_init_hwecc_bch - initialize hardware BCH ecc functionality | ||
1182 | * @cs: chip select number | ||
1183 | * @nsectors: how many 512-byte sectors to process | ||
1184 | * @nerrors: how many errors to correct per sector (4 or 8) | ||
1185 | * | ||
1186 | * This function must be executed before any call to gpmc_enable_hwecc_bch. | ||
1187 | */ | ||
1188 | int gpmc_init_hwecc_bch(int cs, int nsectors, int nerrors) | ||
1189 | { | ||
1190 | /* check if ecc module is in use */ | ||
1191 | if (gpmc_ecc_used != -EINVAL) | ||
1192 | return -EINVAL; | ||
1193 | |||
1194 | /* support only OMAP3 class */ | ||
1195 | if (!cpu_is_omap34xx()) { | ||
1196 | printk(KERN_ERR "BCH ecc is not supported on this CPU\n"); | ||
1197 | return -EINVAL; | ||
1198 | } | ||
1199 | |||
1200 | /* | ||
1201 | * For now, assume 4-bit mode is only supported on OMAP3630 ES1.x, x>=1. | ||
1202 | * Other chips may be added if confirmed to work. | ||
1203 | */ | ||
1204 | if ((nerrors == 4) && | ||
1205 | (!cpu_is_omap3630() || (GET_OMAP_REVISION() == 0))) { | ||
1206 | printk(KERN_ERR "BCH 4-bit mode is not supported on this CPU\n"); | ||
1207 | return -EINVAL; | ||
1208 | } | ||
1209 | |||
1210 | /* sanity check */ | ||
1211 | if (nsectors > 8) { | ||
1212 | printk(KERN_ERR "BCH cannot process %d sectors (max is 8)\n", | ||
1213 | nsectors); | ||
1214 | return -EINVAL; | ||
1215 | } | ||
1216 | |||
1217 | return 0; | ||
1218 | } | ||
1219 | EXPORT_SYMBOL_GPL(gpmc_init_hwecc_bch); | ||
1220 | |||
1221 | /** | ||
1222 | * gpmc_enable_hwecc_bch - enable hardware BCH ecc functionality | ||
1223 | * @cs: chip select number | ||
1224 | * @mode: read/write mode | ||
1225 | * @dev_width: device bus width(1 for x16, 0 for x8) | ||
1226 | * @nsectors: how many 512-byte sectors to process | ||
1227 | * @nerrors: how many errors to correct per sector (4 or 8) | ||
1228 | */ | ||
1229 | int gpmc_enable_hwecc_bch(int cs, int mode, int dev_width, int nsectors, | ||
1230 | int nerrors) | ||
1231 | { | ||
1232 | unsigned int val; | ||
1233 | |||
1234 | /* check if ecc module is in use */ | ||
1235 | if (gpmc_ecc_used != -EINVAL) | ||
1236 | return -EINVAL; | ||
1237 | |||
1238 | gpmc_ecc_used = cs; | ||
1239 | |||
1240 | /* clear ecc and enable bits */ | ||
1241 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x1); | ||
1242 | |||
1243 | /* | ||
1244 | * When using BCH, sector size is hardcoded to 512 bytes. | ||
1245 | * Here we are using wrapping mode 6 both for reading and writing, with: | ||
1246 | * size0 = 0 (no additional protected byte in spare area) | ||
1247 | * size1 = 32 (skip 32 nibbles = 16 bytes per sector in spare area) | ||
1248 | */ | ||
1249 | gpmc_write_reg(GPMC_ECC_SIZE_CONFIG, (32 << 22) | (0 << 12)); | ||
1250 | |||
1251 | /* BCH configuration */ | ||
1252 | val = ((1 << 16) | /* enable BCH */ | ||
1253 | (((nerrors == 8) ? 1 : 0) << 12) | /* 8 or 4 bits */ | ||
1254 | (0x06 << 8) | /* wrap mode = 6 */ | ||
1255 | (dev_width << 7) | /* bus width */ | ||
1256 | (((nsectors-1) & 0x7) << 4) | /* number of sectors */ | ||
1257 | (cs << 1) | /* ECC CS */ | ||
1258 | (0x1)); /* enable ECC */ | ||
1259 | |||
1260 | gpmc_write_reg(GPMC_ECC_CONFIG, val); | ||
1261 | gpmc_write_reg(GPMC_ECC_CONTROL, 0x101); | ||
1262 | return 0; | ||
1263 | } | ||
1264 | EXPORT_SYMBOL_GPL(gpmc_enable_hwecc_bch); | ||
1265 | |||
1266 | /** | ||
1267 | * gpmc_calculate_ecc_bch4 - Generate 7 ecc bytes per sector of 512 data bytes | ||
1268 | * @cs: chip select number | ||
1269 | * @dat: The pointer to data on which ecc is computed | ||
1270 | * @ecc: The ecc output buffer | ||
1271 | */ | ||
1272 | int gpmc_calculate_ecc_bch4(int cs, const u_char *dat, u_char *ecc) | ||
1273 | { | ||
1274 | int i; | ||
1275 | unsigned long nsectors, reg, val1, val2; | ||
1276 | |||
1277 | if (gpmc_ecc_used != cs) | ||
1278 | return -EINVAL; | ||
1279 | |||
1280 | nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; | ||
1281 | |||
1282 | for (i = 0; i < nsectors; i++) { | ||
1283 | |||
1284 | reg = GPMC_ECC_BCH_RESULT_0 + 16*i; | ||
1285 | |||
1286 | /* Read hw-computed remainder */ | ||
1287 | val1 = gpmc_read_reg(reg + 0); | ||
1288 | val2 = gpmc_read_reg(reg + 4); | ||
1289 | |||
1290 | /* | ||
1291 | * Add constant polynomial to remainder, in order to get an ecc | ||
1292 | * sequence of 0xFFs for a buffer filled with 0xFFs; and | ||
1293 | * left-justify the resulting polynomial. | ||
1294 | */ | ||
1295 | *ecc++ = 0x28 ^ ((val2 >> 12) & 0xFF); | ||
1296 | *ecc++ = 0x13 ^ ((val2 >> 4) & 0xFF); | ||
1297 | *ecc++ = 0xcc ^ (((val2 & 0xF) << 4)|((val1 >> 28) & 0xF)); | ||
1298 | *ecc++ = 0x39 ^ ((val1 >> 20) & 0xFF); | ||
1299 | *ecc++ = 0x96 ^ ((val1 >> 12) & 0xFF); | ||
1300 | *ecc++ = 0xac ^ ((val1 >> 4) & 0xFF); | ||
1301 | *ecc++ = 0x7f ^ ((val1 & 0xF) << 4); | ||
1302 | } | ||
1303 | |||
1304 | gpmc_ecc_used = -EINVAL; | ||
1305 | return 0; | ||
1306 | } | ||
1307 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch4); | ||
1308 | |||
1309 | /** | ||
1310 | * gpmc_calculate_ecc_bch8 - Generate 13 ecc bytes per block of 512 data bytes | ||
1311 | * @cs: chip select number | ||
1312 | * @dat: The pointer to data on which ecc is computed | ||
1313 | * @ecc: The ecc output buffer | ||
1314 | */ | ||
1315 | int gpmc_calculate_ecc_bch8(int cs, const u_char *dat, u_char *ecc) | ||
1316 | { | ||
1317 | int i; | ||
1318 | unsigned long nsectors, reg, val1, val2, val3, val4; | ||
1319 | |||
1320 | if (gpmc_ecc_used != cs) | ||
1321 | return -EINVAL; | ||
1322 | |||
1323 | nsectors = ((gpmc_read_reg(GPMC_ECC_CONFIG) >> 4) & 0x7) + 1; | ||
1324 | |||
1325 | for (i = 0; i < nsectors; i++) { | ||
1326 | |||
1327 | reg = GPMC_ECC_BCH_RESULT_0 + 16*i; | ||
1328 | |||
1329 | /* Read hw-computed remainder */ | ||
1330 | val1 = gpmc_read_reg(reg + 0); | ||
1331 | val2 = gpmc_read_reg(reg + 4); | ||
1332 | val3 = gpmc_read_reg(reg + 8); | ||
1333 | val4 = gpmc_read_reg(reg + 12); | ||
1334 | |||
1335 | /* | ||
1336 | * Add constant polynomial to remainder, in order to get an ecc | ||
1337 | * sequence of 0xFFs for a buffer filled with 0xFFs. | ||
1338 | */ | ||
1339 | *ecc++ = 0xef ^ (val4 & 0xFF); | ||
1340 | *ecc++ = 0x51 ^ ((val3 >> 24) & 0xFF); | ||
1341 | *ecc++ = 0x2e ^ ((val3 >> 16) & 0xFF); | ||
1342 | *ecc++ = 0x09 ^ ((val3 >> 8) & 0xFF); | ||
1343 | *ecc++ = 0xed ^ (val3 & 0xFF); | ||
1344 | *ecc++ = 0x93 ^ ((val2 >> 24) & 0xFF); | ||
1345 | *ecc++ = 0x9a ^ ((val2 >> 16) & 0xFF); | ||
1346 | *ecc++ = 0xc2 ^ ((val2 >> 8) & 0xFF); | ||
1347 | *ecc++ = 0x97 ^ (val2 & 0xFF); | ||
1348 | *ecc++ = 0x79 ^ ((val1 >> 24) & 0xFF); | ||
1349 | *ecc++ = 0xe5 ^ ((val1 >> 16) & 0xFF); | ||
1350 | *ecc++ = 0x24 ^ ((val1 >> 8) & 0xFF); | ||
1351 | *ecc++ = 0xb5 ^ (val1 & 0xFF); | ||
1352 | } | ||
1353 | |||
1354 | gpmc_ecc_used = -EINVAL; | ||
1355 | return 0; | ||
1356 | } | ||
1357 | EXPORT_SYMBOL_GPL(gpmc_calculate_ecc_bch8); | ||
1358 | |||
1359 | #endif /* CONFIG_ARCH_OMAP3 */ | ||