Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

mtd: atmel_nand: make PMECC lookup table and offset property optional

If there is no PMECC lookup table stored in ROM, or lookup table offset is
not specified, PMECC driver should build it in DDR by itself.

That make the PMECC driver work for some board which doesn't have PMECC
lookup table in ROM.

The PMECC use the BCH algorithm, so based on the build_gf_tables()
function in lib/bch.c, we can build the Galois Field lookup table.

For more information can refer to section 5.4 of PMECC controller
application note:
http://www.atmel.com/images/doc11127.pdf

Signed-off-by: Josh Wu <josh.wu@atmel.com>
Cc: devicetree@vger.kernel.org
Signed-off-by: Brian Norris <computersforpeace@gmail.com>

authored by

Josh Wu and committed by
Brian Norris
abb1cd00 33a87a15

+85 -6
+4 -2
Documentation/devicetree/bindings/mtd/atmel-nand.txt
··· 5 5 - reg : should specify localbus address and size used for the chip, 6 6 and hardware ECC controller if available. 7 7 If the hardware ECC is PMECC, it should contain address and size for 8 - PMECC, PMECC Error Location controller and ROM which has lookup tables. 8 + PMECC and PMECC Error Location controller. 9 + The PMECC lookup table address and size in ROM is optional. If not 10 + specified, driver will build it in runtime. 9 11 - atmel,nand-addr-offset : offset for the address latch. 10 12 - atmel,nand-cmd-offset : offset for the command latch. 11 13 - #address-cells, #size-cells : Must be present if the device has sub-nodes ··· 29 27 are: 512, 1024. 30 28 - atmel,pmecc-lookup-table-offset : includes two offsets of lookup table in ROM 31 29 for different sector size. First one is for sector size 512, the next is for 32 - sector size 1024. 30 + sector size 1024. If not specified, driver will build the table in runtime. 33 31 - nand-bus-width : 8 or 16 bus width if not present 8 34 32 - nand-on-flash-bbt: boolean to enable on flash bbt option if not present false 35 33 - Nand Flash Controller(NFC) is a slave driver under Atmel nand flash
+77 -4
drivers/mtd/nand/atmel_nand.c
··· 127 127 bool has_pmecc; 128 128 u8 pmecc_corr_cap; 129 129 u16 pmecc_sector_size; 130 + bool has_no_lookup_table; 130 131 u32 pmecc_lookup_table_offset; 131 132 u32 pmecc_lookup_table_offset_512; 132 133 u32 pmecc_lookup_table_offset_1024; ··· 1113 1112 return 0; 1114 1113 } 1115 1114 1115 + static inline int deg(unsigned int poly) 1116 + { 1117 + /* polynomial degree is the most-significant bit index */ 1118 + return fls(poly) - 1; 1119 + } 1120 + 1121 + static int build_gf_tables(int mm, unsigned int poly, 1122 + int16_t *index_of, int16_t *alpha_to) 1123 + { 1124 + unsigned int i, x = 1; 1125 + const unsigned int k = 1 << deg(poly); 1126 + unsigned int nn = (1 << mm) - 1; 1127 + 1128 + /* primitive polynomial must be of degree m */ 1129 + if (k != (1u << mm)) 1130 + return -EINVAL; 1131 + 1132 + for (i = 0; i < nn; i++) { 1133 + alpha_to[i] = x; 1134 + index_of[x] = i; 1135 + if (i && (x == 1)) 1136 + /* polynomial is not primitive (a^i=1 with 0<i<2^m-1) */ 1137 + return -EINVAL; 1138 + x <<= 1; 1139 + if (x & k) 1140 + x ^= poly; 1141 + } 1142 + alpha_to[nn] = 1; 1143 + index_of[0] = 0; 1144 + 1145 + return 0; 1146 + } 1147 + 1148 + static uint16_t *create_lookup_table(struct device *dev, int sector_size) 1149 + { 1150 + int degree = (sector_size == 512) ? 1151 + PMECC_GF_DIMENSION_13 : 1152 + PMECC_GF_DIMENSION_14; 1153 + unsigned int poly = (sector_size == 512) ? 1154 + PMECC_GF_13_PRIMITIVE_POLY : 1155 + PMECC_GF_14_PRIMITIVE_POLY; 1156 + int table_size = (sector_size == 512) ? 1157 + PMECC_LOOKUP_TABLE_SIZE_512 : 1158 + PMECC_LOOKUP_TABLE_SIZE_1024; 1159 + 1160 + int16_t *addr = devm_kzalloc(dev, 2 * table_size * sizeof(uint16_t), 1161 + GFP_KERNEL); 1162 + if (addr && build_gf_tables(degree, poly, addr, addr + table_size)) 1163 + return NULL; 1164 + 1165 + return addr; 1166 + } 1167 + 1116 1168 static int atmel_pmecc_nand_init_params(struct platform_device *pdev, 1117 1169 struct atmel_nand_host *host) 1118 1170 { 1119 1171 struct mtd_info *mtd = &host->mtd; 1120 1172 struct nand_chip *nand_chip = &host->nand_chip; 1121 1173 struct resource *regs, *regs_pmerr, *regs_rom; 1174 + uint16_t *galois_table; 1122 1175 int cap, sector_size, err_no; 1123 1176 1124 1177 err_no = pmecc_choose_ecc(host, &cap, &sector_size); ··· 1218 1163 regs_rom = platform_get_resource(pdev, IORESOURCE_MEM, 3); 1219 1164 host->pmecc_rom_base = devm_ioremap_resource(&pdev->dev, regs_rom); 1220 1165 if (IS_ERR(host->pmecc_rom_base)) { 1221 - err_no = PTR_ERR(host->pmecc_rom_base); 1222 - goto err; 1166 + if (!host->has_no_lookup_table) 1167 + /* Don't display the information again */ 1168 + dev_err(host->dev, "Can not get I/O resource for ROM, will build a lookup table in runtime!\n"); 1169 + 1170 + host->has_no_lookup_table = true; 1171 + } 1172 + 1173 + if (host->has_no_lookup_table) { 1174 + /* Build the look-up table in runtime */ 1175 + galois_table = create_lookup_table(host->dev, sector_size); 1176 + if (!galois_table) { 1177 + dev_err(host->dev, "Failed to build a lookup table in runtime!\n"); 1178 + err_no = -EINVAL; 1179 + goto err; 1180 + } 1181 + 1182 + host->pmecc_rom_base = (void __iomem *)galois_table; 1183 + host->pmecc_lookup_table_offset = 0; 1223 1184 } 1224 1185 1225 1186 nand_chip->ecc.size = sector_size; ··· 1572 1501 1573 1502 if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", 1574 1503 offset, 2) != 0) { 1575 - dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); 1576 - return -EINVAL; 1504 + dev_err(host->dev, "Cannot get PMECC lookup table offset, will build a lookup table in runtime.\n"); 1505 + host->has_no_lookup_table = true; 1506 + /* Will build a lookup table and initialize the offset later */ 1507 + return 0; 1577 1508 } 1578 1509 if (!offset[0] && !offset[1]) { 1579 1510 dev_err(host->dev, "Invalid PMECC lookup table offset\n");
+4
drivers/mtd/nand/atmel_nand_ecc.h
··· 142 142 #define PMECC_GF_DIMENSION_13 13 143 143 #define PMECC_GF_DIMENSION_14 14 144 144 145 + /* Primitive Polynomial used by PMECC */ 146 + #define PMECC_GF_13_PRIMITIVE_POLY 0x201b 147 + #define PMECC_GF_14_PRIMITIVE_POLY 0x4443 148 + 145 149 #define PMECC_LOOKUP_TABLE_SIZE_512 0x2000 146 150 #define PMECC_LOOKUP_TABLE_SIZE_1024 0x4000 147 151