diff options
Diffstat (limited to 'drivers/mtd/nand/atmel_nand.c')
-rw-r--r-- | drivers/mtd/nand/atmel_nand.c | 141 |
1 files changed, 117 insertions, 24 deletions
diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index c516a9408087..ffcbcca2fd2d 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c | |||
@@ -101,6 +101,8 @@ struct atmel_nand_host { | |||
101 | u8 pmecc_corr_cap; | 101 | u8 pmecc_corr_cap; |
102 | u16 pmecc_sector_size; | 102 | u16 pmecc_sector_size; |
103 | u32 pmecc_lookup_table_offset; | 103 | u32 pmecc_lookup_table_offset; |
104 | u32 pmecc_lookup_table_offset_512; | ||
105 | u32 pmecc_lookup_table_offset_1024; | ||
104 | 106 | ||
105 | int pmecc_bytes_per_sector; | 107 | int pmecc_bytes_per_sector; |
106 | int pmecc_sector_number; | 108 | int pmecc_sector_number; |
@@ -908,6 +910,84 @@ static void atmel_pmecc_core_init(struct mtd_info *mtd) | |||
908 | pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); | 910 | pmecc_writel(host->ecc, CTRL, PMECC_CTRL_ENABLE); |
909 | } | 911 | } |
910 | 912 | ||
913 | /* | ||
914 | * Get ECC requirement in ONFI parameters, returns -1 if ONFI | ||
915 | * parameters is not supported. | ||
916 | * return 0 if success to get the ECC requirement. | ||
917 | */ | ||
918 | static int get_onfi_ecc_param(struct nand_chip *chip, | ||
919 | int *ecc_bits, int *sector_size) | ||
920 | { | ||
921 | *ecc_bits = *sector_size = 0; | ||
922 | |||
923 | if (chip->onfi_params.ecc_bits == 0xff) | ||
924 | /* TODO: the sector_size and ecc_bits need to be find in | ||
925 | * extended ecc parameter, currently we don't support it. | ||
926 | */ | ||
927 | return -1; | ||
928 | |||
929 | *ecc_bits = chip->onfi_params.ecc_bits; | ||
930 | |||
931 | /* The default sector size (ecc codeword size) is 512 */ | ||
932 | *sector_size = 512; | ||
933 | |||
934 | return 0; | ||
935 | } | ||
936 | |||
937 | /* | ||
938 | * Get ecc requirement from ONFI parameters ecc requirement. | ||
939 | * If pmecc-cap, pmecc-sector-size in DTS are not specified, this function | ||
940 | * will set them according to ONFI ecc requirement. Otherwise, use the | ||
941 | * value in DTS file. | ||
942 | * return 0 if success. otherwise return error code. | ||
943 | */ | ||
944 | static int pmecc_choose_ecc(struct atmel_nand_host *host, | ||
945 | int *cap, int *sector_size) | ||
946 | { | ||
947 | /* Get ECC requirement from ONFI parameters */ | ||
948 | *cap = *sector_size = 0; | ||
949 | if (host->nand_chip.onfi_version) { | ||
950 | if (!get_onfi_ecc_param(&host->nand_chip, cap, sector_size)) | ||
951 | dev_info(host->dev, "ONFI params, minimum required ECC: %d bits in %d bytes\n", | ||
952 | *cap, *sector_size); | ||
953 | else | ||
954 | dev_info(host->dev, "NAND chip ECC reqirement is in Extended ONFI parameter, we don't support yet.\n"); | ||
955 | } else { | ||
956 | dev_info(host->dev, "NAND chip is not ONFI compliant, assume ecc_bits is 2 in 512 bytes"); | ||
957 | } | ||
958 | if (*cap == 0 && *sector_size == 0) { | ||
959 | *cap = 2; | ||
960 | *sector_size = 512; | ||
961 | } | ||
962 | |||
963 | /* If dts file doesn't specify then use the one in ONFI parameters */ | ||
964 | if (host->pmecc_corr_cap == 0) { | ||
965 | /* use the most fitable ecc bits (the near bigger one ) */ | ||
966 | if (*cap <= 2) | ||
967 | host->pmecc_corr_cap = 2; | ||
968 | else if (*cap <= 4) | ||
969 | host->pmecc_corr_cap = 4; | ||
970 | else if (*cap < 8) | ||
971 | host->pmecc_corr_cap = 8; | ||
972 | else if (*cap < 12) | ||
973 | host->pmecc_corr_cap = 12; | ||
974 | else if (*cap < 24) | ||
975 | host->pmecc_corr_cap = 24; | ||
976 | else | ||
977 | return -EINVAL; | ||
978 | } | ||
979 | if (host->pmecc_sector_size == 0) { | ||
980 | /* use the most fitable sector size (the near smaller one ) */ | ||
981 | if (*sector_size >= 1024) | ||
982 | host->pmecc_sector_size = 1024; | ||
983 | else if (*sector_size >= 512) | ||
984 | host->pmecc_sector_size = 512; | ||
985 | else | ||
986 | return -EINVAL; | ||
987 | } | ||
988 | return 0; | ||
989 | } | ||
990 | |||
911 | static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, | 991 | static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, |
912 | struct atmel_nand_host *host) | 992 | struct atmel_nand_host *host) |
913 | { | 993 | { |
@@ -916,8 +996,22 @@ static int __init atmel_pmecc_nand_init_params(struct platform_device *pdev, | |||
916 | struct resource *regs, *regs_pmerr, *regs_rom; | 996 | struct resource *regs, *regs_pmerr, *regs_rom; |
917 | int cap, sector_size, err_no; | 997 | int cap, sector_size, err_no; |
918 | 998 | ||
999 | err_no = pmecc_choose_ecc(host, &cap, §or_size); | ||
1000 | if (err_no) { | ||
1001 | dev_err(host->dev, "The NAND flash's ECC requirement are not support!"); | ||
1002 | return err_no; | ||
1003 | } | ||
1004 | |||
1005 | if (cap != host->pmecc_corr_cap || | ||
1006 | sector_size != host->pmecc_sector_size) | ||
1007 | dev_info(host->dev, "WARNING: Be Caution! Using different PMECC parameters from Nand ONFI ECC reqirement.\n"); | ||
1008 | |||
919 | cap = host->pmecc_corr_cap; | 1009 | cap = host->pmecc_corr_cap; |
920 | sector_size = host->pmecc_sector_size; | 1010 | sector_size = host->pmecc_sector_size; |
1011 | host->pmecc_lookup_table_offset = (sector_size == 512) ? | ||
1012 | host->pmecc_lookup_table_offset_512 : | ||
1013 | host->pmecc_lookup_table_offset_1024; | ||
1014 | |||
921 | dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", | 1015 | dev_info(host->dev, "Initialize PMECC params, cap: %d, sector: %d\n", |
922 | cap, sector_size); | 1016 | cap, sector_size); |
923 | 1017 | ||
@@ -1215,7 +1309,7 @@ static void atmel_nand_hwctl(struct mtd_info *mtd, int mode) | |||
1215 | static int atmel_of_init_port(struct atmel_nand_host *host, | 1309 | static int atmel_of_init_port(struct atmel_nand_host *host, |
1216 | struct device_node *np) | 1310 | struct device_node *np) |
1217 | { | 1311 | { |
1218 | u32 val, table_offset; | 1312 | u32 val; |
1219 | u32 offset[2]; | 1313 | u32 offset[2]; |
1220 | int ecc_mode; | 1314 | int ecc_mode; |
1221 | struct atmel_nand_data *board = &host->board; | 1315 | struct atmel_nand_data *board = &host->board; |
@@ -1259,42 +1353,41 @@ static int atmel_of_init_port(struct atmel_nand_host *host, | |||
1259 | 1353 | ||
1260 | /* use PMECC, get correction capability, sector size and lookup | 1354 | /* use PMECC, get correction capability, sector size and lookup |
1261 | * table offset. | 1355 | * table offset. |
1356 | * If correction bits and sector size are not specified, then find | ||
1357 | * them from NAND ONFI parameters. | ||
1262 | */ | 1358 | */ |
1263 | if (of_property_read_u32(np, "atmel,pmecc-cap", &val) != 0) { | 1359 | if (of_property_read_u32(np, "atmel,pmecc-cap", &val) == 0) { |
1264 | dev_err(host->dev, "Cannot decide PMECC Capability\n"); | 1360 | if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && |
1265 | return -EINVAL; | 1361 | (val != 24)) { |
1266 | } else if ((val != 2) && (val != 4) && (val != 8) && (val != 12) && | 1362 | dev_err(host->dev, |
1267 | (val != 24)) { | 1363 | "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", |
1268 | dev_err(host->dev, | 1364 | val); |
1269 | "Unsupported PMECC correction capability: %d; should be 2, 4, 8, 12 or 24\n", | 1365 | return -EINVAL; |
1270 | val); | 1366 | } |
1271 | return -EINVAL; | 1367 | host->pmecc_corr_cap = (u8)val; |
1272 | } | 1368 | } |
1273 | host->pmecc_corr_cap = (u8)val; | ||
1274 | 1369 | ||
1275 | if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) != 0) { | 1370 | if (of_property_read_u32(np, "atmel,pmecc-sector-size", &val) == 0) { |
1276 | dev_err(host->dev, "Cannot decide PMECC Sector Size\n"); | 1371 | if ((val != 512) && (val != 1024)) { |
1277 | return -EINVAL; | 1372 | dev_err(host->dev, |
1278 | } else if ((val != 512) && (val != 1024)) { | 1373 | "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", |
1279 | dev_err(host->dev, | 1374 | val); |
1280 | "Unsupported PMECC sector size: %d; should be 512 or 1024 bytes\n", | 1375 | return -EINVAL; |
1281 | val); | 1376 | } |
1282 | return -EINVAL; | 1377 | host->pmecc_sector_size = (u16)val; |
1283 | } | 1378 | } |
1284 | host->pmecc_sector_size = (u16)val; | ||
1285 | 1379 | ||
1286 | if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", | 1380 | if (of_property_read_u32_array(np, "atmel,pmecc-lookup-table-offset", |
1287 | offset, 2) != 0) { | 1381 | offset, 2) != 0) { |
1288 | dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); | 1382 | dev_err(host->dev, "Cannot get PMECC lookup table offset\n"); |
1289 | return -EINVAL; | 1383 | return -EINVAL; |
1290 | } | 1384 | } |
1291 | table_offset = host->pmecc_sector_size == 512 ? offset[0] : offset[1]; | 1385 | if (!offset[0] && !offset[1]) { |
1292 | |||
1293 | if (!table_offset) { | ||
1294 | dev_err(host->dev, "Invalid PMECC lookup table offset\n"); | 1386 | dev_err(host->dev, "Invalid PMECC lookup table offset\n"); |
1295 | return -EINVAL; | 1387 | return -EINVAL; |
1296 | } | 1388 | } |
1297 | host->pmecc_lookup_table_offset = table_offset; | 1389 | host->pmecc_lookup_table_offset_512 = offset[0]; |
1390 | host->pmecc_lookup_table_offset_1024 = offset[1]; | ||
1298 | 1391 | ||
1299 | return 0; | 1392 | return 0; |
1300 | } | 1393 | } |