diff options
author | Christian Riesch <christian.riesch@omicron.at> | 2014-03-06 07:18:30 -0500 |
---|---|---|
committer | Brian Norris <computersforpeace@gmail.com> | 2014-07-11 22:44:25 -0400 |
commit | 4f5cb243823b3a83864a8f51266aff6bc08436b3 (patch) | |
tree | 9afa64b970f452f25faa61a3c2a384d7849af056 | |
parent | af7447505732ea729af6a99e76fc558b6e3fcbcd (diff) |
mtd: cfi_cmdset_0002: Add support for locking OTP memory
This patch adds support for the locking of the one time
programmable (OTP) memory of Micron M29EW devices.
Signed-off-by: Christian Riesch <christian.riesch@omicron.at>
Signed-off-by: Brian Norris <computersforpeace@gmail.com>
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0002.c | 89 |
1 files changed, 84 insertions, 5 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 6c575e65010b..bf313be6ee26 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c | |||
@@ -69,6 +69,7 @@ static int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t, | |||
69 | size_t *, u_char *); | 69 | size_t *, u_char *); |
70 | static int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t, | 70 | static int cfi_amdstd_write_user_prot_reg(struct mtd_info *, loff_t, size_t, |
71 | size_t *, u_char *); | 71 | size_t *, u_char *); |
72 | static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *, loff_t, size_t); | ||
72 | 73 | ||
73 | static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, | 74 | static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, |
74 | size_t *retlen, const u_char *buf); | 75 | size_t *retlen, const u_char *buf); |
@@ -533,6 +534,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) | |||
533 | mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info; | 534 | mtd->_get_fact_prot_info = cfi_amdstd_get_fact_prot_info; |
534 | mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info; | 535 | mtd->_get_user_prot_info = cfi_amdstd_get_user_prot_info; |
535 | mtd->_write_user_prot_reg = cfi_amdstd_write_user_prot_reg; | 536 | mtd->_write_user_prot_reg = cfi_amdstd_write_user_prot_reg; |
537 | mtd->_lock_user_prot_reg = cfi_amdstd_lock_user_prot_reg; | ||
536 | mtd->flags = MTD_CAP_NORFLASH; | 538 | mtd->flags = MTD_CAP_NORFLASH; |
537 | mtd->name = map->name; | 539 | mtd->name = map->name; |
538 | mtd->writesize = 1; | 540 | mtd->writesize = 1; |
@@ -1153,7 +1155,7 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_ | |||
1153 | } | 1155 | } |
1154 | 1156 | ||
1155 | typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, | 1157 | typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, |
1156 | loff_t adr, size_t len, u_char *buf); | 1158 | loff_t adr, size_t len, u_char *buf, size_t grouplen); |
1157 | 1159 | ||
1158 | static inline void otp_enter(struct map_info *map, struct flchip *chip, | 1160 | static inline void otp_enter(struct map_info *map, struct flchip *chip, |
1159 | loff_t adr, size_t len) | 1161 | loff_t adr, size_t len) |
@@ -1187,7 +1189,10 @@ static inline void otp_exit(struct map_info *map, struct flchip *chip, | |||
1187 | INVALIDATE_CACHED_RANGE(map, chip->start + adr, len); | 1189 | INVALIDATE_CACHED_RANGE(map, chip->start + adr, len); |
1188 | } | 1190 | } |
1189 | 1191 | ||
1190 | static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) | 1192 | static inline int do_read_secsi_onechip(struct map_info *map, |
1193 | struct flchip *chip, loff_t adr, | ||
1194 | size_t len, u_char *buf, | ||
1195 | size_t grouplen) | ||
1191 | { | 1196 | { |
1192 | DECLARE_WAITQUEUE(wait, current); | 1197 | DECLARE_WAITQUEUE(wait, current); |
1193 | unsigned long timeo = jiffies + HZ; | 1198 | unsigned long timeo = jiffies + HZ; |
@@ -1246,7 +1251,8 @@ static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, | |||
1246 | else | 1251 | else |
1247 | thislen = len; | 1252 | thislen = len; |
1248 | 1253 | ||
1249 | ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); | 1254 | ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs, |
1255 | thislen, buf, 0); | ||
1250 | if (ret) | 1256 | if (ret) |
1251 | break; | 1257 | break; |
1252 | 1258 | ||
@@ -1265,7 +1271,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, | |||
1265 | int mode); | 1271 | int mode); |
1266 | 1272 | ||
1267 | static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr, | 1273 | static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr, |
1268 | size_t len, u_char *buf) | 1274 | size_t len, u_char *buf, size_t grouplen) |
1269 | { | 1275 | { |
1270 | int ret; | 1276 | int ret; |
1271 | while (len) { | 1277 | while (len) { |
@@ -1294,6 +1300,70 @@ static int do_otp_write(struct map_info *map, struct flchip *chip, loff_t adr, | |||
1294 | return 0; | 1300 | return 0; |
1295 | } | 1301 | } |
1296 | 1302 | ||
1303 | static int do_otp_lock(struct map_info *map, struct flchip *chip, loff_t adr, | ||
1304 | size_t len, u_char *buf, size_t grouplen) | ||
1305 | { | ||
1306 | struct cfi_private *cfi = map->fldrv_priv; | ||
1307 | uint8_t lockreg; | ||
1308 | unsigned long timeo; | ||
1309 | int ret; | ||
1310 | |||
1311 | /* make sure area matches group boundaries */ | ||
1312 | if ((adr != 0) || (len != grouplen)) | ||
1313 | return -EINVAL; | ||
1314 | |||
1315 | mutex_lock(&chip->mutex); | ||
1316 | ret = get_chip(map, chip, chip->start, FL_LOCKING); | ||
1317 | if (ret) { | ||
1318 | mutex_unlock(&chip->mutex); | ||
1319 | return ret; | ||
1320 | } | ||
1321 | chip->state = FL_LOCKING; | ||
1322 | |||
1323 | /* Enter lock register command */ | ||
1324 | cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, | ||
1325 | cfi->device_type, NULL); | ||
1326 | cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, | ||
1327 | cfi->device_type, NULL); | ||
1328 | cfi_send_gen_cmd(0x40, cfi->addr_unlock1, chip->start, map, cfi, | ||
1329 | cfi->device_type, NULL); | ||
1330 | |||
1331 | /* read lock register */ | ||
1332 | lockreg = cfi_read_query(map, 0); | ||
1333 | |||
1334 | /* set bit 0 to protect extended memory block */ | ||
1335 | lockreg &= ~0x01; | ||
1336 | |||
1337 | /* set bit 0 to protect extended memory block */ | ||
1338 | /* write lock register */ | ||
1339 | map_write(map, CMD(0xA0), chip->start); | ||
1340 | map_write(map, CMD(lockreg), chip->start); | ||
1341 | |||
1342 | /* wait for chip to become ready */ | ||
1343 | timeo = jiffies + msecs_to_jiffies(2); | ||
1344 | for (;;) { | ||
1345 | if (chip_ready(map, adr)) | ||
1346 | break; | ||
1347 | |||
1348 | if (time_after(jiffies, timeo)) { | ||
1349 | pr_err("Waiting for chip to be ready timed out.\n"); | ||
1350 | ret = -EIO; | ||
1351 | break; | ||
1352 | } | ||
1353 | UDELAY(map, chip, 0, 1); | ||
1354 | } | ||
1355 | |||
1356 | /* exit protection commands */ | ||
1357 | map_write(map, CMD(0x90), chip->start); | ||
1358 | map_write(map, CMD(0x00), chip->start); | ||
1359 | |||
1360 | chip->state = FL_READY; | ||
1361 | put_chip(map, chip, chip->start); | ||
1362 | mutex_unlock(&chip->mutex); | ||
1363 | |||
1364 | return ret; | ||
1365 | } | ||
1366 | |||
1297 | static int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, | 1367 | static int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, |
1298 | size_t *retlen, u_char *buf, | 1368 | size_t *retlen, u_char *buf, |
1299 | otp_op_t action, int user_regs) | 1369 | otp_op_t action, int user_regs) |
@@ -1392,7 +1462,8 @@ static int cfi_amdstd_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, | |||
1392 | } else if ((from < otpsize) && (len > 0)) { | 1462 | } else if ((from < otpsize) && (len > 0)) { |
1393 | size_t size; | 1463 | size_t size; |
1394 | size = (len < otpsize - from) ? len : otpsize - from; | 1464 | size = (len < otpsize - from) ? len : otpsize - from; |
1395 | ret = action(map, chip, otpoffset + from, size, buf); | 1465 | ret = action(map, chip, otpoffset + from, size, buf, |
1466 | otpsize); | ||
1396 | if (ret < 0) | 1467 | if (ret < 0) |
1397 | return ret; | 1468 | return ret; |
1398 | 1469 | ||
@@ -1445,6 +1516,14 @@ static int cfi_amdstd_write_user_prot_reg(struct mtd_info *mtd, loff_t from, | |||
1445 | do_otp_write, 1); | 1516 | do_otp_write, 1); |
1446 | } | 1517 | } |
1447 | 1518 | ||
1519 | static int cfi_amdstd_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, | ||
1520 | size_t len) | ||
1521 | { | ||
1522 | size_t retlen; | ||
1523 | return cfi_amdstd_otp_walk(mtd, from, len, &retlen, NULL, | ||
1524 | do_otp_lock, 1); | ||
1525 | } | ||
1526 | |||
1448 | static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, | 1527 | static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, |
1449 | unsigned long adr, map_word datum, | 1528 | unsigned long adr, map_word datum, |
1450 | int mode) | 1529 | int mode) |