diff options
author | Nicolas Pitre <nico@cam.org> | 2005-02-08 12:11:19 -0500 |
---|---|---|
committer | Thomas Gleixner <tglx@mtd.linutronix.de> | 2005-05-23 06:25:23 -0400 |
commit | f77814dd5728edaf1239d19755d2aa0d8c33d861 (patch) | |
tree | 5cf7f73aa367bf152e5fbd16b5173e18bb787dd5 | |
parent | 67d9e95c393d23c229836e28b262dc73d71da784 (diff) |
[MTD] Support for protection register support on Intel FLASH chips
This enables support for reading, writing and locking so called
"Protection Registers" present on some flash chips.
A subset of them are pre-programmed at the factory with a
unique set of values. The rest is user-programmable.
Signed-off-by: Nicolas Pitre <nico@cam.org>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
-rw-r--r-- | drivers/mtd/chips/Kconfig | 27 | ||||
-rw-r--r-- | drivers/mtd/chips/cfi_cmdset_0001.c | 401 | ||||
-rw-r--r-- | drivers/mtd/mtdpart.c | 28 | ||||
-rw-r--r-- | include/linux/mtd/cfi.h | 4 | ||||
-rw-r--r-- | include/linux/mtd/flashchip.h | 3 | ||||
-rw-r--r-- | include/linux/mtd/map.h | 15 | ||||
-rw-r--r-- | include/linux/mtd/mtd.h | 10 | ||||
-rw-r--r-- | include/mtd/mtd-abi.h | 8 |
8 files changed, 369 insertions, 127 deletions
diff --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index d682dbc8157e..f4eda1e40d51 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig | |||
@@ -1,5 +1,5 @@ | |||
1 | # drivers/mtd/chips/Kconfig | 1 | # drivers/mtd/chips/Kconfig |
2 | # $Id: Kconfig,v 1.13 2004/12/01 15:49:10 nico Exp $ | 2 | # $Id: Kconfig,v 1.14 2005/02/08 17:11:15 nico Exp $ |
3 | 3 | ||
4 | menu "RAM/ROM/Flash chip drivers" | 4 | menu "RAM/ROM/Flash chip drivers" |
5 | depends on MTD!=n | 5 | depends on MTD!=n |
@@ -155,6 +155,31 @@ config MTD_CFI_I8 | |||
155 | If your flash chips are interleaved in eights - i.e. you have eight | 155 | If your flash chips are interleaved in eights - i.e. you have eight |
156 | flash chips addressed by each bus cycle, then say 'Y'. | 156 | flash chips addressed by each bus cycle, then say 'Y'. |
157 | 157 | ||
158 | config MTD_OTP | ||
159 | bool "Protection Registers aka one-time programmable (OTP) bits" | ||
160 | depends on MTD_CFI_ADV_OPTIONS | ||
161 | default n | ||
162 | help | ||
163 | This enables support for reading, writing and locking so called | ||
164 | "Protection Registers" present on some flash chips. | ||
165 | A subset of them are pre-programmed at the factory with a | ||
166 | unique set of values. The rest is user-programmable. | ||
167 | |||
168 | The user-programmable Protection Registers contain one-time | ||
169 | programmable (OTP) bits; when programmed, register bits cannot be | ||
170 | erased. Each Protection Register can be accessed multiple times to | ||
171 | program individual bits, as long as the register remains unlocked. | ||
172 | |||
173 | Each Protection Register has an associated Lock Register bit. When a | ||
174 | Lock Register bit is programmed, the associated Protection Register | ||
175 | can only be read; it can no longer be programmed. Additionally, | ||
176 | because the Lock Register bits themselves are OTP, when programmed, | ||
177 | Lock Register bits cannot be erased. Therefore, when a Protection | ||
178 | Register is locked, it cannot be unlocked. | ||
179 | |||
180 | This feature should therefore be used with extreme care. Any mistake | ||
181 | in the programming of OTP bits will waste them. | ||
182 | |||
158 | config MTD_CFI_INTELEXT | 183 | config MTD_CFI_INTELEXT |
159 | tristate "Support for Intel/Sharp flash chips" | 184 | tristate "Support for Intel/Sharp flash chips" |
160 | depends on MTD_GEN_PROBE | 185 | depends on MTD_GEN_PROBE |
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c630d7532f7a..b3f5acf0760c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c | |||
@@ -4,7 +4,7 @@ | |||
4 | * | 4 | * |
5 | * (C) 2000 Red Hat. GPL'd | 5 | * (C) 2000 Red Hat. GPL'd |
6 | * | 6 | * |
7 | * $Id: cfi_cmdset_0001.c,v 1.165 2005/02/05 02:06:15 nico Exp $ | 7 | * $Id: cfi_cmdset_0001.c,v 1.167 2005/02/08 17:11:15 nico Exp $ |
8 | * | 8 | * |
9 | * | 9 | * |
10 | * 10/10/2000 Nicolas Pitre <nico@cam.org> | 10 | * 10/10/2000 Nicolas Pitre <nico@cam.org> |
@@ -48,14 +48,20 @@ | |||
48 | #define M50LPW080 0x002F | 48 | #define M50LPW080 0x002F |
49 | 49 | ||
50 | static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | 50 | static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); |
51 | //static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | ||
52 | //static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | ||
53 | static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); | 51 | static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); |
54 | static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); | 52 | static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); |
55 | static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); | 53 | static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); |
56 | static void cfi_intelext_sync (struct mtd_info *); | 54 | static void cfi_intelext_sync (struct mtd_info *); |
57 | static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); | 55 | static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len); |
58 | static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); | 56 | static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); |
57 | static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | ||
58 | static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | ||
59 | static int cfi_intelext_write_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); | ||
60 | static int cfi_intelext_lock_user_prot_reg (struct mtd_info *, loff_t, size_t); | ||
61 | static int cfi_intelext_get_fact_prot_info (struct mtd_info *, | ||
62 | struct otp_info *, size_t); | ||
63 | static int cfi_intelext_get_user_prot_info (struct mtd_info *, | ||
64 | struct otp_info *, size_t); | ||
59 | static int cfi_intelext_suspend (struct mtd_info *); | 65 | static int cfi_intelext_suspend (struct mtd_info *); |
60 | static void cfi_intelext_resume (struct mtd_info *); | 66 | static void cfi_intelext_resume (struct mtd_info *); |
61 | 67 | ||
@@ -423,9 +429,13 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) | |||
423 | mtd->eraseregions[i].numblocks); | 429 | mtd->eraseregions[i].numblocks); |
424 | } | 430 | } |
425 | 431 | ||
426 | #if 0 | 432 | #ifdef CONFIG_MTD_OTP |
427 | mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; | ||
428 | mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; | 433 | mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; |
434 | mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; | ||
435 | mtd->write_user_prot_reg = cfi_intelext_write_user_prot_reg; | ||
436 | mtd->lock_user_prot_reg = cfi_intelext_lock_user_prot_reg; | ||
437 | mtd->get_fact_prot_info = cfi_intelext_get_fact_prot_info; | ||
438 | mtd->get_user_prot_info = cfi_intelext_get_user_prot_info; | ||
429 | #endif | 439 | #endif |
430 | 440 | ||
431 | /* This function has the potential to distort the reality | 441 | /* This function has the potential to distort the reality |
@@ -565,7 +575,7 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr | |||
565 | resettime: | 575 | resettime: |
566 | timeo = jiffies + HZ; | 576 | timeo = jiffies + HZ; |
567 | retry: | 577 | retry: |
568 | if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) { | 578 | if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING || mode == FL_OTP_WRITE)) { |
569 | /* | 579 | /* |
570 | * OK. We have possibility for contension on the write/erase | 580 | * OK. We have possibility for contension on the write/erase |
571 | * operations which are global to the real chip and not per | 581 | * operations which are global to the real chip and not per |
@@ -1178,111 +1188,11 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz | |||
1178 | return ret; | 1188 | return ret; |
1179 | } | 1189 | } |
1180 | 1190 | ||
1181 | #if 0 | ||
1182 | static int __xipram cfi_intelext_read_prot_reg (struct mtd_info *mtd, | ||
1183 | loff_t from, size_t len, | ||
1184 | size_t *retlen, | ||
1185 | u_char *buf, | ||
1186 | int base_offst, int reg_sz) | ||
1187 | { | ||
1188 | struct map_info *map = mtd->priv; | ||
1189 | struct cfi_private *cfi = map->fldrv_priv; | ||
1190 | struct cfi_pri_intelext *extp = cfi->cmdset_priv; | ||
1191 | struct flchip *chip; | ||
1192 | int ofs_factor = cfi->interleave * cfi->device_type; | ||
1193 | int count = len; | ||
1194 | int chip_num, offst; | ||
1195 | int ret; | ||
1196 | |||
1197 | chip_num = ((unsigned int)from/reg_sz); | ||
1198 | offst = from - (reg_sz*chip_num)+base_offst; | ||
1199 | |||
1200 | while (count) { | ||
1201 | /* Calculate which chip & protection register offset we need */ | ||
1202 | |||
1203 | if (chip_num >= cfi->numchips) | ||
1204 | goto out; | ||
1205 | |||
1206 | chip = &cfi->chips[chip_num]; | ||
1207 | |||
1208 | spin_lock(chip->mutex); | ||
1209 | ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); | ||
1210 | if (ret) { | ||
1211 | spin_unlock(chip->mutex); | ||
1212 | return (len-count)?:ret; | ||
1213 | } | ||
1214 | |||
1215 | xip_disable(map, chip, chip->start); | ||
1216 | |||
1217 | if (chip->state != FL_JEDEC_QUERY) { | ||
1218 | map_write(map, CMD(0x90), chip->start); | ||
1219 | chip->state = FL_JEDEC_QUERY; | ||
1220 | } | ||
1221 | |||
1222 | while (count && ((offst-base_offst) < reg_sz)) { | ||
1223 | *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); | ||
1224 | buf++; | ||
1225 | offst++; | ||
1226 | count--; | ||
1227 | } | ||
1228 | |||
1229 | xip_enable(map, chip, chip->start); | ||
1230 | put_chip(map, chip, chip->start); | ||
1231 | spin_unlock(chip->mutex); | ||
1232 | |||
1233 | /* Move on to the next chip */ | ||
1234 | chip_num++; | ||
1235 | offst = base_offst; | ||
1236 | } | ||
1237 | |||
1238 | out: | ||
1239 | return len-count; | ||
1240 | } | ||
1241 | |||
1242 | static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) | ||
1243 | { | ||
1244 | struct map_info *map = mtd->priv; | ||
1245 | struct cfi_private *cfi = map->fldrv_priv; | ||
1246 | struct cfi_pri_intelext *extp=cfi->cmdset_priv; | ||
1247 | int base_offst,reg_sz; | ||
1248 | |||
1249 | /* Check that we actually have some protection registers */ | ||
1250 | if(!extp || !(extp->FeatureSupport&64)){ | ||
1251 | printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); | ||
1252 | return 0; | ||
1253 | } | ||
1254 | |||
1255 | base_offst=(1<<extp->FactProtRegSize); | ||
1256 | reg_sz=(1<<extp->UserProtRegSize); | ||
1257 | |||
1258 | return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); | ||
1259 | } | ||
1260 | |||
1261 | static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) | ||
1262 | { | ||
1263 | struct map_info *map = mtd->priv; | ||
1264 | struct cfi_private *cfi = map->fldrv_priv; | ||
1265 | struct cfi_pri_intelext *extp=cfi->cmdset_priv; | ||
1266 | int base_offst,reg_sz; | ||
1267 | |||
1268 | /* Check that we actually have some protection registers */ | ||
1269 | if(!extp || !(extp->FeatureSupport&64)){ | ||
1270 | printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); | ||
1271 | return 0; | ||
1272 | } | ||
1273 | |||
1274 | base_offst=0; | ||
1275 | reg_sz=(1<<extp->FactProtRegSize); | ||
1276 | |||
1277 | return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); | ||
1278 | } | ||
1279 | #endif | ||
1280 | |||
1281 | static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, | 1191 | static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, |
1282 | unsigned long adr, map_word datum) | 1192 | unsigned long adr, map_word datum, int mode) |
1283 | { | 1193 | { |
1284 | struct cfi_private *cfi = map->fldrv_priv; | 1194 | struct cfi_private *cfi = map->fldrv_priv; |
1285 | map_word status, status_OK; | 1195 | map_word status, status_OK, write_cmd; |
1286 | unsigned long timeo; | 1196 | unsigned long timeo; |
1287 | int z, ret=0; | 1197 | int z, ret=0; |
1288 | 1198 | ||
@@ -1290,9 +1200,14 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, | |||
1290 | 1200 | ||
1291 | /* Let's determine this according to the interleave only once */ | 1201 | /* Let's determine this according to the interleave only once */ |
1292 | status_OK = CMD(0x80); | 1202 | status_OK = CMD(0x80); |
1203 | switch (mode) { | ||
1204 | case FL_WRITING: write_cmd = CMD(0x40); break; | ||
1205 | case FL_OTP_WRITE: write_cmd = CMD(0xc0); break; | ||
1206 | default: return -EINVAL; | ||
1207 | } | ||
1293 | 1208 | ||
1294 | spin_lock(chip->mutex); | 1209 | spin_lock(chip->mutex); |
1295 | ret = get_chip(map, chip, adr, FL_WRITING); | 1210 | ret = get_chip(map, chip, adr, mode); |
1296 | if (ret) { | 1211 | if (ret) { |
1297 | spin_unlock(chip->mutex); | 1212 | spin_unlock(chip->mutex); |
1298 | return ret; | 1213 | return ret; |
@@ -1301,9 +1216,9 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, | |||
1301 | XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); | 1216 | XIP_INVAL_CACHED_RANGE(map, adr, map_bankwidth(map)); |
1302 | ENABLE_VPP(map); | 1217 | ENABLE_VPP(map); |
1303 | xip_disable(map, chip, adr); | 1218 | xip_disable(map, chip, adr); |
1304 | map_write(map, CMD(0x40), adr); | 1219 | map_write(map, write_cmd, adr); |
1305 | map_write(map, datum, adr); | 1220 | map_write(map, datum, adr); |
1306 | chip->state = FL_WRITING; | 1221 | chip->state = mode; |
1307 | 1222 | ||
1308 | spin_unlock(chip->mutex); | 1223 | spin_unlock(chip->mutex); |
1309 | INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); | 1224 | INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map)); |
@@ -1313,7 +1228,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, | |||
1313 | timeo = jiffies + (HZ/2); | 1228 | timeo = jiffies + (HZ/2); |
1314 | z = 0; | 1229 | z = 0; |
1315 | for (;;) { | 1230 | for (;;) { |
1316 | if (chip->state != FL_WRITING) { | 1231 | if (chip->state != mode) { |
1317 | /* Someone's suspended the write. Sleep */ | 1232 | /* Someone's suspended the write. Sleep */ |
1318 | DECLARE_WAITQUEUE(wait, current); | 1233 | DECLARE_WAITQUEUE(wait, current); |
1319 | 1234 | ||
@@ -1401,7 +1316,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le | |||
1401 | datum = map_word_load_partial(map, datum, buf, gap, n); | 1316 | datum = map_word_load_partial(map, datum, buf, gap, n); |
1402 | 1317 | ||
1403 | ret = do_write_oneword(map, &cfi->chips[chipnum], | 1318 | ret = do_write_oneword(map, &cfi->chips[chipnum], |
1404 | bus_ofs, datum); | 1319 | bus_ofs, datum, FL_WRITING); |
1405 | if (ret) | 1320 | if (ret) |
1406 | return ret; | 1321 | return ret; |
1407 | 1322 | ||
@@ -1422,7 +1337,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le | |||
1422 | map_word datum = map_word_load(map, buf); | 1337 | map_word datum = map_word_load(map, buf); |
1423 | 1338 | ||
1424 | ret = do_write_oneword(map, &cfi->chips[chipnum], | 1339 | ret = do_write_oneword(map, &cfi->chips[chipnum], |
1425 | ofs, datum); | 1340 | ofs, datum, FL_WRITING); |
1426 | if (ret) | 1341 | if (ret) |
1427 | return ret; | 1342 | return ret; |
1428 | 1343 | ||
@@ -1446,7 +1361,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le | |||
1446 | datum = map_word_load_partial(map, datum, buf, 0, len); | 1361 | datum = map_word_load_partial(map, datum, buf, 0, len); |
1447 | 1362 | ||
1448 | ret = do_write_oneword(map, &cfi->chips[chipnum], | 1363 | ret = do_write_oneword(map, &cfi->chips[chipnum], |
1449 | ofs, datum); | 1364 | ofs, datum, FL_WRITING); |
1450 | if (ret) | 1365 | if (ret) |
1451 | return ret; | 1366 | return ret; |
1452 | 1367 | ||
@@ -2036,6 +1951,262 @@ static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) | |||
2036 | return ret; | 1951 | return ret; |
2037 | } | 1952 | } |
2038 | 1953 | ||
1954 | #ifdef CONFIG_MTD_OTP | ||
1955 | |||
1956 | typedef int (*otp_op_t)(struct map_info *map, struct flchip *chip, | ||
1957 | u_long data_offset, u_char *buf, u_int size, | ||
1958 | u_long prot_offset, u_int groupno, u_int groupsize); | ||
1959 | |||
1960 | static int __xipram | ||
1961 | do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, | ||
1962 | u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) | ||
1963 | { | ||
1964 | struct cfi_private *cfi = map->fldrv_priv; | ||
1965 | int ret; | ||
1966 | |||
1967 | spin_lock(chip->mutex); | ||
1968 | ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); | ||
1969 | if (ret) { | ||
1970 | spin_unlock(chip->mutex); | ||
1971 | return ret; | ||
1972 | } | ||
1973 | |||
1974 | /* let's ensure we're not reading back cached data from array mode */ | ||
1975 | if (map->inval_cache) | ||
1976 | map->inval_cache(map, chip->start + offset, size); | ||
1977 | |||
1978 | xip_disable(map, chip, chip->start); | ||
1979 | if (chip->state != FL_JEDEC_QUERY) { | ||
1980 | map_write(map, CMD(0x90), chip->start); | ||
1981 | chip->state = FL_JEDEC_QUERY; | ||
1982 | } | ||
1983 | map_copy_from(map, buf, chip->start + offset, size); | ||
1984 | xip_enable(map, chip, chip->start); | ||
1985 | |||
1986 | /* then ensure we don't keep OTP data in the cache */ | ||
1987 | if (map->inval_cache) | ||
1988 | map->inval_cache(map, chip->start + offset, size); | ||
1989 | |||
1990 | put_chip(map, chip, chip->start); | ||
1991 | spin_unlock(chip->mutex); | ||
1992 | return 0; | ||
1993 | } | ||
1994 | |||
1995 | static int | ||
1996 | do_otp_write(struct map_info *map, struct flchip *chip, u_long offset, | ||
1997 | u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) | ||
1998 | { | ||
1999 | int ret; | ||
2000 | |||
2001 | while (size) { | ||
2002 | unsigned long bus_ofs = offset & ~(map_bankwidth(map)-1); | ||
2003 | int gap = offset - bus_ofs; | ||
2004 | int n = min_t(int, size, map_bankwidth(map)-gap); | ||
2005 | map_word datum = map_word_ff(map); | ||
2006 | |||
2007 | datum = map_word_load_partial(map, datum, buf, gap, n); | ||
2008 | ret = do_write_oneword(map, chip, bus_ofs, datum, FL_OTP_WRITE); | ||
2009 | if (ret) | ||
2010 | return ret; | ||
2011 | |||
2012 | offset += n; | ||
2013 | buf += n; | ||
2014 | size -= n; | ||
2015 | } | ||
2016 | |||
2017 | return 0; | ||
2018 | } | ||
2019 | |||
2020 | static int | ||
2021 | do_otp_lock(struct map_info *map, struct flchip *chip, u_long offset, | ||
2022 | u_char *buf, u_int size, u_long prot, u_int grpno, u_int grpsz) | ||
2023 | { | ||
2024 | struct cfi_private *cfi = map->fldrv_priv; | ||
2025 | map_word datum; | ||
2026 | |||
2027 | /* make sure area matches group boundaries */ | ||
2028 | if (offset != 0 || size != grpsz) | ||
2029 | return -EXDEV; | ||
2030 | |||
2031 | datum = map_word_ff(map); | ||
2032 | datum = map_word_clr(map, datum, CMD(1 << grpno)); | ||
2033 | return do_write_oneword(map, chip, prot, datum, FL_OTP_WRITE); | ||
2034 | } | ||
2035 | |||
2036 | static int cfi_intelext_otp_walk(struct mtd_info *mtd, loff_t from, size_t len, | ||
2037 | size_t *retlen, u_char *buf, | ||
2038 | otp_op_t action, int user_regs) | ||
2039 | { | ||
2040 | struct map_info *map = mtd->priv; | ||
2041 | struct cfi_private *cfi = map->fldrv_priv; | ||
2042 | struct cfi_pri_intelext *extp = cfi->cmdset_priv; | ||
2043 | struct flchip *chip; | ||
2044 | struct cfi_intelext_otpinfo *otp; | ||
2045 | u_long devsize, reg_prot_offset, data_offset; | ||
2046 | u_int chip_num, chip_step, field, reg_fact_size, reg_user_size; | ||
2047 | u_int groups, groupno, groupsize, reg_fact_groups, reg_user_groups; | ||
2048 | int ret; | ||
2049 | |||
2050 | *retlen = 0; | ||
2051 | |||
2052 | /* Check that we actually have some OTP registers */ | ||
2053 | if (!extp || !(extp->FeatureSupport & 64) || !extp->NumProtectionFields) | ||
2054 | return -ENODATA; | ||
2055 | |||
2056 | /* we need real chips here not virtual ones */ | ||
2057 | devsize = (1 << cfi->cfiq->DevSize) * cfi->interleave; | ||
2058 | chip_step = devsize >> cfi->chipshift; | ||
2059 | |||
2060 | for (chip_num = 0; chip_num < cfi->numchips; chip_num += chip_step) { | ||
2061 | chip = &cfi->chips[chip_num]; | ||
2062 | otp = (struct cfi_intelext_otpinfo *)&extp->extra[0]; | ||
2063 | |||
2064 | /* first OTP region */ | ||
2065 | field = 0; | ||
2066 | reg_prot_offset = extp->ProtRegAddr; | ||
2067 | reg_fact_groups = 1; | ||
2068 | reg_fact_size = 1 << extp->FactProtRegSize; | ||
2069 | reg_user_groups = 1; | ||
2070 | reg_user_size = 1 << extp->UserProtRegSize; | ||
2071 | |||
2072 | while (len > 0) { | ||
2073 | /* flash geometry fixup */ | ||
2074 | data_offset = reg_prot_offset + 1; | ||
2075 | data_offset *= cfi->interleave * cfi->device_type; | ||
2076 | reg_prot_offset *= cfi->interleave * cfi->device_type; | ||
2077 | reg_fact_size *= cfi->interleave; | ||
2078 | reg_user_size *= cfi->interleave; | ||
2079 | |||
2080 | if (user_regs) { | ||
2081 | groups = reg_user_groups; | ||
2082 | groupsize = reg_user_size; | ||
2083 | /* skip over factory reg area */ | ||
2084 | groupno = reg_fact_groups; | ||
2085 | data_offset += reg_fact_groups * reg_fact_size; | ||
2086 | } else { | ||
2087 | groups = reg_fact_groups; | ||
2088 | groupsize = reg_fact_size; | ||
2089 | groupno = 0; | ||
2090 | } | ||
2091 | |||
2092 | while (groups > 0) { | ||
2093 | if (!action) { | ||
2094 | /* | ||
2095 | * Special case: if action is NULL | ||
2096 | * we fill buf with otp_info records. | ||
2097 | */ | ||
2098 | struct otp_info *otpinfo; | ||
2099 | map_word lockword; | ||
2100 | len -= sizeof(struct otp_info); | ||
2101 | if (len <= 0) | ||
2102 | return -ENOSPC; | ||
2103 | ret = do_otp_read(map, chip, | ||
2104 | reg_prot_offset, | ||
2105 | (u_char *)&lockword, | ||
2106 | map_bankwidth(map), | ||
2107 | 0, 0, 0); | ||
2108 | if (ret) | ||
2109 | return ret; | ||
2110 | otpinfo = (struct otp_info *)buf; | ||
2111 | otpinfo->start = from; | ||
2112 | otpinfo->length = groupsize; | ||
2113 | otpinfo->locked = | ||
2114 | !map_word_bitsset(map, lockword, | ||
2115 | CMD(1 << groupno)); | ||
2116 | from += groupsize; | ||
2117 | buf += sizeof(*otpinfo); | ||
2118 | *retlen += sizeof(*otpinfo); | ||
2119 | } else if (from >= groupsize) { | ||
2120 | from -= groupsize; | ||
2121 | } else { | ||
2122 | int size = groupsize; | ||
2123 | data_offset += from; | ||
2124 | size -= from; | ||
2125 | from = 0; | ||
2126 | if (size > len) | ||
2127 | size = len; | ||
2128 | ret = action(map, chip, data_offset, | ||
2129 | buf, size, reg_prot_offset, | ||
2130 | groupno, groupsize); | ||
2131 | if (ret < 0) | ||
2132 | return ret; | ||
2133 | buf += size; | ||
2134 | len -= size; | ||
2135 | *retlen += size; | ||
2136 | } | ||
2137 | groupno++; | ||
2138 | groups--; | ||
2139 | } | ||
2140 | |||
2141 | /* next OTP region */ | ||
2142 | if (++field == extp->NumProtectionFields) | ||
2143 | break; | ||
2144 | reg_prot_offset = otp->ProtRegAddr; | ||
2145 | reg_fact_groups = otp->FactGroups; | ||
2146 | reg_fact_size = 1 << otp->FactProtRegSize; | ||
2147 | reg_user_groups = otp->UserGroups; | ||
2148 | reg_user_size = 1 << otp->UserProtRegSize; | ||
2149 | otp++; | ||
2150 | } | ||
2151 | } | ||
2152 | |||
2153 | return 0; | ||
2154 | } | ||
2155 | |||
2156 | static int cfi_intelext_read_fact_prot_reg(struct mtd_info *mtd, loff_t from, | ||
2157 | size_t len, size_t *retlen, | ||
2158 | u_char *buf) | ||
2159 | { | ||
2160 | return cfi_intelext_otp_walk(mtd, from, len, retlen, | ||
2161 | buf, do_otp_read, 0); | ||
2162 | } | ||
2163 | |||
2164 | static int cfi_intelext_read_user_prot_reg(struct mtd_info *mtd, loff_t from, | ||
2165 | size_t len, size_t *retlen, | ||
2166 | u_char *buf) | ||
2167 | { | ||
2168 | return cfi_intelext_otp_walk(mtd, from, len, retlen, | ||
2169 | buf, do_otp_read, 1); | ||
2170 | } | ||
2171 | |||
2172 | static int cfi_intelext_write_user_prot_reg(struct mtd_info *mtd, loff_t from, | ||
2173 | size_t len, size_t *retlen, | ||
2174 | u_char *buf) | ||
2175 | { | ||
2176 | return cfi_intelext_otp_walk(mtd, from, len, retlen, | ||
2177 | buf, do_otp_write, 1); | ||
2178 | } | ||
2179 | |||
2180 | static int cfi_intelext_lock_user_prot_reg(struct mtd_info *mtd, | ||
2181 | loff_t from, size_t len) | ||
2182 | { | ||
2183 | size_t retlen; | ||
2184 | return cfi_intelext_otp_walk(mtd, from, len, &retlen, | ||
2185 | NULL, do_otp_lock, 1); | ||
2186 | } | ||
2187 | |||
2188 | static int cfi_intelext_get_fact_prot_info(struct mtd_info *mtd, | ||
2189 | struct otp_info *buf, size_t len) | ||
2190 | { | ||
2191 | size_t retlen; | ||
2192 | int ret; | ||
2193 | |||
2194 | ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 0); | ||
2195 | return ret ? : retlen; | ||
2196 | } | ||
2197 | |||
2198 | static int cfi_intelext_get_user_prot_info(struct mtd_info *mtd, | ||
2199 | struct otp_info *buf, size_t len) | ||
2200 | { | ||
2201 | size_t retlen; | ||
2202 | int ret; | ||
2203 | |||
2204 | ret = cfi_intelext_otp_walk(mtd, 0, len, &retlen, (u_char *)buf, NULL, 1); | ||
2205 | return ret ? : retlen; | ||
2206 | } | ||
2207 | |||
2208 | #endif | ||
2209 | |||
2039 | static int cfi_intelext_suspend(struct mtd_info *mtd) | 2210 | static int cfi_intelext_suspend(struct mtd_info *mtd) |
2040 | { | 2211 | { |
2041 | struct map_info *map = mtd->priv; | 2212 | struct map_info *map = mtd->priv; |
diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 96ebb52f24b1..b92e6bfffaf2 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * | 5 | * |
6 | * This code is GPL | 6 | * This code is GPL |
7 | * | 7 | * |
8 | * $Id: mtdpart.c,v 1.51 2004/11/16 18:28:59 dwmw2 Exp $ | 8 | * $Id: mtdpart.c,v 1.53 2005/02/08 17:11:13 nico Exp $ |
9 | * | 9 | * |
10 | * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> | 10 | * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> |
11 | * added support for read_oob, write_oob | 11 | * added support for read_oob, write_oob |
@@ -116,6 +116,13 @@ static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t le | |||
116 | len, retlen, buf); | 116 | len, retlen, buf); |
117 | } | 117 | } |
118 | 118 | ||
119 | static int part_get_user_prot_info (struct mtd_info *mtd, | ||
120 | struct otp_info *buf, size_t len) | ||
121 | { | ||
122 | struct mtd_part *part = PART(mtd); | ||
123 | return part->master->get_user_prot_info (part->master, buf, len); | ||
124 | } | ||
125 | |||
119 | static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, | 126 | static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, |
120 | size_t *retlen, u_char *buf) | 127 | size_t *retlen, u_char *buf) |
121 | { | 128 | { |
@@ -124,6 +131,13 @@ static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t le | |||
124 | len, retlen, buf); | 131 | len, retlen, buf); |
125 | } | 132 | } |
126 | 133 | ||
134 | static int part_get_fact_prot_info (struct mtd_info *mtd, | ||
135 | struct otp_info *buf, size_t len) | ||
136 | { | ||
137 | struct mtd_part *part = PART(mtd); | ||
138 | return part->master->get_fact_prot_info (part->master, buf, len); | ||
139 | } | ||
140 | |||
127 | static int part_write (struct mtd_info *mtd, loff_t to, size_t len, | 141 | static int part_write (struct mtd_info *mtd, loff_t to, size_t len, |
128 | size_t *retlen, const u_char *buf) | 142 | size_t *retlen, const u_char *buf) |
129 | { | 143 | { |
@@ -182,6 +196,12 @@ static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t l | |||
182 | len, retlen, buf); | 196 | len, retlen, buf); |
183 | } | 197 | } |
184 | 198 | ||
199 | static int part_lock_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len) | ||
200 | { | ||
201 | struct mtd_part *part = PART(mtd); | ||
202 | return part->master->lock_user_prot_reg (part->master, from, len); | ||
203 | } | ||
204 | |||
185 | static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, | 205 | static int part_writev (struct mtd_info *mtd, const struct kvec *vecs, |
186 | unsigned long count, loff_t to, size_t *retlen) | 206 | unsigned long count, loff_t to, size_t *retlen) |
187 | { | 207 | { |
@@ -409,6 +429,12 @@ int add_mtd_partitions(struct mtd_info *master, | |||
409 | slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; | 429 | slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; |
410 | if(master->write_user_prot_reg) | 430 | if(master->write_user_prot_reg) |
411 | slave->mtd.write_user_prot_reg = part_write_user_prot_reg; | 431 | slave->mtd.write_user_prot_reg = part_write_user_prot_reg; |
432 | if(master->lock_user_prot_reg) | ||
433 | slave->mtd.lock_user_prot_reg = part_lock_user_prot_reg; | ||
434 | if(master->get_user_prot_info) | ||
435 | slave->mtd.get_user_prot_info = part_get_user_prot_info; | ||
436 | if(master->get_fact_prot_info) | ||
437 | slave->mtd.get_fact_prot_info = part_get_fact_prot_info; | ||
412 | if (master->sync) | 438 | if (master->sync) |
413 | slave->mtd.sync = part_sync; | 439 | slave->mtd.sync = part_sync; |
414 | if (!i && master->suspend && master->resume) { | 440 | if (!i && master->suspend && master->resume) { |
diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index d87dc3fbd4ba..76255474a27c 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h | |||
@@ -1,7 +1,7 @@ | |||
1 | 1 | ||
2 | /* Common Flash Interface structures | 2 | /* Common Flash Interface structures |
3 | * See http://support.intel.com/design/flash/technote/index.htm | 3 | * See http://support.intel.com/design/flash/technote/index.htm |
4 | * $Id: cfi.h,v 1.51 2005/02/05 02:06:16 nico Exp $ | 4 | * $Id: cfi.h,v 1.52 2005/02/08 17:11:15 nico Exp $ |
5 | */ | 5 | */ |
6 | 6 | ||
7 | #ifndef __MTD_CFI_H__ | 7 | #ifndef __MTD_CFI_H__ |
@@ -252,7 +252,7 @@ static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int | |||
252 | * It looks too long to be inline, but in the common case it should almost all | 252 | * It looks too long to be inline, but in the common case it should almost all |
253 | * get optimised away. | 253 | * get optimised away. |
254 | */ | 254 | */ |
255 | static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) | 255 | static inline map_word cfi_build_cmd(u_long cmd, struct map_info *map, struct cfi_private *cfi) |
256 | { | 256 | { |
257 | map_word val = { {0} }; | 257 | map_word val = { {0} }; |
258 | int wordwidth, words_per_bus, chip_mode, chips_per_word; | 258 | int wordwidth, words_per_bus, chip_mode, chips_per_word; |
diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index c66ba812bf90..e778a1ab23c4 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h | |||
@@ -6,7 +6,7 @@ | |||
6 | * | 6 | * |
7 | * (C) 2000 Red Hat. GPLd. | 7 | * (C) 2000 Red Hat. GPLd. |
8 | * | 8 | * |
9 | * $Id: flashchip.h,v 1.15 2004/11/05 22:41:06 nico Exp $ | 9 | * $Id: flashchip.h,v 1.16 2005/02/08 17:11:15 nico Exp $ |
10 | * | 10 | * |
11 | */ | 11 | */ |
12 | 12 | ||
@@ -29,6 +29,7 @@ typedef enum { | |||
29 | FL_ERASE_SUSPENDED, | 29 | FL_ERASE_SUSPENDED, |
30 | FL_WRITING, | 30 | FL_WRITING, |
31 | FL_WRITING_TO_BUFFER, | 31 | FL_WRITING_TO_BUFFER, |
32 | FL_OTP_WRITE, | ||
32 | FL_WRITE_SUSPENDING, | 33 | FL_WRITE_SUSPENDING, |
33 | FL_WRITE_SUSPENDED, | 34 | FL_WRITE_SUSPENDED, |
34 | FL_PM_SUSPENDED, | 35 | FL_PM_SUSPENDED, |
diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index f0268b99c900..8fc6679aa9b1 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h | |||
@@ -1,6 +1,6 @@ | |||
1 | 1 | ||
2 | /* Overhauled routines for dealing with different mmap regions of flash */ | 2 | /* Overhauled routines for dealing with different mmap regions of flash */ |
3 | /* $Id: map.h,v 1.46 2005/01/05 17:09:44 dwmw2 Exp $ */ | 3 | /* $Id: map.h,v 1.47 2005/02/08 17:11:15 nico Exp $ */ |
4 | 4 | ||
5 | #ifndef __LINUX_MTD_MAP_H__ | 5 | #ifndef __LINUX_MTD_MAP_H__ |
6 | #define __LINUX_MTD_MAP_H__ | 6 | #define __LINUX_MTD_MAP_H__ |
@@ -263,6 +263,17 @@ static inline map_word map_word_and(struct map_info *map, map_word val1, map_wor | |||
263 | return r; | 263 | return r; |
264 | } | 264 | } |
265 | 265 | ||
266 | static inline map_word map_word_clr(struct map_info *map, map_word val1, map_word val2) | ||
267 | { | ||
268 | map_word r; | ||
269 | int i; | ||
270 | |||
271 | for (i=0; i<map_words(map); i++) { | ||
272 | r.x[i] = val1.x[i] & ~val2.x[i]; | ||
273 | } | ||
274 | return r; | ||
275 | } | ||
276 | |||
266 | static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2) | 277 | static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2) |
267 | { | 278 | { |
268 | map_word r; | 279 | map_word r; |
@@ -273,6 +284,7 @@ static inline map_word map_word_or(struct map_info *map, map_word val1, map_word | |||
273 | } | 284 | } |
274 | return r; | 285 | return r; |
275 | } | 286 | } |
287 | |||
276 | #define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b)) | 288 | #define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b)) |
277 | 289 | ||
278 | static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2) | 290 | static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2) |
@@ -338,6 +350,7 @@ static inline map_word map_word_ff(struct map_info *map) | |||
338 | } | 350 | } |
339 | return r; | 351 | return r; |
340 | } | 352 | } |
353 | |||
341 | static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) | 354 | static inline map_word inline_map_read(struct map_info *map, unsigned long ofs) |
342 | { | 355 | { |
343 | map_word r; | 356 | map_word r; |
diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index b3d134392b31..3aab1b8729e0 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * $Id: mtd.h,v 1.56 2004/08/09 18:46:04 dmarlin Exp $ | 2 | * $Id: mtd.h,v 1.57 2005/02/08 17:11:15 nico Exp $ |
3 | * | 3 | * |
4 | * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al. | 4 | * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al. |
5 | * | 5 | * |
@@ -113,12 +113,12 @@ struct mtd_info { | |||
113 | * flash devices. The user data is one time programmable but the | 113 | * flash devices. The user data is one time programmable but the |
114 | * factory data is read only. | 114 | * factory data is read only. |
115 | */ | 115 | */ |
116 | int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); | 116 | int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); |
117 | |||
118 | int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); | 117 | int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); |
119 | 118 | int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len); | |
120 | /* This function is not yet implemented */ | 119 | int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); |
121 | int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); | 120 | int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); |
121 | int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len); | ||
122 | 122 | ||
123 | /* kvec-based read/write methods. We need these especially for NAND flash, | 123 | /* kvec-based read/write methods. We need these especially for NAND flash, |
124 | with its limited number of write cycles per erase. | 124 | with its limited number of write cycles per erase. |
diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index a76ab898f445..091eb571e993 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * $Id: mtd-abi.h,v 1.7 2004/11/23 15:37:32 gleixner Exp $ | 2 | * $Id: mtd-abi.h,v 1.8 2005/02/08 17:11:16 nico Exp $ |
3 | * | 3 | * |
4 | * Portions of MTD ABI definition which are shared by kernel and user space | 4 | * Portions of MTD ABI definition which are shared by kernel and user space |
5 | */ | 5 | */ |
@@ -80,6 +80,12 @@ struct region_info_user { | |||
80 | uint32_t regionindex; | 80 | uint32_t regionindex; |
81 | }; | 81 | }; |
82 | 82 | ||
83 | struct otp_info { | ||
84 | uint32_t start; | ||
85 | uint32_t length; | ||
86 | uint32_t locked; | ||
87 | }; | ||
88 | |||
83 | #define MEMGETINFO _IOR('M', 1, struct mtd_info_user) | 89 | #define MEMGETINFO _IOR('M', 1, struct mtd_info_user) |
84 | #define MEMERASE _IOW('M', 2, struct erase_info_user) | 90 | #define MEMERASE _IOW('M', 2, struct erase_info_user) |
85 | #define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) | 91 | #define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) |