aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mtd
diff options
context:
space:
mode:
authorChristian Riesch <christian.riesch@omicron.at>2014-03-06 07:18:27 -0500
committerBrian Norris <computersforpeace@gmail.com>2014-07-11 22:44:24 -0400
commitdc7e9ecdd6a41edbd57b80e5ed837a06debd14ae (patch)
tree4c4a920ce433df7eb57a011482ef426b2af73bcb /drivers/mtd
parentc14deddec1fbd8c9757c53a49dbfd2dc83265f21 (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.c156
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 *);
58static int cfi_amdstd_suspend (struct mtd_info *); 58static int cfi_amdstd_suspend (struct mtd_info *);
59static void cfi_amdstd_resume (struct mtd_info *); 59static void cfi_amdstd_resume (struct mtd_info *);
60static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); 60static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *);
61static int cfi_amdstd_get_fact_prot_info(struct mtd_info *, size_t,
62 size_t *, struct otp_info *);
63static int cfi_amdstd_get_user_prot_info(struct mtd_info *, size_t,
64 size_t *, struct otp_info *);
61static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); 65static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
66static int cfi_amdstd_read_fact_prot_reg(struct mtd_info *, loff_t, size_t,
67 size_t *, u_char *);
68static int cfi_amdstd_read_user_prot_reg(struct mtd_info *, loff_t, size_t,
69 size_t *, u_char *);
62 70
63static int cfi_amdstd_panic_write(struct mtd_info *mtd, loff_t to, size_t len, 71static 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
1152typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip,
1153 loff_t adr, size_t len, u_char *buf);
1140 1154
1141static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) 1155static 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
1236static 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
1349static 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
1356static 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
1363static 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
1371static 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
1223static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum) 1379static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
1224{ 1380{