diff options
author | Christian Riesch <christian.riesch@omicron.at> | 2014-03-06 07:18:27 -0500 |
---|---|---|
committer | Brian Norris <computersforpeace@gmail.com> | 2014-07-11 22:44:24 -0400 |
commit | dc7e9ecdd6a41edbd57b80e5ed837a06debd14ae (patch) | |
tree | 4c4a920ce433df7eb57a011482ef426b2af73bcb /drivers/mtd | |
parent | c14deddec1fbd8c9757c53a49dbfd2dc83265f21 (diff) |
mtd: cfi_cmdset_0002: Add support for reading OTP
The Micron M29EW has a 256 byte one time programmable (OTP) memory.
This patch adds support for reading this memory. This support will be
extended for locking and writing in subsequent patches.
Signed-off-by: Christian Riesch <christian.riesch@omicron.at>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0002.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index e21fde9d4d7e..54825083fd14 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
@@ -58,7 +58,15 @@ static void cfi_amdstd_sync (struct mtd_info *); | |||
58 | static int cfi_amdstd_suspend (struct mtd_info *); | 58 | static int cfi_amdstd_suspend (struct mtd_info *); |
59 | static void cfi_amdstd_resume (struct mtd_info *); | 59 | static void cfi_amdstd_resume (struct mtd_info *); |
60 | static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); | 60 | static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); |
61 | static int cfi_amdstd_get_fact_prot_info(struct mtd_info *, size_t, | ||
62 | size_t *, struct otp_info *); | ||
63 | static int cfi_amdstd_get_user_prot_info(struct mtd_info *, size_t, | ||
64 | size_t *, struct otp_info *); | ||
61 | static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | 65 | static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); |
66 | static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t, | ||
67 | size_t *, u_char *); | ||
68 | static int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t, | ||
69 | size_t *, u_char *); | ||
62 | 70 | ||
63 | static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, | 71 | static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, |
64 | size_t *retlen, const u_char *buf); | 72 | size_t *retlen, const u_char *buf); |
@@ -518,6 +526,10 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) | |||
518 | mtd->_sync = cfi_amdstd_sync; | 526 | mtd->_sync = cfi_amdstd_sync; |
519 | mtd->_suspend = cfi_amdstd_suspend; | 527 | mtd->_suspend = cfi_amdstd_suspend; |
520 | mtd->_resume = cfi_amdstd_resume; | 528 | mtd->_resume = cfi_amdstd_resume; |
529 | mtd->_read_user_prot_reg = cfi_amdstd_read_user_prot_reg; | ||
530 | mtd->_read_fact_prot_reg = cfi_amdstd_read_fact_prot_reg; | ||
531 | mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info; | ||
532 | mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info; | ||
521 | mtd->flags = MTD_CAP_NORFLASH; | 533 | mtd->flags = MTD_CAP_NORFLASH; |
522 | mtd->name = map->name; | 534 | mtd->name = map->name; |
523 | mtd->writesize = 1; | 535 | mtd->writesize = 1; |
@@ -1137,6 +1149,8 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_ | |||
1137 | return ret; | 1149 | return ret; |
1138 | } | 1150 | } |
1139 | 1151 | ||
1152 | typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, | ||
1153 | loff_t adr, size_t len, u_char *buf); | ||
1140 | 1154 | ||
1141 | static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) | 1155 | static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) |
1142 | { | 1156 | { |
@@ -1219,6 +1233,148 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, | |||
1219 | return ret; | 1233 | return ret; |
1220 | } | 1234 | } |
1221 | 1235 | ||
1236 | static int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, | ||
1237 | size_t *retlen, u_char *buf, | ||
1238 | otp_op_t action, int user_regs) | ||
1239 | { | ||
1240 | struct map_info *map = mtd->priv; | ||
1241 | struct cfi_private *cfi = map->fldrv_priv; | ||
1242 | int ofs_factor = cfi->interleave * cfi->device_type; | ||
1243 | unsigned long base; | ||
1244 | int chipnum; | ||
1245 | struct flchip *chip; | ||
1246 | uint8_t otp, lockreg; | ||
1247 | int ret; | ||
1248 | |||
1249 | size_t user_size, factory_size, otpsize; | ||
1250 | loff_t user_offset, factory_offset, otpoffset; | ||
1251 | int user_locked = 0, otplocked; | ||
1252 | |||
1253 | *retlen = 0; | ||
1254 | |||
1255 | for (chipnum = 0; chipnum < cfi->numchips; chipnum++) { | ||
1256 | chip = &cfi->chips[chipnum]; | ||
1257 | factory_size = 0; | ||
1258 | user_size = 0; | ||
1259 | |||
1260 | /* Micron M29EW family */ | ||
1261 | if (is_m29ew(cfi)) { | ||
1262 | base = chip->start; | ||
1263 | |||
1264 | /* check whether secsi area is factory locked | ||
1265 | or user lockable */ | ||
1266 | mutex_lock(&chip->mutex); | ||
1267 | ret = get_chip(map, chip, base, FL_CFI_QUERY); | ||
1268 | if (ret) { | ||
1269 | mutex_unlock(&chip->mutex); | ||
1270 | return ret; | ||
1271 | } | ||
1272 | cfi_qry_mode_on(base, map, cfi); | ||
1273 | otp = cfi_read_query(map, base + 0x3 * ofs_factor); | ||
1274 | cfi_qry_mode_off(base, map, cfi); | ||
1275 | put_chip(map, chip, base); | ||
1276 | mutex_unlock(&chip->mutex); | ||
1277 | |||
1278 | if (otp & 0x80) { | ||
1279 | /* factory locked */ | ||
1280 | factory_offset = 0; | ||
1281 | factory_size = 0x100; | ||
1282 | } else { | ||
1283 | /* customer lockable */ | ||
1284 | user_offset = 0; | ||
1285 | user_size = 0x100; | ||
1286 | |||
1287 | mutex_lock(&chip->mutex); | ||
1288 | ret = get_chip(map, chip, base, FL_LOCKING); | ||
1289 | |||
1290 | /* Enter lock register command */ | ||
1291 | cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, | ||
1292 | chip->start, map, cfi, | ||
1293 | cfi->device_type, NULL); | ||
1294 | cfi_send_gen_cmd(0x55, cfi->addr_unlock2, | ||
1295 | chip->start, map, cfi, | ||
1296 | cfi->device_type, NULL); | ||
1297 | cfi_send_gen_cmd(0x40, cfi->addr_unlock1, | ||
1298 | chip->start, map, cfi, | ||
1299 | cfi->device_type, NULL); | ||
1300 | /* read lock register */ | ||
1301 | lockreg = cfi_read_query(map, 0); | ||
1302 | /* exit protection commands */ | ||
1303 | map_write(map, CMD(0x90), chip->start); | ||
1304 | map_write(map, CMD(0x00), chip->start); | ||
1305 | put_chip(map, chip, chip->start); | ||
1306 | mutex_unlock(&chip->mutex); | ||
1307 | |||
1308 | user_locked = ((lockreg & 0x01) == 0x00); | ||
1309 | } | ||
1310 | } | ||
1311 | |||
1312 | otpsize = user_regs ? user_size : factory_size; | ||
1313 | if (!otpsize) | ||
1314 | continue; | ||
1315 | otpoffset = user_regs ? user_offset : factory_offset; | ||
1316 | otplocked = user_regs ? user_locked : 1; | ||
1317 | |||
1318 | if (!action) { | ||
1319 | /* return otpinfo */ | ||
1320 | struct otp_info *otpinfo; | ||
1321 | len -= sizeof(*otpinfo); | ||
1322 | if (len <= 0) | ||
1323 | return -ENOSPC; | ||
1324 | otpinfo = (struct otp_info *)buf; | ||
1325 | otpinfo->start = from; | ||
1326 | otpinfo->length = otpsize; | ||
1327 | otpinfo->locked = otplocked; | ||
1328 | buf += sizeof(*otpinfo); | ||
1329 | *retlen += sizeof(*otpinfo); | ||
1330 | from += otpsize; | ||
1331 | } else if ((from < otpsize) && (len > 0)) { | ||
1332 | size_t size; | ||
1333 | size = (len < otpsize - from) ? len : otpsize - from; | ||
1334 | ret = action(map, chip, otpoffset + from, size, buf); | ||
1335 | if (ret < 0) | ||
1336 | return ret; | ||
1337 | |||
1338 | buf += size; | ||
1339 | len -= size; | ||
1340 | *retlen += size; | ||
1341 | from = 0; | ||
1342 | } else { | ||
1343 | from -= otpsize; | ||
1344 | } | ||
1345 | } | ||
1346 | return 0; | ||
1347 | } | ||
1348 | |||
1349 | static int cfi_amdstd_get_fact_prot_info(struct mtd_info *mtd, size_t len, | ||
1350 | size_t *retlen, struct otp_info *buf) | ||
1351 | { | ||
1352 | return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf, | ||
1353 | NULL, 0); | ||
1354 | } | ||
1355 | |||
1356 | static int cfi_amdstd_get_user_prot_info(struct mtd_info *mtd, size_t len, | ||
1357 | size_t *retlen, struct otp_info *buf) | ||
1358 | { | ||
1359 | return cfi_amdstd_otp_walk(mtd, 0, len, retlen, (u_char *)buf, | ||
1360 | NULL, 1); | ||
1361 | } | ||
1362 | |||
1363 | static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, | ||
1364 | size_t len, size_t *retlen, | ||
1365 | u_char *buf) | ||
1366 | { | ||
1367 | return cfi_amdstd_otp_walk(mtd, from, len, retlen, | ||
1368 | buf, do_read_secsi_onechip, 0); | ||
1369 | } | ||
1370 | |||
1371 | static int cfi_amdstd_read_user_prot_reg(struct mtd_info *mtd, loff_t from, | ||
1372 | size_t len, size_t *retlen, | ||
1373 | u_char *buf) | ||
1374 | { | ||
1375 | return cfi_amdstd_otp_walk(mtd, from, len, retlen, | ||
1376 | buf, do_read_secsi_onechip, 1); | ||
1377 | } | ||
1222 | 1378 | ||
1223 | static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) | 1379 | static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) |
1224 | { | 1380 | { |